Lifetime of cl::Event objects?

A quick question about event objects. Let’s say I have a program that operates on a webcam feed. I want to keep looping as fast as I can grab frames and process them. The program might be structured like so:


/*
*  Set up context, memory objects, kernel, etc...
*/

// events that commands will generate
cl::Event writeImgGenEvent;
cl::Event kernelGenEvent;
cl::Event readImgGenEvent;

// lists of events for commands to wait on
std::vector<cl::Event> writeImgWaitEvents;
std::vector<cl::Event> kernelWaitEvents;
std::vector<cl::Event> readImgWaitEvents;

// first we'll write the image, but we won't wait on anything
cmdQueue.enqueueWriteImage(
      /* non-blocking, blah-blah... */,
      webcamDataPtr,
      NULL,
      writeImgGenEvent);

// now we'll enqueue the kernel, and we'll wait on the previous command to finish
kernelWaitEvents.push_back(writeImgGenEvent);
cmdQueue.enqueueNDRangeKernel(
      /* non-blocking, blah-blah... */,
      kernelWaitEvents,
      kernelGenEvent);

// now we'll read the data back so we can display it, but wait on the kernel to finish
kernelWaitEvents.push_back(kernelGenEvent);
cmdQueue.enqueueReadImage(
      /* non-blocking, blah-blah... */,
      displayImgPtr,
      readImgWaitEvents,
      readImgGenEvent);

// NOTE: ONLY NOW CAN WAIT HAVE THE READ_IMAGE EVENT IN WRITE_EVENT'S WAIT LIST
// (it wasn't a valid object until the read completed for the first time)
writeImgWaitEvents.push_back(readImgGenEvent);

// now we want to loop forever so we can keep processing webcam frames
while (1)
{
      // write, but wait on the previous read to finish
      cmdQueue.enqueueWriteImage(
            /* non-blocking, blah-blah... */,
            webcamDataPtr,
            writeImgWaitEvents,
            writeImgGenEvent);
 
      cmdQueue.enqueueNDRangeKernel(
            /* non-blocking, blah-blah... */,
            kernelWaitEvents,
            kernelGenEvent);
                              
      // now we'll read the data back so we can display it, but wait on the kernel to finish
      cmdQueue.enqueueReadImage(
            /* non-blocking, blah-blah... */,
            displayImgPtr,
            readImgWaitEvents,
            readImgGenEvent);
}

Unfortunately, this doesn’t work. It appears that the generated events are no longer valid after a certain period of time, because the program just hangs after one time through the while loop. The only solution I’ve found is to clear each commands’ wait list and reinsert whatever generated events the command depends on, but this just seems cumbersome. Am I missing something?!

First of all, you don’t need events for synchronization when you’re using a single in-order command queue. Commands get processed in order automatically. The kernel will not execute until the upload is done, and the download won’t start until the compute is done. Look at almost any OpenCL example to see this exact sequence being done.

Second, if you do use events, you are responsible for deleting them or you will have a memory leak. Your example doesn’t do this.

Events are only needed to synchronize an out-of-order command queue, or multiple command queues.

Thanks for your reply!

First of all, you don’t need events for synchronization when you’re using a single in-order command queue. Commands get processed in order automatically. The kernel will not execute until the upload is done, and the download won’t start until the compute is done. Look at almost any OpenCL example to see this exact sequence being done.

Sorry I didn’t include this in the example code - the command queue in the example is out-of-order, and (in my actual application) I am using multiple command queues. Part of the reason I’m using out-of-order queues is so the OpenCL runtime will pipeline commands if possible. For example, if I submit two small kernels one right after the other in an in-order command queue, the second won’t start until the first has completed; if an out-of-order command queue is used, they could be executed in parallel. Assuming you’ve been careful to avoid any data clobbering, this could be extremely useful.

Second, if you do use events, you are responsible for deleting them or you will have a memory leak. Your example doesn’t do this.

I understand that events should be freed before the application terminates (I should have included that in my example code), but when looping as in my example do I also need to free all events at the end of each loop iteration and then re-allocate? I guess I was thinking the runtime could essentially “reuse” events. This is the crux of my original post - I’m not exactly clear on how this should be handled or what best practices are.

cl::Event objects reference count the underlying cl_event - but when you constructed the vector you made copies. So updating readImgGenEvent during the enqueue, for example, I think will not update the event object - i think it will overwrite and release the original. The best thing would be to clear the vector and push back into it during the loop.