WDF was designed to reduce the amount of special-purpose synchronization code that drivers require. The frameworks include built-in synchronization through the following features:
Reference counts and the hierarchical object model.
Automatic serialization of Plug and Play and power callbacks.
Driver-configurable flow control for I/O queues.
Object presentation lock for device objects and I/O queues.
The WDF synchronization features are intended to help you get a driver working quickly, so that you can focus on optimizing the driver for your particular device and its most common usage scenarios. The WDF features are designed to be simple; they do not provide for all possible requirements.
Many drivers require synchronization primitives that the frameworks do not provide, such as a reader/writer lock. Drivers should use Windows synchronization mechanisms for these requirements.
In general, WDF drivers should use the WDF synchronization features for WDF objects and callbacks and should use the Windows synchronization mechanisms to coordinate activities with applications and to perform arithmetic or logical operations on a single variable.
Chapter 14, "Beyond the Frameworks," explains how to go outside the framework to call Windows API functions and kernel-mode driver support functions.
The WDF object model eliminates most synchronization requirements related to completing I/O requests and deleting objects when a device is removed or a driver is unloaded. Reference counting prevents the deletion of an object as long as it is being used, and the object hierarchy ensures that parent objects persist as long as their children. A driver is not required to use a lock to prevent the deletion of an object, because it can simply take a reference instead. Remember, however, that the state of the object can change even if the driver has a reference. For example, an outstanding reference does not prevent the completion-and freeing-of the IRP underlying a request object. The reference merely ensures that the handle to the request object remains valid.
Chapter 5, "WDF Object Model," explains the object hierarchy and object lifetimes.
Both frameworks automatically serialize most Plug and Play and power callbacks, so that only one such callback function runs at a time for each device object.
Neither UMDF nor KMDF serializes calls to the surprise-remove, query-remove, and query-stop callbacks with the other Plug and Play and power callbacks, although these three callbacks are serialized with respect to each other. Therefore, the framework can call these functions while the device is changing power state or is not in the working state. The following are the surprise-remove, query-remove, and query-stop callbacks:
For a UMDF driver: the device callback object's IPnpCallback::OnSurpriseRemoval, OnQueryRemove, and OnQueryStop methods.
For a KMDF driver: EvtDeviceSurpriseRemoval, EvtDeviceQueryRemove, and EvtDeviceQueryStop.
The automatic serialization of Plug and Play and power callbacks, together with the WDF object hierarchy, means that most drivers require little or no additional synchronization code for object rundown and deletion.
WDF drivers can configure each of their I/O queues for parallel, sequential, or manual dispatching. By analyzing the capabilities of your device and configuring the queues appropriately, you can reduce your driver's need for additional synchronization. The dispatch method for an I/O queue affects the degree of concurrency in a driver's I/O processing, because it controls the number of I/O requests from the queue that are concurrently active in the driver.
Consider the following examples:
If the device can handle only one I/O request at a time, you should configure a single, sequential I/O queue.
If the device can handle one read request and one write request simultaneously but has no limit on the number of device I/O requests, you might configure a sequential queue for read requests, another sequential queue for write requests, and a parallel queue for device I/O control requests.
Perhaps the device can handle some device I/O control requests concurrently but can deal with other such requests only one at a time. In this case, you can set up a single parallel queue for incoming device I/O control requests, inspect the requests as the queue dispatches them, and then redirect the requests that require sequential handling to a sequential queue for further processing.
For many drivers, controlling the flow of I/O requests is the easiest and most important means of managing concurrent operations. However, limiting the number of concurrently active I/O requests does not resolve all potential synchronization issues in I/O processing. For example, most drivers require additional synchronization to resolve race conditions during I/O cancellation.
Chapter 8, "I/O Flow and Dispatching," provides details about I/O queue dispatch types and configuration.
The object presentation lock-also called the object synchronization lock-is central to the WDF synchronization scheme. The framework creates one object presentation lock for the device object and one object presentation lock for each queue object. The framework acquires the device or queue lock, as appropriate, to serialize callbacks for the driver's device, file, and queue events.
The framework acquires the object presentation lock before it calls most event callback functions on a device or queue object, Consequently, the callback functions can access the object and its context area without additional locks. The framework also uses the object presentation locks to implement synchronization scope, which is described in "Synchronization Scope and I/O Callback Serialization" later in this chapter.
In addition, a driver can acquire the object presentation lock for a device or queue object by calling a framework lock-acquisition method. This feature is useful when the driver accesses shared, writable data that is stored in a device or queue object from code that is outside the serialized event callbacks. After acquiring the lock, the driver can safely use the writable data in the object and perform other actions that affect the object. For example, the framework does not serialize calls to I/O completion callbacks. Consequently, a driver's I/O completion callback might acquire the device object presentation lock before it writes shared data in the device object's context area. However, remember that the driver is not required to protect every access to data in the context area.
The frameworks provide the following methods to acquire and release the presentation lock for a device object or a queue object:
UMDF A UMDF driver calls IWDFObject:AcquireLock and IWDFObject::ReleaseLock on the framework device or queue object.
The UMDF methods cause a driver stop error if the driver supplies an invalid interface pointer or a pointer to an interface on the wrong type of object.
KMDF A KMDF driver calls WdfObjectAcquireLock and WdfObjectReleaseLock and supplies a handle to a WDFDEVICE or WDFQUEUE object.
The KMDF methods cause a bug check if the driver supplies an invalid handle or a handle to an object of any other type.