Using Framework Work Items
A work item is a task that a driver performs in an EvtWorkItem event callback function. These functions run asynchronously, at IRQL = PASSIVE_LEVEL, in the context of a system worker thread.
In other words, a driver can use work items if a function that runs at IRQL = DISPATCH_LEVEL must call a function that can be called only at IRQL = PASSIVE_LEVEL.
Typically, a driver's EvtInterruptDpc or EvtDpcFunc callback function creates a work-item object and adds it to the system's work-item queue. Subsequently, a system worker thread dequeues the object and calls the work item's EvtWorkItem callback function.
Sample Drivers That Use Work Items
Sample framework-based drivers that use work items include 1394, AMCC5933, PCIDRV, and Toaster.
To set up a work item, your driver must:
Create the work item.
Store information about the work item.
Typically, drivers use the context memory of the work-item object to store information about the task that the EvtWorkItem callback function should perform. When the EvtWorkItem callback function is called, it can retrieve the information by accessing this context memory. For information about how to allocate and access context memory, see Framework Object Context Space.
Add the work item to the system's work-item queue.
Your driver calls WdfWorkItemEnqueue, which adds the driver's work item to the work-item queue.
When your driver calls WdfWorkItemCreate, it must supply a handle to either a framework device object or a framework queue object. When the system deletes that object, it also deletes any existing work items that are associated with the object. The work item object will be disposed and its associated work-item callback will be cleaned up before the parent object's EvtCleanupCallback callback is invoked.
For more info about the cleanup rules for a framework object hierarchy, see Framework Object Life Cycle.
After the work item has been added to the work-item queue, it remains in the queue until a system worker thread becomes available. The system worker thread removes the work item from the queue and then calls the driver's EvtWorkItem callback function, passing the work-item object as input.
Typically, the EvtWorkItem callback function performs the following steps:
Obtains driver-supplied information about the work item by accessing the context memory of the work-item object.
Performs the task that you specified. If necessary, the callback function can call WdfWorkItemGetParentObject to determine the work item's parent object.
Calls WdfObjectDelete to delete the work-item object or, if the driver will requeue the work item, indicates that the handle to the work item is now available for reuse.
The task that each work item's callback function performs must be relatively short. The operating system provides a limited number of system worker threads, so your driver can impede system performance if it uses work-item callback functions to perform time-consuming tasks.
Drivers can use one of the following two techniques to create and delete work items:
Use each work item once: create the work item when you need it and delete it immediately after it is used.
This technique is useful for drivers that require infrequent use (less often than once per minute) of a small number of work items.
If your driver follows this scenario, and if its EvtInterruptDpc callback function receives a STATUS_INSUFFICIENT_RESOURCES return value from WdfWorkItemCreate, the driver must be able to postpone the required work until system resources (typically memory) become available.
Create one or more work items that your driver requeues as necessary.
This technique is useful for drivers that use work items frequently (more often than once per minute), or if your driver's EvtInterruptDpc callback function cannot easily handle a STATUS_INSUFFICIENT_RESOURCES return value from WdfWorkItemCreate.
The system does not allocate a worker thread to a work item until the driver calls WdfWorkItemEnqueue. Therefore, even though system worker threads are a limited resource, creating work items while initializing a device consumes a small amount of memory but does not otherwise affect system performance.
The following steps describe a possible scenario:
- A driver's EvtDriverDeviceAdd callback function calls WdfWorkItemCreate to obtain a work item handle.
- The driver's EvtInterruptDpc callback function creates a list of actions that the EvtWorkItem callback function must perform and then calls WdfWorkItemEnqueue, using the handle from step 1.
- The driver's EvtWorkItem callback function performs the list of actions and sets a flag to indicate that the callback function has run.
Subsequently, each time that the driver's EvtInterruptDpc callback function is called it must determine if the EvtWorkItem callback function has run. If the EvtWorkItem callback function has not run, the EvtInterruptDpc callback function does not call WdfWorkItemEnqueue, because the work item is still queued. In this case, the EvtInterruptDpc callback function only updates the list of actions for the EvtWorkItem callback function.
Each work item is associated with a device or a queue. When the associated device or queue is removed, the framework deletes all of the associated work items, so if you are using this technique, the driver does not have to call WdfObjectDelete.
A few drivers might need to call WdfWorkItemFlush to flush their work items from the work-item queue. For an example use of WdfWorkItemFlush, see the method's reference page.
If the driver calls WdfObjectDelete on an outstanding work item, the result depends on the state of the work item:
|Work item state||Result|
|Created but not enqueued||Work item is cleaned up immediately.|
|Enqueued||Call to WdfObjectDelete waits until work item finishes execution, then work item is cleaned up|
|Executing||If the driver calls WdfObjectDelete from within EvtWorkItem (on same thread), WdfObjectDelete returns immediately. Once EvtWorkItem finishes, the work item will be cleaned up. Otherwise, WdfObjectDelete waits for EvtWorkItem to finish.|