Geoff Chappell, Software Analyst
This function obtains exclusive use of whatever resource is protected by the given lock.
KIRQL KeAcquireSpinLockRaiseToDpc (KSPIN_LOCK *SpinLock);
The SpinLock argument is the address of the lock that is to be acquired.
The function returns the Interrupt Request Level (IRQL) that is to be restored when the lock is released.
The KeAcquireSpinLockRaiseToDpc function is exported by name from x64 builds of the kernel in all versions, i.e., starting with the version 5.2 from Windows Server 2003 SP1.
The most direct x86 analogue of this function is KfAcquireSpinLock. This anyway is not intended for programmers to use explicitly. They instead write as if calling the documented KeAcquireSpinLock function, which a macro definition reinterprets in terms of KfAcquireSpinLock.
Something similar is intended when programming for the x64 processor. Programmers write as if calling the documented KeAcquireSpinLock function, but it is again a macro. The difference is that the x64 macro reinterprets in terms of KeAcquireSpinLockRaiseToDpc.
The KeAcquireSpinLockRaiseToDpc function is documented. The first known documentation is in the Device Driver Kit (DDK) for Windows XP, which presents it as “a faster version of the KeAcquireSpinLock function” and says nothing of its being processor-specific. The WDM.H from this DDK version declares the function only if the target architecture is not x86. Not until the Windows Driver Kit (WDK) for Windows 7 does the documentation spell out that “This routine is not available in 32-bit versions of Windows.”
The KeAcquireSpinLockRaiseToDpc function is a compound. Its work is in two parts which can be done independently by calling other exported functions. The KeAcquireSpinLockRaiseToDpc implementation gains by inlining its two parts (and skipping the second entirely for single-processor builds).
The first part is to raise the IRQL to DISPATCH_LEVEL if it is not already there. This ensures that the current thread has exclusive use of the current processor. Interrupts can occur at higher IRQL, as with hardware interrupts, but they are not permitted to switch the processor to another thread. The equivalent exported function is KeRaiseIrqlToDpcLevel.
The second part is to obtain the current processor’s exclusive use of the protected resource. All threads that seek to use the resource must acquire the agreed lock. All call this function or a similar one so that all try to mark the lock in the same way but are able to do so only while the lock is not already marked. Typically, they wait in a more or less tight loop until they are able to mark the lock to signify that they now own the resource. The equivalent exported function for this second part is KeAcquireSpinLockAtDpcLevel. This part is trivial in single-processor builds.
The KeAcquireSpinLockRaiseToDpc function is intended for use at IRQL up to and including DISPATCH_LEVEL. The address that is given in the SpinLock argument must be of non-paged memory.
The function returns with the IRQL at DISPATCH_LEVEL. No other thread can execute on the same processor. No thread on another processor can acquire the lock. The owning thread must cause no exception, including by touching paged memory. These constraints continue until the owning thread releases the lock, e.g., by calling the KeReleaseSpinLock function.
Spin locks are either owned or not. That is their entire state. If KeAcquireSpinLockRaiseToDpc or any similar function is called to acquire a spin lock that the thread already owns, then the function cannot return (except with contrivance). This is not a deadlock between two threads, just that the one thread is hung in the function’s spin loop, waiting forever for the same thread to release the lock.