Using Work Queues

This topic describes how to use work queues in Microsoft Media Foundation.

Using Work Queues

A work queue is an efficient way to perform asynchronous operations on another thread. Conceptually, you put work items in the queue, and the queue has a thread that pulls each item from the queue and dispatches it. Work items are implemented as callbacks by using the IMFAsyncCallback interface.

Media Foundation creates several standard work queues, called platform work queues. Applications can also create their own work queues, called private work queues. For a list of the platform work queues, see Work Queue Identifiers. To create a private work queue, call MFAllocateWorkQueue. This function returns an identifier for the new work queue. To put an item in the queue, call MFPutWorkItem or MFPutWorkItemEx. In both cases, you must specify a callback interface.

  • MFPutWorkItem takes a pointer to the IMFAsyncCallback interface, plus an optional state object that implements IUnknown. These are the same parameters used in asynchronous methods, as described in the topic Asynchronous Callback Methods. Internally, this function creates an asynchronous result object, which is passed to the callback's Invoke method.
  • MFPutWorkItemEx takes a pointer to the IMFAsyncResult interface. This interface represents an asynchronous result object. Create this object by calling MFCreateAsyncResult and specifying your callback interface and, optionally, a state object.

In both cases, the work queue thread calls your IMFAsyncCallback::Invoke method. Use the Invoke method to perform the work item.

If more than one thread or component shares the same work queue, you can call MFLockWorkQueue to lock the work queue, which prevents the platform from releasing it. For each call to MFAllocateWorkQueue or MFLockWorkQueue, you must call MFUnlockWorkQueue once to release the work queue. For example, if you create a new work queue and then lock it once, you must call MFUnlockWorkQueue twice, once for the call to MFAllocateWorkQueue and once for the call to MFLockWorkQueue.

The following code shows how to create a new work queue, put a work item in the queue, and release the work queue.

See Work Queue and Threading Improvements for additional information on work queues in Windows 8.

DWORD idWorkQueue = 0;
HRESULT hr = S_OK;

// Create a new work queue.
hr = MFAllocateWorkQueue(&idWorkQueue);

// Put an item on the queue.
if (SUCCEEDED(hr))
{
    hr = MFPutWorkItem(idWorkQueue, pCallback, NULL);
}

// Wait for the callback to be invoked.
if (SUCCEEDED(hr))
{
    WaitForSingleObject(hEvent, INFINITE);
}

// Release the work queue.
if (SUCCEEDED(hr))
{
    hr = MFUnlockWorkQueue(idWorkQueue);
}

This example assumes that pCallback is a pointer to the application's IMFAsyncCallback interface. It also assumes that the callback sets the hEvent event handle. The code waits for this event to be set before calling MFUnlockWorkQueue.

Work queue threads are always created in the caller's process. Within each work queue, the callbacks are serialized. If you call MFPutWorkItem twice with the same work queue, the second callback is not invoked until the first callback has returned.

Shutting Down Work Queues

Before calling MFShutdown, release any resources that are being used by work queue threads. To synchronize this process, you can lock the Media Foundation platform, which prevents the MFShutdown function from closing any work queue threads. If MFShutdown is called while the platform is locked, MFShutdown waits a few hundred milliseconds for the platform to be unlocked. If it is not unlocked within that time, MFShutdown closes the work queue threads.

The default implementation of IMFAsyncResult automatically locks the Media Foundation platform when the result object is created. Releasing the interface unlocks the platform. Therefore, you will almost never need to lock the platform directly. But if you write your own custom implementation of IMFAsyncResult, then you should manually lock and unlock the platform. To lock the platform, call MFLockPlatform. To unlock the platform, call MFUnlockPlatform. For an example, see Custom Asynchronous Result Objects.

After you call MFShutdown, you need to ensure that the platform is unlocked within the 5-second time-out period. Do this by releasing all IMFAsyncResult pointers, and by calling MFUnlockPlatform if you locked the platform manually. Make sure to release any resources that are being used by work queue threads, or your application might leak memory.

Typically, if your application shuts down and releases every Media Foundation object before calling MFShutdown, you do not have to worry about locking. The locking mechanism simply allows work queue threads to exit gracefully after you call MFShutdown.

Using Scheduled Work Items

You can schedule a callback to occur after a set period of time by calling MFScheduleWorkItem or MFScheduleWorkItemEx.

  • MFScheduleWorkItem takes a pointer to your callback, an optional state object, and a time-out interval.
  • MFScheduleWorkItemEx takes a pointer to an asynchronous result object and a time-out value.

Specify the time-out as a negative value in milliseconds. For example, to schedule a callback to be invoked in 5 seconds, use the value −5000. Both functions return an MFWORKITEM_KEY value, which you can use to cancel the callback by passing it to the MFCancelWorkItem function.

Scheduled work items always use the MFASYNC_CALLBACK_QUEUE_TIMER platform work queue.

Using Periodic Callbacks

The MFAddPeriodicCallback function schedules a callback to be invoked periodically until you cancel it. The callback interval is fixed; applications cannot change it. To find out the exact interval, call MFGetTimerPeriodicity. The interval is on the order of 10 milliseconds, so this function is meant for situations where you need a frequent "tick," such as implementing a presentation clock. If you want to schedule an operation to occur less frequently, use a scheduled work item, as described previously.

Unlike the other callbacks described in this topic, the periodic callback does not use the IMFAsyncCallback interface. Instead, it uses a function pointer. For more information, see MFPERIODICCALLBACK Callback.

To cancel the periodic callback, call MFRemovePeriodicCallback.

Periodic callbacks use the MFASYNC_CALLBACK_QUEUE_TIMER platform work queue.

Work Queues

MFASYNCRESULT

Work Queue and Threading Improvements