Geoff Chappell, Software Analyst
This function tests whether a spin lock is available for acquisition.
BOOLEAN FASTCALL KeTestSpinLock (KSPIN_LOCK *SpinLock);
The SpinLock argument is the address of the lock that is to be tested. Starting with the version 5.2 from Windows Server 2003 SP1, this spin lock can be either a classic spin lock or a queued spin lock.
The function returns TRUE if the spin lock is available for acquisition, else FALSE.
Given that the supposed spin lock is in non-paged memory, the KeTestSpinLock function can safely be called at any IRQL.
The KeTestSpinLock function is exported by name from the kernel in version 5.2 and higher. It is present in the kernel in versions 5.0 and 5.1, but only as an internal routine.
The KeTestSpinLock function is documented but was not immediately so.
The first known documentation is for the Windows Driver Kit (WDK) for Windows Vista. It is there said, correctly, to be “available on Windows Server 2003 and later operating systems”, but with no sign that this availability for Windows Server 2003 is retrospective. No Device Driver Kit (DDK) for Windows Server 2003 documents the function, though all have a C-language declaration in WDM.H.
If the given spin lock is currently unowned, the function returns TRUE. Otherwise, it executes a pause instruction and returns FALSE.
Thus has it been for all the function’s existence, except on the one point of how it determines whether the spin lock is owned. Originally, the test is only of the lowest bit: the lock is unowned if this one bit is clear. Starting with the version 5.2 from Windows Server 2003 SP1, the test is of the whole lock: it is owned if all 32 or 64 bits of the lock are clear.
The return value is, of course, just what was determined of ownership at the time of the test. Ownership can have changed by the time the function returns.
Simple functions are easily overlooked as insignificant. The great worth of the KeTestSpinLock function is that it lets programmers implement their own spin loops, notably because they have non-trivial work that can usefully be done while waiting for the lock’s availability.
For this purpose, KeTestSpinLock supplements KeTryToAcquireSpinLockAtDpcLevel. The latter is the means of acquiring the lock while leaving the caller to decide what to do if the lock is unavailable. If the caller proceeds to a custom spin loop, the reasonable presumption is that the lock may remain owned for a few spins yet. The better test for exit is not to reattempt the lock’s acquisition (which necessarily involves the multi-processor disruption of a lock instruction) but just to test its availability. While KeTestSpinLock fails, KeTryToAcquireSpinLockAtDpcLevel likely would have too but at greater cost.
For a simple example, imagine programming to acquire the spin lock at the address SpinLock while already executing at DISPATCH_LEVEL. You might just pass SpinLock to KeAcquireSpinLockAtDpcLevel, but you would then give up all control of what gets done if the spin lock is not yet available. If you have other work to do at DISPATCH_LEVEL, you can instead write:
while (!KeTryToAcquireSpinLockAtDpcLevel (SpinLock)) { do { /* do custom work at DPC level */ } while (!KeTestSpinLock (SpinLock)); }
Do no custom work at all, and the preceding is practically indistinguishable from how KeAcquireSpinLockAtDpcLevel stood before KeTestSpinLock and KeTryToAcquireSpinLockAtDpcLevel became available for finer control.