aboutsummaryrefslogtreecommitdiff
path: root/src/core/commandqueue.h
blob: 8d90ef6f64f4baec648bff5141f9d5d89fc95364 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
/*
 * Copyright (c) 2011, Denis Steckelmacher <steckdenis@yahoo.fr>
 * Copyright (c) 2012-2014, Texas Instruments Incorporated - http://www.ti.com/
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the copyright holder nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * \file commandqueue.h
 * \brief Command queue and base class for events
 */

#ifndef __COMMANDQUEUE_H__
#define __COMMANDQUEUE_H__

#include "object.h"
#include "icd.h"

#include <CL/cl.h>
#include <pthread.h>

#include <map>
#include <list>
#include <vector>

namespace Coal
{
  class CommandQueue;
}
struct _cl_command_queue: public Coal::descriptor<Coal::CommandQueue, _cl_command_queue> {};


namespace Coal
{

class Context;
class DeviceInterface;
class Event;

/**
 * \brief Command queue
 *
 * This class holds a list of events that will be pushed on a given device.
 *
 * More details are given on the \ref events page.
 */
class CommandQueue : public _cl_command_queue, public Object
{
    public:
        CommandQueue(Context *ctx,
                     DeviceInterface *device,
                     cl_command_queue_properties properties,
                     cl_int *errcode_ret);
        ~CommandQueue();

        /**
         * \brief Queue an event
         * \param event event to be queued
         * \return \c CL_SUCCESS if success, otherwise an error code
         */
        cl_int queueEvent(Event *event);

        /**
         * \brief Information about the command queue
         * \copydetails Coal::DeviceInterface::info
         */
        cl_int info(cl_command_queue_info param_name,
                    size_t param_value_size,
                    void *param_value,
                    size_t *param_value_size_ret) const;

        /**
         * \brief Set properties of the command queue
         * \note This function is deprecated and only there for OpenCL 1.0
         *       compatibility
         * \param properties property to enable or disable
         * \param enable true to enable the property, false to disable it
         * \param old_properties old value of the properties, ignored if NULL
         * \return \c CL_SUCCESS if all is good, an error code if \p properties is
         *         invalid
         */
        cl_int setProperty(cl_command_queue_properties properties,
                           cl_bool enable,
                           cl_command_queue_properties *old_properties);

        /**
         * \brief Check the properties given
         * \return \c CL_SUCCESS if they are valid, an error code otherwise
         */
        cl_int checkProperties() const;

        /**
         * \brief Push events on the device
         *
         * This function implements a big part of what is described in
         * \ref events .
         *
         * It is called by \c Coal::Event::setStatus() when an event is
         * completed, or by \c queueEvent(). Its purpose is to explore the list
         * of queued events (\c p_events) and to call
         * \c Coal::DeviceInterface::pushEvent() for each event meeting its push
         * conditions.
         *
         * \param ready_event is know to be pushable, push events in the
         * queue till this point, skip the events after this one.
         *
         * \param one_event_completed_on_device can be used to differentiate
         * whether this function is called by worker thread when an event is
         * completed, or by main thread's queueEvent().
         *
         * \section conditions Conditions
         *
         * If the command queue has the \c CL_OUT_OF_ORDER_EXEC_MODE_ENABLE
         * property disabled, an event can be pushed only if all the previous
         * ones in the list are completed with success. This way, an event
         * must be completed before any other can be pushed. This ensures
         * in-order execution.
         *
         * If this property is enable, more complex heuristics are used.
         *
         * The event list \c p_events is explored from top to bottom. At each
         * loop iteration, checks are performed to see if the event can be pushed.
         *
         * - When a \c Coal::BarrierEvent is encountered, no more events can be
         *   pushed, except if the \c Coal::BarrierEvent is the first in the list,
         *   as that means there are no other events that can be pushed, so the
         *   barrier can go away
         * - All events that are already pushed or finished are skipped
         * - The wait list of the event is then explored to ensure that all its
         *   dependencies are met.
         * - Finally, if the events passes all the tests, it is either pushed on
         *   the device, or simply set to \c Coal::Event::Complete if it's a
         *   dummy event (see \c Coal::Event::isInstantaneous()).
         */
        void pushEventsOnDevice(Event *ready_event = NULL,
                                bool one_event_completed_on_device = false);

        /**
         * \brief Push an event onto p_release_event list
         *
         * Later main thread will perform release event action.
         */
        void releaseEvent(Event *e);

        /**
         * \brief Remove from the event list completed events
         *
         * This function is called periodically to clean the event list from
         * completed events.
         *
         * It is needed to do that out of \c pushEventsOnDevice() as deleting
         * event may \c dereference() this command queue, and also delete it. It
         * would produce crashes.
         */
        void cleanEvents();

