When does EvtCleanupCallback run?
Updated: June 12, 2006
Kernel-Mode Driver Framework (KMDF) objects are organized hierarchically, and KMDF deletes objects in hierarchical order. Object deletion starts from the object farthest from the parent and works up the object hierarchy toward the root. KMDF takes the following steps to delete an object:
1. | Starting with the child object farthest from the parent, calls the object's EvtCleanupCallback. In this routine, drivers should perform any cleanup tasks that must be done before the object's parent is deleted. Such tasks might include releasing explicit references on the object or a parent object. Note that when the EvtCleanupCallback routine runs, the object's children still exist, even though their EvtCleanupCallback routines have already been invoked. |
2. | When the object's reference count reaches zero, calls the object's EvtDestroyCallback if the driver has registered one. |
3. | Deallocates the memory that was allocated to the object and its context area. |
Order of Calls to EvtCleanupCallback
With one exception, KMDF calls the EvtCleanupCallback routines of child objects before calling those of their parent objects, so drivers are guaranteed that the parent object still exists when a child's EvtCleanupCallback routine runs.
The exception to this guaranteed ordering applies to I/O requests that the driver completes at DISPATCH_LEVEL. If such an I/O request object has one or more children whose EvtCleanupCallback routines must be called at PASSIVE_LEVEL, the parent request might be deleted before one or more of its children. An object requires cleanup at PASSIVE_LEVEL if it must wait for something to complete or if it accesses paged memory.
KMDF calls the EvtCleanupCallback routines for the following types of objects at PASSIVE_LEVEL:
| • | WDFDEVICE |
| • | WDFTIMER |
| • | WDFDPC |
| • | WDFWORKITEM |
| • | WDFMEMORY objects that contain paged pool |
| • | WDFLOOKASIDE objects that allocate paged pool |
| • | WDFQUEUE |
| • | WDFIOTARGET |
| • | WDFSTRING |
If the driver attempts to delete such an object (or the parent of such an object) while it is running at DISPATCH_LEVEL, KMDF queues the EvtCleanupCallback to a work item for later processing at PASSIVE_LEVEL and then calls the cleanup callback for the parent object without determining whether the callbacks for the children have run. Currently, KMDF maintains one work item queue for each WDFDEVICE object on which it calls the EvtCleanupCallback routines for the device object and its children.
Consider what happens when a driver completes an I/O request that has a child timer object. According to the "contract" for a WDFREQUEST object, KMDF calls the request object's EvtCleanupCallback while the pointer to the underlying Microsoft Windows Driver Model (WDM) I/O request packet (IRP) is still valid and accessible. For performance reasons, KMDF must complete the IRP as soon as possible. As soon as the IRP is complete, however, the pointer is no longer valid. Instead of waiting to complete the IRP after any EvtCleanupCallback routines have run at PASSIVE_LEVEL, KMDF queues the timer's cleanup callback to a work item and then completes the IRP. The EvtCleanupCallback for the timer might thus run after the request has been completed, the IRP pointer has been freed, and the request object has been deleted.
To avoid any problems that might result from this behavior, drivers should not set the WDFREQUEST object as the parent for any object that requires cleanup at PASSIVE_LEVEL. By default, the parent for most objects is WDFDEVICE, so drivers should just accept the default. Generally, if the WDFDEVICE object is passed as a parameter (either directly or as part of a structure) to the method that creates the object, the WDFDEVICE is the default parent. For a complete list of default parents, see the Windows Driver Kit (WDK) documentation.
A driver can change the parent of most KMDF objects by setting the ParentObject attribute. By setting the parent/child relationships appropriately, a driver can avoid taking out explicit references on related objects and can instead use the hierarchy and the associated callbacks to manage the object's lifetime.
In addition, note that objects that require cleanup at PASSIVE_LEVEL do not necessarily require allocation at PASSIVE_LEVEL. WDFWORKITEM, WDFTIMER, WDFDPC, and WDFQUEUE objects can be created at DISPATCH_LEVEL, as well as WDFMEMORY and WDFLOOKASIDE objects that use nonpaged pool. WDFMEMORY and WDFLOOKASIDE objects that use paged pool and WDFDEVICE, WDFIOTARGET, and WDFSTRING objects cannot be created at DISPATCH_LEVEL.
Order of Calls to EvtDestroyCallback
KMDF calls the EvtDestroyCallback routine for an object after calling its EvtCleanupCallback routine and after its reference count has reached zero. The EvtDestroyCallback routines for an object and its children can be called in any order, so that the EvtDestroyCallback for a parent might be called before that of one of its children. The EvtDestroyCallback can access the object context but cannot call any methods on the object.
What should you do?
| • | Do not set the WDFREQUEST object as a parent to any object that requires cleanup at PASSIVE_LEVEL. |
| • | Do not attempt to call methods on an object from its EvtDestroyCallback. |
For more information:
Architecture of the Kernel-Mode Driver Framework
Windows Driver Kit (WDK): "Summary of Framework Objects"