1 //---------------------------------------------------------------------------//
2 // Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
3 //
4 // Distributed under the Boost Software License, Version 1.0
5 // See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt
7 //
8 // See http://boostorg.github.com/compute for more information.
9 //---------------------------------------------------------------------------//
10 
11 #ifndef BOOST_COMPUTE_EVENT_HPP
12 #define BOOST_COMPUTE_EVENT_HPP
13 
14 #include <boost/function.hpp>
15 
16 #include <boost/compute/config.hpp>
17 #include <boost/compute/exception.hpp>
18 #include <boost/compute/detail/duration.hpp>
19 #include <boost/compute/detail/get_object_info.hpp>
20 #include <boost/compute/detail/assert_cl_success.hpp>
21 #include <boost/compute/types/fundamental.hpp>
22 
23 namespace boost {
24 namespace compute {
25 
26 /// \class event
27 /// \brief An event corresponding to an operation on a compute device
28 ///
29 /// Event objects are used to track operations running on the device (such as
30 /// kernel executions and memory transfers). Event objects are returned by the
31 /// various \c enqueue_* methods of the command_queue class.
32 ///
33 /// Events can be used to synchronize operations between the host and the
34 /// device. The \c wait() method will block execution on the host until the
35 /// operation corresponding to the event on the device has completed. The
36 /// status of the operation can also be polled with the \c status() method.
37 ///
38 /// Event objects can also be used for performance profiling. In order to use
39 /// events for profiling, the command queue must be constructed with the
40 /// \c CL_QUEUE_PROFILING_ENABLE flag. Then the \c duration() method can be
41 /// used to retrieve the total duration of the operation on the device:
42 /// \code
43 /// std::cout << "time = " << e.duration<std::chrono::milliseconds>().count() << "ms\n";
44 /// \endcode
45 ///
46 /// \see \ref future "future<T>", wait_list
47 class event
48 {
49 public:
50     /// \internal_
51     enum execution_status {
52         complete = CL_COMPLETE,
53         running = CL_RUNNING,
54         submitted = CL_SUBMITTED,
55         queued = CL_QUEUED
56     };
57 
58     /// \internal_
59     enum command_type {
60         ndrange_kernel = CL_COMMAND_NDRANGE_KERNEL,
61         task = CL_COMMAND_TASK,
62         native_kernel = CL_COMMAND_NATIVE_KERNEL,
63         read_buffer = CL_COMMAND_READ_BUFFER,
64         write_buffer = CL_COMMAND_WRITE_BUFFER,
65         copy_buffer = CL_COMMAND_COPY_BUFFER,
66         read_image = CL_COMMAND_READ_IMAGE,
67         write_image = CL_COMMAND_WRITE_IMAGE,
68         copy_image = CL_COMMAND_COPY_IMAGE,
69         copy_image_to_buffer = CL_COMMAND_COPY_IMAGE_TO_BUFFER,
70         copy_buffer_to_image = CL_COMMAND_COPY_BUFFER_TO_IMAGE,
71         map_buffer = CL_COMMAND_MAP_BUFFER,
72         map_image = CL_COMMAND_MAP_IMAGE,
73         unmap_mem_object = CL_COMMAND_UNMAP_MEM_OBJECT,
74         marker = CL_COMMAND_MARKER,
75         aquire_gl_objects = CL_COMMAND_ACQUIRE_GL_OBJECTS,
76         release_gl_object = CL_COMMAND_RELEASE_GL_OBJECTS
77         #if defined(BOOST_COMPUTE_CL_VERSION_1_1)
78         ,
79         read_buffer_rect = CL_COMMAND_READ_BUFFER_RECT,
80         write_buffer_rect = CL_COMMAND_WRITE_BUFFER_RECT,
81         copy_buffer_rect = CL_COMMAND_COPY_BUFFER_RECT
82         #endif
83     };
84 
85     /// \internal_
86     enum profiling_info {
87         profiling_command_queued = CL_PROFILING_COMMAND_QUEUED,
88         profiling_command_submit = CL_PROFILING_COMMAND_SUBMIT,
89         profiling_command_start = CL_PROFILING_COMMAND_START,
90         profiling_command_end = CL_PROFILING_COMMAND_END
91     };
92 
93     /// Creates a null event object.
event()94     event()
95         : m_event(0)
96     {
97     }
98 
event(cl_event event,bool retain=true)99     explicit event(cl_event event, bool retain = true)
100         : m_event(event)
101     {
102         if(m_event && retain){
103             clRetainEvent(event);
104         }
105     }
106 
107     /// Makes a new event as a copy of \p other.
event(const event & other)108     event(const event &other)
109         : m_event(other.m_event)
110     {
111         if(m_event){
112             clRetainEvent(m_event);
113         }
114     }
115 
116     /// Copies the event object from \p other to \c *this.
operator =(const event & other)117     event& operator=(const event &other)
118     {
119         if(this != &other){
120             if(m_event){
121                 clReleaseEvent(m_event);
122             }
123 
124             m_event = other.m_event;
125 
126             if(m_event){
127                 clRetainEvent(m_event);
128             }
129         }
130 
131         return *this;
132     }
133 
134     #ifndef BOOST_COMPUTE_NO_RVALUE_REFERENCES
135     /// Move-constructs a new event object from \p other.
event(event && other)136     event(event&& other) BOOST_NOEXCEPT
137         : m_event(other.m_event)
138     {
139         other.m_event = 0;
140     }
141 
142     /// Move-assigns the event from \p other to \c *this.
operator =(event && other)143     event& operator=(event&& other) BOOST_NOEXCEPT
144     {
145         if(m_event){
146             clReleaseEvent(m_event);
147         }
148 
149         m_event = other.m_event;
150         other.m_event = 0;
151 
152         return *this;
153     }
154     #endif // BOOST_COMPUTE_NO_RVALUE_REFERENCES
155 
156     /// Destroys the event object.
~event()157     ~event()
158     {
159         if(m_event){
160             BOOST_COMPUTE_ASSERT_CL_SUCCESS(
161                 clReleaseEvent(m_event)
162             );
163         }
164     }
165 
166     /// Returns a reference to the underlying OpenCL event object.
get() const167     cl_event& get() const
168     {
169         return const_cast<cl_event &>(m_event);
170     }
171 
172     /// Returns the status of the event.
status() const173     cl_int status() const
174     {
175         return get_info<cl_int>(CL_EVENT_COMMAND_EXECUTION_STATUS);
176     }
177 
178     /// Returns the command type for the event.
get_command_type() const179     cl_command_type get_command_type() const
180     {
181         return get_info<cl_command_type>(CL_EVENT_COMMAND_TYPE);
182     }
183 
184     /// Returns information about the event.
185     ///
186     /// \see_opencl_ref{clGetEventInfo}
187     template<class T>
get_info(cl_event_info info) const188     T get_info(cl_event_info info) const
189     {
190         return detail::get_object_info<T>(clGetEventInfo, m_event, info);
191     }
192 
193     /// \overload
194     template<int Enum>
195     typename detail::get_object_info_type<event, Enum>::type
196     get_info() const;
197 
198     /// Returns profiling information for the event.
199     ///
200     /// \see event::duration()
201     ///
202     /// \see_opencl_ref{clGetEventProfilingInfo}
203     template<class T>
get_profiling_info(cl_profiling_info info) const204     T get_profiling_info(cl_profiling_info info) const
205     {
206         return detail::get_object_info<T>(clGetEventProfilingInfo,
207                                           m_event,
208                                           info);
209     }
210 
211     /// Blocks until the actions corresponding to the event have
212     /// completed.
wait() const213     void wait() const
214     {
215         cl_int ret = clWaitForEvents(1, &m_event);
216         if(ret != CL_SUCCESS){
217             BOOST_THROW_EXCEPTION(opencl_error(ret));
218         }
219     }
220 
221     #if defined(BOOST_COMPUTE_CL_VERSION_1_1) || defined(BOOST_COMPUTE_DOXYGEN_INVOKED)
222     /// Registers a function to be called when the event status changes to
223     /// \p status (by default CL_COMPLETE). The callback is passed the OpenCL
224     /// event object, the event status, and a pointer to arbitrary user data.
225     ///
226     /// \see_opencl_ref{clSetEventCallback}
227     ///
228     /// \opencl_version_warning{1,1}
set_callback(void (BOOST_COMPUTE_CL_CALLBACK * callback)(cl_event event,cl_int status,void * user_data),cl_int status=CL_COMPLETE,void * user_data=0)229     void set_callback(void (BOOST_COMPUTE_CL_CALLBACK *callback)(
230                           cl_event event, cl_int status, void *user_data
231                       ),
232                       cl_int status = CL_COMPLETE,
233                       void *user_data = 0)
234     {
235         cl_int ret = clSetEventCallback(m_event, status, callback, user_data);
236         if(ret != CL_SUCCESS){
237             BOOST_THROW_EXCEPTION(opencl_error(ret));
238         }
239     }
240 
241     /// Registers a generic function to be called when the event status
242     /// changes to \p status (by default \c CL_COMPLETE).
243     ///
244     /// The function specified by \p callback must be invokable with zero
245     /// arguments (e.g. \c callback()).
246     ///
247     /// \opencl_version_warning{1,1}
248     template<class Function>
set_callback(Function callback,cl_int status=CL_COMPLETE)249     void set_callback(Function callback, cl_int status = CL_COMPLETE)
250     {
251         set_callback(
252             event_callback_invoker,
253             status,
254             new boost::function<void()>(callback)
255         );
256     }
257     #endif // BOOST_COMPUTE_CL_VERSION_1_1
258 
259     /// Returns the total duration of the event from \p start to \p end.
260     ///
261     /// For example, to print the number of milliseconds the event took to
262     /// execute:
263     /// \code
264     /// std::cout << event.duration<std::chrono::milliseconds>().count() << " ms" << std::endl;
265     /// \endcode
266     ///
267     /// \see event::get_profiling_info()
268     template<class Duration>
duration(cl_profiling_info start=CL_PROFILING_COMMAND_START,cl_profiling_info end=CL_PROFILING_COMMAND_END) const269     Duration duration(cl_profiling_info start = CL_PROFILING_COMMAND_START,
270                       cl_profiling_info end = CL_PROFILING_COMMAND_END) const
271     {
272         const ulong_ nanoseconds =
273             get_profiling_info<ulong_>(end) - get_profiling_info<ulong_>(start);
274 
275         return detail::make_duration_from_nanoseconds(Duration(), nanoseconds);
276     }
277 
278     /// Returns \c true if the event is the same as \p other.
operator ==(const event & other) const279     bool operator==(const event &other) const
280     {
281         return m_event == other.m_event;
282     }
283 
284     /// Returns \c true if the event is different from \p other.
operator !=(const event & other) const285     bool operator!=(const event &other) const
286     {
287         return m_event != other.m_event;
288     }
289 
290     /// \internal_
operator cl_event() const291     operator cl_event() const
292     {
293         return m_event;
294     }
295 
296     /// \internal_ (deprecated)
get_status() const297     cl_int get_status() const
298     {
299         return status();
300     }
301 
302 private:
303     #ifdef BOOST_COMPUTE_CL_VERSION_1_1
304     /// \internal_
305     static void BOOST_COMPUTE_CL_CALLBACK
event_callback_invoker(cl_event,cl_int,void * user_data)306     event_callback_invoker(cl_event, cl_int, void *user_data)
307     {
308         boost::function<void()> *callback =
309             static_cast<boost::function<void()> *>(user_data);
310 
311         (*callback)();
312 
313         delete callback;
314     }
315     #endif // BOOST_COMPUTE_CL_VERSION_1_1
316 
317 protected:
318     cl_event m_event;
319 };
320 
321 /// \internal_ define get_info() specializations for event
322 BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(event,
323     ((cl_command_queue, CL_EVENT_COMMAND_QUEUE))
324     ((cl_command_type, CL_EVENT_COMMAND_TYPE))
325     ((cl_int, CL_EVENT_COMMAND_EXECUTION_STATUS))
326     ((cl_uint, CL_EVENT_REFERENCE_COUNT))
327 )
328 
329 #ifdef BOOST_COMPUTE_CL_VERSION_1_1
330 BOOST_COMPUTE_DETAIL_DEFINE_GET_INFO_SPECIALIZATIONS(event,
331     ((cl_context, CL_EVENT_CONTEXT))
332 )
333 #endif
334 
335 } // end compute namespace
336 } // end boost namespace
337 
338 #endif // BOOST_COMPUTE_EVENT_HPP
339