        /**
         * \brief Release events on the released event list
         *
         * This function is called periodically to release the events on the
         * released events list.  This is only performed on the main thread
         * because deleting/freeing memory from worker thread has caused
         * weird memory problems on ARM.
         *
         */
        void cleanReleasedEvents();

        /**
         * \brief Flush the command queue
         *
         * Pushes all the events on the device, and then return. The event
         * don't need to be completed after this call.
         */
        void flush();

        /**
         * \brief Finish the command queue
         *
         * Pushes the events like \c flush() but also wait for them to be
         * completed before returning.
         */
        void finish();

        /**
         * \brief Return all the events in the command queue
         * \note Retains all the events
         * \param count number of events in the event queue
         * \param include_completed_events default to true
         * \return events currently in the event queue
         */
        Event **events(unsigned int &count,
                       bool include_completed_events = true);

    private:
        DeviceInterface *p_device;
        cl_int p_num_events_in_queue;
        cl_int p_num_events_on_device;
        cl_int p_num_events_completed;
        cl_command_queue_properties p_properties;

        std::list<Event *> p_events;
        std::list<Event *> p_released_events;
        pthread_mutex_t p_event_list_mutex;
        pthread_cond_t p_event_list_cond;
        bool p_flushed;
};

/**
 * \brief Base class for all events
 *
 * This class contains logic common to all the events.
 * 
 * Beside handling OpenCL-specific stuff, \c Coal::Event objects do nothing
 * implementation-wise. They do not compile kernels, copy data around, etc.
 * They only contain static and immutable data that is then used by the devices
 * to actually implement the event.
 */
class Event : public Object
{
    public:
        /**
         * \brief Event type
         * 
         * The allows objects using \c Coal::Event to know which event it is, 
         * and to cast it to the correct sub-class.
         */
        enum Type
        {
            NDRangeKernel = CL_COMMAND_NDRANGE_KERNEL,
            TaskKernel = CL_COMMAND_TASK,
            NativeKernel = CL_COMMAND_NATIVE_KERNEL,
            ReadBuffer = CL_COMMAND_READ_BUFFER,
            WriteBuffer = CL_COMMAND_WRITE_BUFFER,
            CopyBuffer = CL_COMMAND_COPY_BUFFER,
            ReadImage = CL_COMMAND_READ_IMAGE,
            WriteImage = CL_COMMAND_WRITE_IMAGE,
            CopyImage = CL_COMMAND_COPY_IMAGE,
            CopyImageToBuffer = CL_COMMAND_COPY_IMAGE_TO_BUFFER,
            CopyBufferToImage = CL_COMMAND_COPY_BUFFER_TO_IMAGE,
            MapBuffer = CL_COMMAND_MAP_BUFFER,
            MapImage = CL_COMMAND_MAP_IMAGE,
            UnmapMemObject = CL_COMMAND_UNMAP_MEM_OBJECT,
            Marker = CL_COMMAND_MARKER,
            AcquireGLObjects = CL_COMMAND_ACQUIRE_GL_OBJECTS,
            ReleaseGLObjects = CL_COMMAND_RELEASE_GL_OBJECTS,
            ReadBufferRect = CL_COMMAND_READ_BUFFER_RECT,
            WriteBufferRect = CL_COMMAND_WRITE_BUFFER_RECT,
            CopyBufferRect = CL_COMMAND_COPY_BUFFER_RECT,
            FillBuffer = CL_COMMAND_FILL_BUFFER,
            MigrateMemObjects = CL_COMMAND_MIGRATE_MEM_OBJECTS,
            User = CL_COMMAND_USER,
            Barrier = CL_COMMAND_BARRIER,
            WaitForEvents
        };

	typedef int Status;
        /**
         * \brief Event status, can also be a negative value representing an abort code.
         * Positive values include:
         *   CL_QUEUED      Simply queued in a command queue
         *   CL_SUBMITTED   Submitted to a device
         *   CL_RUNNING     Running on the device
         *   CL_COMPLETE    Completed
         */

        /**
         * \brief Function that can be called when an event change status
         */
        typedef void (CL_CALLBACK *event_callback)(cl_event, cl_int, void *);

        /**
         * Structure used internally by \c Coal::Event to store for each event
         * status the callbacks to call with the corresponding \c user_data.
         */
        struct CallbackData
        {
            event_callback callback; /*!< Function to call */
            void *user_data;         /*!< Pointer to pass as its third argument */
        };

