2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/spinlock.c
5 * PURPOSE: Spinlock and Queued Spinlock Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
18 /* PRIVATE FUNCTIONS *********************************************************/
22 // FIXME: The queued spinlock routines are broken.
27 KeAcquireQueuedSpinLockAtDpcLevel(IN PKSPIN_LOCK_QUEUE LockHandle
)
30 PKSPIN_LOCK_QUEUE Prev
;
32 /* Set the new lock */
33 Prev
= (PKSPIN_LOCK_QUEUE
)
34 InterlockedExchange((PLONG
)LockHandle
->Next
,
38 /* There was nothing there before. We now own it */
39 *LockHandle
->Lock
|= LQ_OWN
;
43 /* Set the wait flag */
44 *LockHandle
->Lock
|= LQ_WAIT
;
47 Prev
->Next
= (PKSPIN_LOCK_QUEUE
)LockHandle
;
50 while (*LockHandle
->Lock
& LQ_WAIT
)
57 KeReleaseQueuedSpinLockFromDpcLevel(IN PKSPIN_LOCK_QUEUE LockHandle
)
61 PKSPIN_LOCK_QUEUE Waiter
;
63 /* Remove own and wait flags */
64 *LockHandle
->Lock
&= ~(LQ_OWN
| LQ_WAIT
);
65 LockVal
= *LockHandle
->Lock
;
67 /* Check if we already own it */
68 if (LockVal
== (KSPIN_LOCK
)LockHandle
)
71 LockVal
= (KSPIN_LOCK
)
72 InterlockedCompareExchangePointer(LockHandle
->Lock
,
76 if (LockVal
== (KSPIN_LOCK
)LockHandle
) return;
78 /* Need to wait for it */
79 Waiter
= LockHandle
->Next
;
83 Waiter
= LockHandle
->Next
;
87 *(ULONG_PTR
*)&Waiter
->Lock
^= (LQ_OWN
| LQ_WAIT
);
88 LockHandle
->Next
= NULL
;
94 // HACK: Hacked to work like normal spinlocks
97 _IRQL_requires_min_(DISPATCH_LEVEL
)
98 _Acquires_nonreentrant_lock_(*LockHandle
->Lock
)
99 _Acquires_exclusive_lock_(*LockHandle
->Lock
)
102 KeAcquireQueuedSpinLockAtDpcLevel(_Inout_ PKSPIN_LOCK_QUEUE LockHandle
)
104 #if defined(CONFIG_SMP) || DBG
105 /* Make sure we are at DPC or above! */
106 if (KeGetCurrentIrql() < DISPATCH_LEVEL
)
108 /* We aren't -- bugcheck */
109 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL
,
110 (ULONG_PTR
)LockHandle
->Lock
,
117 /* Do the inlined function */
118 KxAcquireSpinLock(LockHandle
->Lock
);
121 _IRQL_requires_min_(DISPATCH_LEVEL
)
122 _Releases_nonreentrant_lock_(*LockHandle
->Lock
)
123 _Releases_exclusive_lock_(*LockHandle
->Lock
)
126 KeReleaseQueuedSpinLockFromDpcLevel(_Inout_ PKSPIN_LOCK_QUEUE LockHandle
)
128 #if defined(CONFIG_SMP) || DBG
129 /* Make sure we are at DPC or above! */
130 if (KeGetCurrentIrql() < DISPATCH_LEVEL
)
132 /* We aren't -- bugcheck */
133 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL
,
134 (ULONG_PTR
)LockHandle
->Lock
,
141 /* Do the inlined function */
142 KxReleaseSpinLock(LockHandle
->Lock
);
147 /* PUBLIC FUNCTIONS **********************************************************/
154 KeAcquireInterruptSpinLock(IN PKINTERRUPT Interrupt
)
159 KeRaiseIrql(Interrupt
->SynchronizeIrql
, &OldIrql
);
161 /* Acquire spinlock on MP */
162 KeAcquireSpinLockAtDpcLevel(Interrupt
->ActualLock
);
171 KeReleaseInterruptSpinLock(IN PKINTERRUPT Interrupt
,
174 /* Release lock on MP */
175 KeReleaseSpinLockFromDpcLevel(Interrupt
->ActualLock
);
178 KeLowerIrql(OldIrql
);
186 _KeInitializeSpinLock(IN PKSPIN_LOCK SpinLock
)
195 #undef KeAcquireSpinLockAtDpcLevel
198 KeAcquireSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock
)
200 /* Make sure we are at DPC or above! */
201 if (KeGetCurrentIrql() < DISPATCH_LEVEL
)
203 /* We aren't -- bugcheck */
204 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL
,
211 /* Do the inlined function */
212 KxAcquireSpinLock(SpinLock
);
218 #undef KeReleaseSpinLockFromDpcLevel
221 KeReleaseSpinLockFromDpcLevel(IN PKSPIN_LOCK SpinLock
)
223 /* Make sure we are at DPC or above! */
224 if (KeGetCurrentIrql() < DISPATCH_LEVEL
)
226 /* We aren't -- bugcheck */
227 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL
,
234 /* Do the inlined function */
235 KxReleaseSpinLock(SpinLock
);
243 KefAcquireSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock
)
245 /* Make sure we are at DPC or above! */
246 if (KeGetCurrentIrql() < DISPATCH_LEVEL
)
248 /* We aren't -- bugcheck */
249 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL
,
256 /* Do the inlined function */
257 KxAcquireSpinLock(SpinLock
);
265 KefReleaseSpinLockFromDpcLevel(IN PKSPIN_LOCK SpinLock
)
267 /* Make sure we are at DPC or above! */
268 if (KeGetCurrentIrql() < DISPATCH_LEVEL
)
270 /* We aren't -- bugcheck */
271 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL
,
278 /* Do the inlined function */
279 KxReleaseSpinLock(SpinLock
);
287 KiAcquireSpinLock(IN PKSPIN_LOCK SpinLock
)
289 /* Do the inlined function */
290 KxAcquireSpinLock(SpinLock
);
298 KiReleaseSpinLock(IN PKSPIN_LOCK SpinLock
)
300 /* Do the inlined function */
301 KxReleaseSpinLock(SpinLock
);
309 KeTryToAcquireSpinLockAtDpcLevel(IN OUT PKSPIN_LOCK SpinLock
)
312 /* Make sure we are at DPC or above! */
313 if (KeGetCurrentIrql() < DISPATCH_LEVEL
)
315 /* We aren't -- bugcheck */
316 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL
,
323 /* Make sure that we don't own the lock already */
324 if (((KSPIN_LOCK
)KeGetCurrentThread() | 1) == *SpinLock
)
326 /* We do, bugcheck! */
327 KeBugCheckEx(SPIN_LOCK_ALREADY_OWNED
, (ULONG_PTR
)SpinLock
, 0, 0, 0);
332 /* Check if it's already acquired */
335 /* Try to acquire it */
336 if (InterlockedBitTestAndSet((PLONG
)SpinLock
, 0))
338 /* Someone else acquired it */
344 /* It was already acquired */
350 /* On debug builds, we OR in the KTHREAD */
351 *SpinLock
= (ULONG_PTR
)KeGetCurrentThread() | 1;
354 /* All is well, return TRUE */
363 KeAcquireInStackQueuedSpinLockAtDpcLevel(IN PKSPIN_LOCK SpinLock
,
364 IN PKLOCK_QUEUE_HANDLE LockHandle
)
366 /* Set it up properly */
367 LockHandle
->LockQueue
.Next
= NULL
;
368 LockHandle
->LockQueue
.Lock
= SpinLock
;
371 KeAcquireQueuedSpinLockAtDpcLevel(LockHandle
->LockQueue
.Next
);
373 /* Make sure we are at DPC or above! */
374 if (KeGetCurrentIrql() < DISPATCH_LEVEL
)
376 /* We aren't -- bugcheck */
377 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL
,
378 (ULONG_PTR
)LockHandle
->LockQueue
.Lock
,
386 /* Acquire the lock */
387 KxAcquireSpinLock(LockHandle
->LockQueue
.Lock
); // HACK
395 KeReleaseInStackQueuedSpinLockFromDpcLevel(IN PKLOCK_QUEUE_HANDLE LockHandle
)
399 /* Call the internal function */
400 KeReleaseQueuedSpinLockFromDpcLevel(LockHandle
->LockQueue
.Next
);
402 /* Make sure we are at DPC or above! */
403 if (KeGetCurrentIrql() < DISPATCH_LEVEL
)
405 /* We aren't -- bugcheck */
406 KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL
,
407 (ULONG_PTR
)LockHandle
->LockQueue
.Lock
,
415 /* Release the lock */
416 KxReleaseSpinLock(LockHandle
->LockQueue
.Lock
); // HACK
424 KeAcquireSpinLockForDpc(IN PKSPIN_LOCK SpinLock
)
435 KeReleaseSpinLockForDpc(IN PKSPIN_LOCK SpinLock
,
446 KeAcquireInStackQueuedSpinLockForDpc(IN PKSPIN_LOCK SpinLock
,
447 IN PKLOCK_QUEUE_HANDLE LockHandle
)
449 LockHandle
->OldIrql
= KeGetCurrentIrql();
450 if (LockHandle
->OldIrql
>= DISPATCH_LEVEL
)
451 KeAcquireInStackQueuedSpinLockAtDpcLevel(SpinLock
, LockHandle
);
453 KeAcquireInStackQueuedSpinLock(SpinLock
, LockHandle
);
461 KeReleaseInStackQueuedSpinLockForDpc(IN PKLOCK_QUEUE_HANDLE LockHandle
)
463 if (LockHandle
->OldIrql
>= DISPATCH_LEVEL
)
464 KeReleaseInStackQueuedSpinLockFromDpcLevel(LockHandle
);
466 KeReleaseInStackQueuedSpinLock(LockHandle
);
475 KeTestSpinLock(IN PKSPIN_LOCK SpinLock
)
477 /* Test this spinlock */
480 /* Spinlock is busy, yield execution */
483 /* Return busy flag */
487 /* Spinlock appears to be free */
494 Kii386SpinOnSpinLock(PKSPIN_LOCK SpinLock
, ULONG Flags
)
496 // FIXME: Handle flags
497 UNREFERENCED_PARAMETER(Flags
);
499 /* Spin until it's unlocked */
500 while (*(volatile KSPIN_LOCK
*)SpinLock
& 1)
502 // FIXME: Check for timeout
504 /* Yield and keep looping */