2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ex/pushlock.c
5 * PURPOSE: Pushlock and Cache-Aware Pushlock Implementation
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.com)
9 /* INCLUDES *****************************************************************/
13 #include <internal/debug.h>
15 /* DATA **********************************************************************/
17 ULONG ExPushLockSpinCount
;
19 /* PRIVATE FUNCTIONS *********************************************************/
22 * @name ExpInitializePushLocks
24 * The ExpInitializePushLocks routine initialized Pushlock support.
30 * @remarks The ExpInitializePushLocks routine sets up the spin on SMP machines.
35 ExpInitializePushLocks(VOID
)
37 /* Initialize an internal 1024-iteration spin for MP CPUs */
38 ExPushLockSpinCount
= (KeNumberProcessors
== 1) ? 0 : 1024;
42 * @name ExfWakePushLock
44 * The ExfWakePushLock routine wakes a Pushlock that is in the waiting
48 * Pointer to a pushlock that is waiting.
51 * Last known value of the pushlock before this routine was called.
55 * @remarks This is an internal routine; do not call it manually. Only the system
56 * can properly know if the pushlock is ready to be awakened or not.
57 * External callers should use ExfTrytoWakePushLock.
62 ExfWakePushLock(PEX_PUSH_LOCK PushLock
,
63 EX_PUSH_LOCK OldValue
)
65 EX_PUSH_LOCK NewValue
;
66 PEX_PUSH_LOCK_WAIT_BLOCK PreviousWaitBlock
, FirstWaitBlock
, NextWaitBlock
;
67 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
70 /* Start main wake loop */
74 ASSERT(!OldValue
.MultipleShared
);
76 /* Check if it's locked */
79 /* If it's locked we must simply un-wake it*/
82 /* It's not waking anymore */
83 NewValue
.Value
= OldValue
.Value
&~ EX_PUSH_LOCK_WAKING
;
86 ASSERT(!NewValue
.Waking
);
87 ASSERT(NewValue
.Locked
);
88 ASSERT(NewValue
.Waiting
);
90 /* Write the New Value */
91 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
94 if (NewValue
.Value
== OldValue
.Value
) return;
96 /* Someone changed the value behind our back, update it*/
99 /* Check if it's still locked */
100 if (OldValue
.Locked
) continue;
104 /* Save the First Block */
105 FirstWaitBlock
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)OldValue
.Ptr
&
106 ~EX_PUSH_LOCK_PTR_BITS
);
107 NextWaitBlock
= FirstWaitBlock
;
108 WaitBlock
= NextWaitBlock
->Last
;
110 /* Try to find a wait block */
113 /* Save the previous block */
114 PreviousWaitBlock
= NextWaitBlock
;
116 /* Move to next block */
117 NextWaitBlock
= NextWaitBlock
->Next
;
119 /* Save the previous block */
120 NextWaitBlock
->Previous
= PreviousWaitBlock
;
122 /* Move to the next one */
123 WaitBlock
= NextWaitBlock
->Last
;
126 /* Check if the last Wait Block is not Exclusive or if it's the only one */
127 PreviousWaitBlock
= WaitBlock
->Previous
;
128 if (!(WaitBlock
->Flags
& EX_PUSH_LOCK_FLAGS_EXCLUSIVE
) ||
129 !(PreviousWaitBlock
))
131 /* Destroy the pushlock */
132 if (InterlockedCompareExchangePointer(PushLock
, 0, OldValue
.Ptr
) ==
137 /* Link the wait blocks */
138 FirstWaitBlock
->Last
= PreviousWaitBlock
;
139 WaitBlock
->Previous
= NULL
;
142 ASSERT(FirstWaitBlock
!= WaitBlock
);
143 ASSERT(PushLock
->Waiting
);
145 /* Remove waking bit from pushlock */
146 InterlockedAnd((PLONG
)PushLock
, ~EX_PUSH_LOCK_WAKING
);
150 /* Check if there's a previous block */
151 OldIrql
= DISPATCH_LEVEL
;
152 if (WaitBlock
->Previous
)
154 /* Raise to Dispatch */
155 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
161 /* Get the previous Wait block */
162 PreviousWaitBlock
= WaitBlock
->Previous
;
165 ASSERT(!WaitBlock
->Signaled
);
167 /* We are about to get signaled */
168 WaitBlock
->Signaled
= TRUE
;
170 /* Set the Wait Bit in the Wait Block */
171 if (!InterlockedBitTestAndReset(&WaitBlock
->Flags
, 1))
173 /* Nobody signaled us, so do it */
174 KeSignalGateBoostPriority(&WaitBlock
->WakeGate
);
177 /* Set the wait block and check if there still is one to loop*/
178 WaitBlock
= PreviousWaitBlock
;
179 if (!WaitBlock
) break;
182 /* Check if we have to lower back the IRQL */
183 if (OldIrql
!= DISPATCH_LEVEL
) KeLowerIrql(OldIrql
);
187 * @name ExpOptimizePushLockList
189 * The ExpOptimizePushLockList routine optimizes the list of waiters
190 * associated to a pushlock's wait block.
193 * Pointer to a pushlock whose waiter list needs to be optimized.
196 * Last known value of the pushlock before this routine was called.
200 * @remarks At the end of the optimization, the pushlock will also be wakened.
205 ExpOptimizePushLockList(PEX_PUSH_LOCK PushLock
,
206 EX_PUSH_LOCK OldValue
)
208 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
, LastWaitBlock
, PreviousWaitBlock
;
209 EX_PUSH_LOCK NewValue
;
211 /* Check if the pushlock is locked */
214 /* Start main loop */
217 /* Get the wait block */
218 WaitBlock
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)OldValue
.Ptr
&
219 ~EX_PUSH_LOCK_PTR_BITS
);
221 /* Loop the blocks */
222 LastWaitBlock
= WaitBlock
->Last
;
223 while (LastWaitBlock
)
226 PreviousWaitBlock
= WaitBlock
;
228 /* Get the next block */
229 WaitBlock
= WaitBlock
->Next
;
231 /* Save the previous */
232 WaitBlock
->Previous
= PreviousWaitBlock
;
234 /* Move to the next */
235 LastWaitBlock
= WaitBlock
->Last
;
238 /* Remove the wake bit */
239 NewValue
.Value
= OldValue
.Value
&~ EX_PUSH_LOCK_WAKING
;
242 ASSERT(NewValue
.Locked
);
243 ASSERT(!NewValue
.Waking
);
245 /* Update the value */
246 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
250 /* If we updated correctly, leave */
251 if (NewValue
.Value
== OldValue
.Value
) return;
253 /* If the value is now locked, loop again */
254 if (NewValue
.Locked
) continue;
258 /* Wake the push lock */
259 ExfWakePushLock(PushLock
, OldValue
);
263 * @name ExTimedWaitForUnblockPushLock
265 * The ExTimedWaitForUnblockPushLock routine waits for a pushlock
266 * to be unblocked, for a specified internal.
269 * Pointer to a pushlock whose waiter list needs to be optimized.
272 * Pointer to the pushlock's wait block.
275 * Amount of time to wait for this pushlock to be unblocked.
277 * @return STATUS_SUCCESS is the pushlock is now unblocked, otherwise the error
278 * code returned by KeWaitForSingleObject.
280 * @remarks If the wait fails, then a manual unblock is attempted.
285 ExTimedWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock
,
287 IN PLARGE_INTEGER Timeout
)
292 /* Initialize the wait event */
293 KeInitializeEvent(&((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->WakeEvent
,
297 /* Spin on the push lock if necessary */
298 i
= ExPushLockSpinCount
;
304 /* Check if we got lucky and can leave early */
305 if (!(((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->Flags
&
306 EX_PUSH_LOCK_WAITING
))
308 /* This wait block isn't waiting anymore, we can leave */
309 return STATUS_SUCCESS
;
315 /* Now try to remove the wait bit */
316 if (InterlockedBitTestAndReset(&((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->Flags
,
319 /* Nobody removed it already, let's do a full wait */
320 Status
= KeWaitForSingleObject(&((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->
326 if (!NT_SUCCESS(Status
))
328 /* Try unblocking the pushlock */
329 ExfUnblockPushLock(PushLock
, WaitBlock
);
334 /* Someone beat us to it, no need to wait */
335 Status
= STATUS_SUCCESS
;
343 * @name ExBlockPushLock
345 * The ExBlockPushLock routine blocks a pushlock.
348 * Pointer to a pushlock whose waiter list needs to be optimized.
351 * Pointer to the pushlock's wait block.
360 ExBlockPushLock(PEX_PUSH_LOCK PushLock
,
363 PVOID NewValue
, OldValue
;
365 /* Set the waiting bit */
366 ((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->Flags
|= EX_PUSH_LOCK_FLAGS_WAIT
;
368 /* Link the wait blocks */
369 ((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->Next
= PushLock
->Ptr
;
371 /* Try to set this one as the wait block now */
372 NewValue
= PushLock
->Ptr
;
375 /* Set the new wait block value */
376 OldValue
= InterlockedCompareExchangePointer(&PushLock
->Ptr
,
379 if (OldValue
== NewValue
) break;
384 /* PUBLIC FUNCTIONS **********************************************************/
387 * @name ExAcquirePushLockExclusive
390 * The ExAcquirePushLockExclusive macro exclusively acquires a PushLock.
393 * Pointer to the pushlock which is to be acquired.
397 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
398 * This macro should usually be paired up with KeAcquireCriticalRegion.
403 ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock
)
405 EX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
406 EX_PUSH_LOCK OldValue
= *PushLock
, NewValue
, TempValue
;
410 /* Start main loop */
413 /* Check if it's unlocked */
414 if (!OldValue
.Locked
)
417 NewValue
.Value
= OldValue
.Value
| EX_PUSH_LOCK_LOCK
;
418 ASSERT(NewValue
.Locked
);
420 /* Set the new value */
421 if (InterlockedCompareExchangePointer(PushLock
,
423 OldValue
.Ptr
) != OldValue
.Ptr
)
426 OldValue
= *PushLock
;
430 /* Break out of the loop */
435 /* We'll have to create a Waitblock */
436 WaitBlock
.Flags
= EX_PUSH_LOCK_FLAGS_EXCLUSIVE
|
437 EX_PUSH_LOCK_FLAGS_WAIT
;
438 WaitBlock
.Previous
= NULL
;
441 /* Check if there is already a waiter */
442 if (OldValue
.Waiting
)
444 /* Nobody is the last waiter yet */
445 WaitBlock
.Last
= NULL
;
447 /* We are an exclusive waiter */
448 WaitBlock
.ShareCount
= 0;
450 /* Set the current Wait Block pointer */
451 WaitBlock
.Next
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)
452 OldValue
.Ptr
&~ EX_PUSH_LOCK_PTR_BITS
);
455 NewValue
.Value
= (OldValue
.Value
& EX_PUSH_LOCK_MULTIPLE_SHARED
) |
457 EX_PUSH_LOCK_WAKING
|
458 EX_PUSH_LOCK_WAITING
|
459 PtrToUlong(&WaitBlock
);
461 /* Check if the pushlock was already waking */
462 if (OldValue
.Waking
) NeedWake
= TRUE
;
466 /* We are the first waiter, so loop the wait block */
467 WaitBlock
.Last
= &WaitBlock
;
469 /* Set the share count */
470 WaitBlock
.ShareCount
= OldValue
.Shared
;
472 /* Check if someone is sharing this pushlock */
473 if (OldValue
.Shared
> 1)
475 /* Point to our wait block */
476 NewValue
.Value
= EX_PUSH_LOCK_MULTIPLE_SHARED
|
478 EX_PUSH_LOCK_WAITING
|
479 PtrToUlong(&WaitBlock
);
483 /* No shared count */
484 WaitBlock
.ShareCount
= 0;
486 /* Point to our wait block */
487 NewValue
.Value
= EX_PUSH_LOCK_LOCK
|
488 EX_PUSH_LOCK_WAITING
|
489 PtrToUlong(&WaitBlock
);
494 /* Setup the Debug Wait Block */
495 WaitBlock
.Signaled
= 0;
496 WaitBlock
.OldValue
= OldValue
;
497 WaitBlock
.NewValue
= NewValue
;
498 WaitBlock
.PushLock
= PushLock
;
502 ASSERT(NewValue
.Waiting
);
503 ASSERT(NewValue
.Locked
);
505 /* Write the new value */
506 TempValue
= NewValue
;
507 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
510 if (NewValue
.Value
!= OldValue
.Value
)
513 OldValue
= *PushLock
;
517 /* Check if the pushlock needed waking */
520 /* Scan the Waiters and Wake PushLocks */
521 ExpOptimizePushLockList(PushLock
, TempValue
);
524 /* Set up the Wait Gate */
525 KeInitializeGate(&WaitBlock
.WakeGate
);
527 /* Now spin on the push lock if necessary */
528 i
= ExPushLockSpinCount
;
529 if ((i
) && (WaitBlock
.Flags
& EX_PUSH_LOCK_WAITING
))
532 while (--i
) YieldProcessor();
535 /* Now try to remove the wait bit */
536 if (InterlockedBitTestAndReset(&WaitBlock
.Flags
, 1))
538 /* Nobody removed it already, let's do a full wait */
539 KeWaitForGate(&WaitBlock
.WakeGate
, WrPushLock
, KernelMode
);
540 ASSERT(WaitBlock
.Signaled
);
543 /* We shouldn't be shared anymore */
544 ASSERT((WaitBlock
.ShareCount
== 0));
553 * @name ExAcquirePushLockExclusive
556 * The ExAcquirePushLockShared macro acquires a shared PushLock.
559 * Pointer to the pushlock which is to be acquired.
563 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
564 * This macro should usually be paired up with KeAcquireCriticalRegion.
569 ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock
)
571 EX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
572 EX_PUSH_LOCK OldValue
= *PushLock
, NewValue
;
576 /* Start main loop */
579 /* Check if it's unlocked or if it's waiting without any sharers */
580 if (!(OldValue
.Locked
) || (OldValue
.Waiting
&& OldValue
.Shared
== 0))
582 /* Check if anyone is waiting on it */
583 if (!OldValue
.Waiting
)
585 /* Increase the share count and lock it */
586 NewValue
.Value
= OldValue
.Value
| EX_PUSH_LOCK_LOCK
;
591 /* Simply set the lock bit */
592 NewValue
.Value
= OldValue
.Value
| EX_PUSH_LOCK_LOCK
;
596 ASSERT(NewValue
.Locked
);
598 /* Set the new value */
599 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
602 if (NewValue
.Value
!= OldValue
.Value
)
609 /* Break out of the loop */
614 /* We'll have to create a Waitblock */
615 WaitBlock
.Flags
= EX_PUSH_LOCK_FLAGS_WAIT
;
616 WaitBlock
.ShareCount
= 0;
618 WaitBlock
.Previous
= NULL
;
620 /* Check if there is already a waiter */
621 if (OldValue
.Waiting
)
623 /* Set the current Wait Block pointer */
624 WaitBlock
.Next
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)
625 OldValue
.Ptr
&~ EX_PUSH_LOCK_PTR_BITS
);
627 /* Nobody is the last waiter yet */
628 WaitBlock
.Last
= NULL
;
631 NewValue
.Value
= (OldValue
.Value
& (EX_PUSH_LOCK_MULTIPLE_SHARED
|
632 EX_PUSH_LOCK_LOCK
)) |
633 EX_PUSH_LOCK_WAKING
|
634 EX_PUSH_LOCK_WAITING
|
635 PtrToUlong(&WaitBlock
);
637 /* Check if the pushlock was already waking */
638 if (OldValue
.Waking
) NeedWake
= TRUE
;
642 /* We are the first waiter, so loop the wait block */
643 WaitBlock
.Last
= &WaitBlock
;
645 /* Point to our wait block */
646 NewValue
.Value
= (OldValue
.Value
& (EX_PUSH_LOCK_MULTIPLE_SHARED
|
647 EX_PUSH_LOCK_WAKING
)) |
648 EX_PUSH_LOCK_WAITING
|
649 PtrToUlong(&WaitBlock
);
653 ASSERT(NewValue
.Waiting
);
656 /* Setup the Debug Wait Block */
657 WaitBlock
.Signaled
= 0;
658 WaitBlock
.OldValue
= OldValue
;
659 WaitBlock
.NewValue
= NewValue
;
660 WaitBlock
.PushLock
= PushLock
;
663 /* Write the new value */
664 if (InterlockedCompareExchangePointer(PushLock
,
666 OldValue
.Ptr
) != OldValue
.Ptr
)
673 /* Update the value now */
676 /* Check if the pushlock needed waking */
679 /* Scan the Waiters and Wake PushLocks */
680 ExpOptimizePushLockList(PushLock
, OldValue
);
683 /* Set up the Wait Gate */
684 KeInitializeGate(&WaitBlock
.WakeGate
);
686 /* Now spin on the push lock if necessary */
687 i
= ExPushLockSpinCount
;
688 if ((i
) && (WaitBlock
.Flags
& EX_PUSH_LOCK_WAITING
))
691 while (--i
) YieldProcessor();
694 /* Now try to remove the wait bit */
695 if (InterlockedBitTestAndReset(&WaitBlock
.Flags
, 1))
697 /* Fast-path did not work, we need to do a full wait */
698 KeWaitForGate(&WaitBlock
.WakeGate
, WrPushLock
, KernelMode
);
699 ASSERT(WaitBlock
.Signaled
);
702 /* We shouldn't be shared anymore */
703 ASSERT((WaitBlock
.ShareCount
== 0));
709 * @name ExfReleasePushLock
712 * The ExReleasePushLockExclusive routine releases a previously
713 * exclusively acquired PushLock.
716 * Pointer to a previously acquired pushlock.
720 * @remarks Callers of ExReleasePushLockExclusive must be running at IRQL <= APC_LEVEL.
721 * This macro should usually be paired up with KeLeaveCriticalRegion.
726 ExfReleasePushLock(PEX_PUSH_LOCK PushLock
)
728 EX_PUSH_LOCK OldValue
= *PushLock
;
729 EX_PUSH_LOCK NewValue
;
730 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
733 ASSERT(OldValue
.Locked
);
735 /* Check if someone is waiting on the lock */
736 if (!OldValue
.Waiting
)
738 /* Nobody is waiting on it, so we'll try a quick release */
741 /* Check if it's shared */
742 if (OldValue
.Shared
> 1)
744 /* Write the Old Value but decrease share count */
750 /* Simply clear the lock */
754 /* Write the New Value */
755 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
758 if (NewValue
.Value
== OldValue
.Value
)
760 /* No waiters left, we're done */
764 /* Did it enter a wait state? */
766 if (NewValue
.Waiting
) break;
770 /* Ok, we do know someone is waiting on it. Are there more then one? */
771 if (OldValue
.MultipleShared
)
773 /* Find the last Wait Block */
774 for (WaitBlock
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)OldValue
.Ptr
&
775 ~EX_PUSH_LOCK_PTR_BITS
);
777 WaitBlock
= WaitBlock
->Next
);
779 /* Make sure the Share Count is above 0 */
780 if (WaitBlock
->ShareCount
)
782 /* This shouldn't be an exclusive wait block */
783 ASSERT(WaitBlock
->Flags
&EX_PUSH_LOCK_FLAGS_EXCLUSIVE
);
785 /* Do the decrease and check if the lock isn't shared anymore */
786 if (InterlockedExchangeAdd(&WaitBlock
->ShareCount
, -1))
788 /* Someone is still holding the lock */
795 * If nobody was waiting on the block, then we possibly reduced the number
796 * of times the pushlock was shared, and we unlocked it.
797 * If someone was waiting, and more then one person is waiting, then we
798 * reduced the number of times the pushlock is shared in the wait block.
799 * Therefore, at this point, we can now 'satisfy' the wait.
803 /* Now we need to see if it's waking */
806 /* Remove the lock and multiple shared bits */
807 NewValue
.Value
= OldValue
.Value
;
808 NewValue
.MultipleShared
= FALSE
;
809 NewValue
.Locked
= FALSE
;
812 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
&& !NewValue
.MultipleShared
);
814 /* Write the new value */
815 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
818 if (NewValue
.Value
== OldValue
.Value
) break;
820 /* The value changed, try the unlock again */
825 /* Remove the lock and multiple shared bits */
826 NewValue
.Value
= OldValue
.Value
;
827 NewValue
.MultipleShared
= FALSE
;
828 NewValue
.Locked
= FALSE
;
830 /* It's not already waking, so add the wake bit */
831 NewValue
.Waking
= TRUE
;
834 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
&& !NewValue
.MultipleShared
);
836 /* Write the new value */
837 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
840 if (NewValue
.Value
!= OldValue
.Value
) continue;
842 /* The write was successful. The pushlock is Unlocked and Waking */
843 ExfWakePushLock(PushLock
, NewValue
);
853 * @name ExfReleasePushLockShared
856 * The ExfReleasePushLockShared macro releases a previously acquired PushLock.
859 * Pointer to a previously acquired pushlock.
863 * @remarks Callers of ExReleasePushLockShared must be running at IRQL <= APC_LEVEL.
864 * This macro should usually be paired up with KeLeaveCriticalRegion.
869 ExfReleasePushLockShared(PEX_PUSH_LOCK PushLock
)
871 EX_PUSH_LOCK OldValue
= *PushLock
;
872 EX_PUSH_LOCK NewValue
;
873 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
875 /* Check if someone is waiting on the lock */
876 if (!OldValue
.Waiting
)
878 /* Nobody is waiting on it, so we'll try a quick release */
881 /* Check if it's shared */
882 if (OldValue
.Shared
> 1)
884 /* Write the Old Value but decrease share count */
890 /* Simply clear the lock */
894 /* Write the New Value */
895 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
898 if (NewValue
.Value
== OldValue
.Value
)
900 /* No waiters left, we're done */
904 /* Did it enter a wait state? */
906 if (NewValue
.Waiting
) break;
910 /* Ok, we do know someone is waiting on it. Are there more then one? */
911 if (OldValue
.MultipleShared
)
913 /* Find the last Wait Block */
914 for (WaitBlock
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)OldValue
.Ptr
&
915 ~EX_PUSH_LOCK_PTR_BITS
);
917 WaitBlock
= WaitBlock
->Next
);
920 ASSERT(WaitBlock
->ShareCount
> 0);
921 ASSERT(WaitBlock
->Flags
&EX_PUSH_LOCK_FLAGS_EXCLUSIVE
);
923 /* Do the decrease and check if the lock isn't shared anymore */
924 if (InterlockedExchangeAdd(&WaitBlock
->ShareCount
, -1)) goto quit
;
928 * If nobody was waiting on the block, then we possibly reduced the number
929 * of times the pushlock was shared, and we unlocked it.
930 * If someone was waiting, and more then one person is waiting, then we
931 * reduced the number of times the pushlock is shared in the wait block.
932 * Therefore, at this point, we can now 'satisfy' the wait.
936 /* Now we need to see if it's waking */
939 /* Remove the lock and multiple shared bits */
940 NewValue
.Value
= OldValue
.Value
;
941 NewValue
.MultipleShared
= FALSE
;
942 NewValue
.Locked
= FALSE
;
945 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
&& !NewValue
.MultipleShared
);
947 /* Write the new value */
948 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
951 if (NewValue
.Value
== OldValue
.Value
) break;
953 /* The value changed, try the unlock again */
958 /* Remove the lock and multiple shared bits */
959 NewValue
.Value
= OldValue
.Value
;
960 NewValue
.MultipleShared
= FALSE
;
961 NewValue
.Locked
= FALSE
;
963 /* It's not already waking, so add the wake bit */
964 NewValue
.Waking
= TRUE
;
967 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
&& !NewValue
.MultipleShared
);
969 /* Write the new value */
970 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
973 if (NewValue
.Value
!= OldValue
.Value
) continue;
975 /* The write was successful. The pushlock is Unlocked and Waking */
976 ExfWakePushLock(PushLock
, NewValue
);
986 * ExfReleasePushLockExclusive
989 * The ExfReleasePushLockExclusive routine releases a previously
990 * exclusively acquired PushLock.
993 * Pointer to a previously acquired pushlock.
997 * @remarks Callers of ExReleasePushLockExclusive must be running at IRQL <= APC_LEVEL.
998 * This macro should usually be paired up with KeLeaveCriticalRegion.
1003 ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock
)
1005 EX_PUSH_LOCK NewValue
;
1006 EX_PUSH_LOCK OldValue
= *PushLock
;
1008 /* Loop until we can change */
1012 ASSERT(OldValue
.Locked
);
1013 ASSERT(OldValue
.Waiting
|| OldValue
.Shared
== 0);
1015 /* Check if it's waiting and not yet waking */
1016 if ((OldValue
.Waiting
) && !(OldValue
.Waking
))
1018 /* Remove the lock bit, and add the wake bit */
1019 NewValue
.Value
= (OldValue
.Value
&~ EX_PUSH_LOCK_LOCK
) |
1020 EX_PUSH_LOCK_WAKING
;
1023 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
);
1025 /* Write the New Value */
1026 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
1030 /* Check if the value changed behind our back */
1031 if (NewValue
.Value
!= OldValue
.Value
)
1034 OldValue
= NewValue
;
1038 /* Wake the Pushlock */
1039 ExfWakePushLock(PushLock
, NewValue
);
1044 /* A simple unlock */
1045 NewValue
.Value
= OldValue
.Value
&~ EX_PUSH_LOCK_LOCK
;
1048 ASSERT(NewValue
.Waking
&& !NewValue
.Waiting
);
1050 /* Write the New Value */
1051 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
1055 /* Check if the value changed behind our back */
1056 if (NewValue
.Value
== OldValue
.Value
) break;
1059 OldValue
= NewValue
;
1065 * @name ExfTryToWakePushLock
1066 * @implemented NT5.2
1068 * The ExfTryToWakePushLock attemps to wake a waiting pushlock.
1071 * Pointer to a PushLock which is in the wait state.
1075 * @remarks The pushlock must be in a wait state and must not be already waking.
1080 ExfTryToWakePushLock(PEX_PUSH_LOCK PushLock
)
1082 EX_PUSH_LOCK OldValue
= *PushLock
, NewValue
;
1085 * If the Pushlock is not waiting on anything, or if it's already waking up
1086 * and locked, don't do anything
1088 if (!(OldValue
.Value
== (EX_PUSH_LOCK_WAKING
| EX_PUSH_LOCK_LOCK
)) &&
1091 /* Make it Waking */
1092 NewValue
= OldValue
;
1093 NewValue
.Waking
= TRUE
;
1095 /* Write the New Value */
1096 if (InterlockedCompareExchangePointer(PushLock
,
1098 OldValue
.Ptr
) == OldValue
.Ptr
)
1100 /* Wake the Pushlock */
1101 ExfWakePushLock(PushLock
, NewValue
);
1107 * @name ExfUnblockPushLock
1108 * @implemented NT5.1
1110 * The ExfUnblockPushLock routine unblocks a previously blocked PushLock.
1113 * Pointer to a previously blocked PushLock.
1117 * @remarks Callers of ExfUnblockPushLock can be running at any IRQL.
1122 ExfUnblockPushLock(PEX_PUSH_LOCK PushLock
,
1123 PVOID CurrentWaitBlock
)
1125 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
, NextWaitBlock
;
1126 KIRQL OldIrql
= DISPATCH_LEVEL
;
1128 /* Get the wait block and erase the previous one */
1129 WaitBlock
= InterlockedExchangePointer(PushLock
->Ptr
, 0);
1132 /* Check if there is a linked pushlock and raise IRQL appropriately */
1133 if (WaitBlock
->Next
) KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
1135 /* Start block loop */
1138 /* Get the next block */
1139 NextWaitBlock
= WaitBlock
->Next
;
1141 /* Remove the wait flag from the Wait block */
1142 if (InterlockedBitTestAndReset(&WaitBlock
->Flags
, 1))
1144 /* Nobody removed the flag before us, so signal the event */
1145 KeSetEventBoostPriority(&WaitBlock
->WakeEvent
, IO_NO_INCREMENT
);
1148 /* Check if there was a next block */
1149 if (!NextWaitBlock
) break;
1152 /* Lower IRQL if needed */
1153 if (OldIrql
!= DISPATCH_LEVEL
) KeLowerIrql(OldIrql
);
1156 /* Check if we got a wait block that's pending */
1157 if ((CurrentWaitBlock
) &&
1158 (((PEX_PUSH_LOCK_WAIT_BLOCK
)CurrentWaitBlock
)->Flags
&
1159 EX_PUSH_LOCK_FLAGS_WAIT
))
1161 /* Wait for the pushlock to be unblocked */
1162 ExTimedWaitForUnblockPushLock(PushLock
, CurrentWaitBlock
, NULL
);