        /**
         * \brief Timing counters of an event
         */
        enum Timing
        {
            Queue,                   /*!< Time when the event was queued */
            Submit,                  /*!< Time when the event was submitted to the device */
            Start,                   /*!< Time when its execution began on the device */
            End,                     /*!< Time when its execution finished */
            Max                      /*!< Number of items in this enum */
        };

    public:
        /**
         * \brief Constructor
         * \param parent parent \c Coal::CommandQueue
         * \param status \c Status the event has when it is created
         * \param num_events_in_wait_list number of events to wait on
         * \param event_wait_list list of events to wait on
         * \param errcode_ret return value
         */
        Event(CommandQueue *parent,
              Status status,
              cl_uint num_events_in_wait_list,
              const Event **event_wait_list,
              cl_int *errcode_ret);

        void freeDeviceData();      /*!< \brief Call \c Coal::DeviceInterface::freeEventDeviceData() */
        virtual ~Event();           /*!< \brief Destructor */

        /**
         * \brief Type of the event
         * \return type of the event
         */
        virtual Type type() const = 0;
        
        /**
         * \brief Dummy event
         * 
         * A dummy event is an event that doesn't have to be pushed on a device,
         * it is only a hint for \c Coal::CommandQueue
         * 
         * \return true if the event is dummy
         */
        bool isInstantaneous() const;

        /**
         * \brief Set the event status
         * 
         * This function calls the event callbacks, and
         * \c Coal::CommandQueue::pushEventsOnDevice() if \p status is
         * \c Complete .
         * 
         * \param status new status of the event
         */
        void setStatus(Status status);

        /**
         * \brief Set device-specific data
         * \param data device-specific data
         */
        void setDeviceData(void *data);
        
        /**
         * \brief Update timing info
         * 
         * This function reads current system time and puts it in \c p_timing
         * 
         * \param timing timing event having just finished
         */
        void updateTiming(Timing timing);
        
        /**
         * \brief Status
         * \return status of the event
         */
        Status status() const;
        
        /**
         * \brief Wait for a specified status
         * 
         * This function blocks until the event's status is set to \p status
         * by another thread.
         * 
         * \param status the status the event must have for the function to return
         */
        void waitForStatus(Status status);
        
        /**
         * \brief Device-specific data
         * \return data set using \c setDeviceData()
         */
        void *deviceData();

        /**
         * \brief Add a callback for this event
         * \param command_exec_callback_type status the event must have in order
         *        to have the callback called
         * \param callback callback function
         * \param user_data user data given to the callback
         */
        void setCallback(cl_int command_exec_callback_type,
                         event_callback callback,
                         void *user_data);

        /**
         * \brief Info about the event
         * \copydetails Coal::DeviceInterface::info
         */
        cl_int info(cl_event_info param_name,
                    size_t param_value_size,
                    void *param_value,
                    size_t *param_value_size_ret) const;

        /**
         * \brief Profiling info
         * \copydetails Coal::DeviceInterface::info
         */
        cl_int profilingInfo(cl_profiling_info param_name,
                             size_t param_value_size,
                             void *param_value,
                             size_t *param_value_size_ret) const;

        /**
         * \brief Call \c Coal::CommandQueue::pushEventsOnDevice() for each command queue 
         * in which this event is queued or each queue with an event waiting on this event
         */
        void flushQueues();       


        /**
         * \brief Add event to p_dependent_events, which will be notified when
         * current event completes. If current event is already complete,
         * no need to add and return false.
         * \param event the event to be notified
         */
        bool addDependentEvent(Event *event);

        /**
         * \brief Remove event from p_wait_events, which should be waited on
         * before current event can start. When p_wait_events becomes empty,
         * return true to indicate that current event is ready to be pushed.
         * \param event the event to be removed from p_wait_events
         */
        bool removeWaitEvent(Event *event);

        /**
         * \brief Check if there are no more events to wait on before current
         * event can start.
         */
        bool waitEventsAllCompleted();

    private:
        /**
         * \brief Helper function for setStatus()
         * return number of dependent events
         */
        int setStatusHelper(Status status);

    private:
        pthread_cond_t p_state_change_cond;
        pthread_mutex_t p_state_mutex;

        Status p_status;
        void *p_device_data;
        std::multimap<Status, CallbackData> p_callbacks;

        cl_uint p_timing[Max];

        // p_wait_events: I should wait after these events complete
        // p_dependent_events: when I complete, I should notify these events
        std::list<const Event *>   p_wait_events;
        std::vector<Event *> p_dependent_events;
};

}

struct _cl_event : public Coal::Event
{};

#endif