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
) break;
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
);
168 /* We are about to get signaled */
169 WaitBlock
->Signaled
= TRUE
;
172 /* Set the Wait Bit in the Wait Block */
173 if (!InterlockedBitTestAndReset(&WaitBlock
->Flags
, 1))
175 /* Nobody signaled us, so do it */
176 KeSignalGateBoostPriority(&WaitBlock
->WakeGate
);
179 /* Set the wait block and check if there still is one to loop*/
180 WaitBlock
= PreviousWaitBlock
;
181 if (!WaitBlock
) break;
184 /* Check if we have to lower back the IRQL */
185 if (OldIrql
!= DISPATCH_LEVEL
) KeLowerIrql(OldIrql
);
189 * @name ExpOptimizePushLockList
191 * The ExpOptimizePushLockList routine optimizes the list of waiters
192 * associated to a pushlock's wait block.
195 * Pointer to a pushlock whose waiter list needs to be optimized.
198 * Last known value of the pushlock before this routine was called.
202 * @remarks At the end of the optimization, the pushlock will also be wakened.
207 ExpOptimizePushLockList(PEX_PUSH_LOCK PushLock
,
208 EX_PUSH_LOCK OldValue
)
210 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
, LastWaitBlock
, PreviousWaitBlock
;
211 EX_PUSH_LOCK NewValue
;
213 /* Check if the pushlock is locked */
216 /* Start main loop */
219 /* Get the wait block */
220 WaitBlock
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)OldValue
.Ptr
&
221 ~EX_PUSH_LOCK_PTR_BITS
);
223 /* Loop the blocks */
224 LastWaitBlock
= WaitBlock
->Last
;
225 while (LastWaitBlock
)
228 PreviousWaitBlock
= WaitBlock
;
230 /* Get the next block */
231 WaitBlock
= WaitBlock
->Next
;
233 /* Save the previous */
234 WaitBlock
->Previous
= PreviousWaitBlock
;
236 /* Move to the next */
237 LastWaitBlock
= WaitBlock
->Last
;
240 /* Remove the wake bit */
241 NewValue
.Value
= OldValue
.Value
&~ EX_PUSH_LOCK_WAKING
;
244 ASSERT(NewValue
.Locked
);
245 ASSERT(!NewValue
.Waking
);
247 /* Update the value */
248 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
252 /* If we updated correctly, leave */
253 if (NewValue
.Value
== OldValue
.Value
) return;
255 /* If the value is now locked, loop again */
256 if (NewValue
.Locked
) continue;
260 /* Wake the push lock */
261 ExfWakePushLock(PushLock
, OldValue
);
265 * @name ExTimedWaitForUnblockPushLock
267 * The ExTimedWaitForUnblockPushLock routine waits for a pushlock
268 * to be unblocked, for a specified internal.
271 * Pointer to a pushlock whose waiter list needs to be optimized.
274 * Pointer to the pushlock's wait block.
277 * Amount of time to wait for this pushlock to be unblocked.
279 * @return STATUS_SUCCESS is the pushlock is now unblocked, otherwise the error
280 * code returned by KeWaitForSingleObject.
282 * @remarks If the wait fails, then a manual unblock is attempted.
287 ExTimedWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock
,
289 IN PLARGE_INTEGER Timeout
)
294 /* Initialize the wait event */
295 KeInitializeEvent(&((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->WakeEvent
,
299 /* Spin on the push lock if necessary */
300 i
= ExPushLockSpinCount
;
306 /* Check if we got lucky and can leave early */
307 if (!(((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->Flags
&
308 EX_PUSH_LOCK_WAITING
))
310 /* This wait block isn't waiting anymore, we can leave */
311 return STATUS_SUCCESS
;
317 /* Now try to remove the wait bit */
318 if (InterlockedBitTestAndReset(&((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->Flags
,
321 /* Nobody removed it already, let's do a full wait */
322 Status
= KeWaitForSingleObject(&((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->
328 if (!NT_SUCCESS(Status
))
330 /* Try unblocking the pushlock */
331 ExfUnblockPushLock(PushLock
, WaitBlock
);
336 /* Someone beat us to it, no need to wait */
337 Status
= STATUS_SUCCESS
;
345 * @name ExBlockPushLock
347 * The ExBlockPushLock routine blocks a pushlock.
350 * Pointer to a pushlock whose waiter list needs to be optimized.
353 * Pointer to the pushlock's wait block.
362 ExBlockPushLock(PEX_PUSH_LOCK PushLock
,
365 PVOID NewValue
, OldValue
;
367 /* Set the waiting bit */
368 ((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->Flags
|= EX_PUSH_LOCK_FLAGS_WAIT
;
370 /* Link the wait blocks */
371 ((PEX_PUSH_LOCK_WAIT_BLOCK
)WaitBlock
)->Next
= PushLock
->Ptr
;
373 /* Try to set this one as the wait block now */
374 NewValue
= PushLock
->Ptr
;
377 /* Set the new wait block value */
378 OldValue
= InterlockedCompareExchangePointer(&PushLock
->Ptr
,
381 if (OldValue
== NewValue
) break;
386 /* PUBLIC FUNCTIONS **********************************************************/
389 * @name ExAcquirePushLockExclusive
392 * The ExAcquirePushLockExclusive macro exclusively acquires a PushLock.
395 * Pointer to the pushlock which is to be acquired.
399 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
400 * This macro should usually be paired up with KeAcquireCriticalRegion.
405 ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock
)
407 EX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
408 EX_PUSH_LOCK OldValue
= *PushLock
, NewValue
, TempValue
;
412 /* Start main loop */
415 /* Check if it's unlocked */
416 if (!OldValue
.Locked
)
419 NewValue
.Value
= OldValue
.Value
| EX_PUSH_LOCK_LOCK
;
420 ASSERT(NewValue
.Locked
);
422 /* Set the new value */
423 if (InterlockedCompareExchangePointer(PushLock
,
425 OldValue
.Ptr
) != OldValue
.Ptr
)
428 OldValue
= *PushLock
;
432 /* Break out of the loop */
437 /* We'll have to create a Waitblock */
438 WaitBlock
.Flags
= EX_PUSH_LOCK_FLAGS_EXCLUSIVE
|
439 EX_PUSH_LOCK_FLAGS_WAIT
;
440 WaitBlock
.Previous
= NULL
;
443 /* Check if there is already a waiter */
444 if (OldValue
.Waiting
)
446 /* Nobody is the last waiter yet */
447 WaitBlock
.Last
= NULL
;
449 /* We are an exclusive waiter */
450 WaitBlock
.ShareCount
= 0;
452 /* Set the current Wait Block pointer */
453 WaitBlock
.Next
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)
454 OldValue
.Ptr
&~ EX_PUSH_LOCK_PTR_BITS
);
457 NewValue
.Value
= (OldValue
.Value
& EX_PUSH_LOCK_MULTIPLE_SHARED
) |
459 EX_PUSH_LOCK_WAKING
|
460 EX_PUSH_LOCK_WAITING
|
461 PtrToUlong(&WaitBlock
);
463 /* Check if the pushlock was already waking */
464 if (OldValue
.Waking
) NeedWake
= TRUE
;
468 /* We are the first waiter, so loop the wait block */
469 WaitBlock
.Last
= &WaitBlock
;
471 /* Set the share count */
472 WaitBlock
.ShareCount
= OldValue
.Shared
;
474 /* Check if someone is sharing this pushlock */
475 if (OldValue
.Shared
> 1)
477 /* Point to our wait block */
478 NewValue
.Value
= EX_PUSH_LOCK_MULTIPLE_SHARED
|
480 EX_PUSH_LOCK_WAITING
|
481 PtrToUlong(&WaitBlock
);
485 /* No shared count */
486 WaitBlock
.ShareCount
= 0;
488 /* Point to our wait block */
489 NewValue
.Value
= EX_PUSH_LOCK_LOCK
|
490 EX_PUSH_LOCK_WAITING
|
491 PtrToUlong(&WaitBlock
);
496 /* Setup the Debug Wait Block */
497 WaitBlock
.Signaled
= 0;
498 WaitBlock
.OldValue
= OldValue
;
499 WaitBlock
.NewValue
= NewValue
;
500 WaitBlock
.PushLock
= PushLock
;
504 ASSERT(NewValue
.Waiting
);
505 ASSERT(NewValue
.Locked
);
507 /* Write the new value */
508 TempValue
= NewValue
;
509 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
512 if (NewValue
.Value
!= OldValue
.Value
)
515 OldValue
= *PushLock
;
519 /* Check if the pushlock needed waking */
522 /* Scan the Waiters and Wake PushLocks */
523 ExpOptimizePushLockList(PushLock
, TempValue
);
526 /* Set up the Wait Gate */
527 KeInitializeGate(&WaitBlock
.WakeGate
);
529 /* Now spin on the push lock if necessary */
530 i
= ExPushLockSpinCount
;
531 if ((i
) && (WaitBlock
.Flags
& EX_PUSH_LOCK_WAITING
))
534 while (--i
) YieldProcessor();
537 /* Now try to remove the wait bit */
538 if (InterlockedBitTestAndReset(&WaitBlock
.Flags
, 1))
540 /* Nobody removed it already, let's do a full wait */
541 KeWaitForGate(&WaitBlock
.WakeGate
, WrPushLock
, KernelMode
);
542 ASSERT(WaitBlock
.Signaled
);
545 /* We shouldn't be shared anymore */
546 ASSERT((WaitBlock
.ShareCount
== 0));
555 * @name ExAcquirePushLockExclusive
558 * The ExAcquirePushLockShared macro acquires a shared PushLock.
561 * Pointer to the pushlock which is to be acquired.
565 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
566 * This macro should usually be paired up with KeAcquireCriticalRegion.
571 ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock
)
573 EX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
574 EX_PUSH_LOCK OldValue
= *PushLock
, NewValue
;
578 /* Start main loop */
581 /* Check if it's unlocked or if it's waiting without any sharers */
582 if (!(OldValue
.Locked
) || (OldValue
.Waiting
&& OldValue
.Shared
== 0))
584 /* Check if anyone is waiting on it */
585 if (!OldValue
.Waiting
)
587 /* Increase the share count and lock it */
588 NewValue
.Value
= OldValue
.Value
| EX_PUSH_LOCK_LOCK
;
593 /* Simply set the lock bit */
594 NewValue
.Value
= OldValue
.Value
| EX_PUSH_LOCK_LOCK
;
598 ASSERT(NewValue
.Locked
);
600 /* Set the new value */
601 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
604 if (NewValue
.Value
!= OldValue
.Value
)
611 /* Break out of the loop */
616 /* We'll have to create a Waitblock */
617 WaitBlock
.Flags
= EX_PUSH_LOCK_FLAGS_WAIT
;
618 WaitBlock
.ShareCount
= 0;
620 WaitBlock
.Previous
= NULL
;
622 /* Check if there is already a waiter */
623 if (OldValue
.Waiting
)
625 /* Set the current Wait Block pointer */
626 WaitBlock
.Next
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)
627 OldValue
.Ptr
&~ EX_PUSH_LOCK_PTR_BITS
);
629 /* Nobody is the last waiter yet */
630 WaitBlock
.Last
= NULL
;
633 NewValue
.Value
= (OldValue
.Value
& (EX_PUSH_LOCK_MULTIPLE_SHARED
|
634 EX_PUSH_LOCK_LOCK
)) |
635 EX_PUSH_LOCK_WAKING
|
636 EX_PUSH_LOCK_WAITING
|
637 PtrToUlong(&WaitBlock
);
639 /* Check if the pushlock was already waking */
640 if (OldValue
.Waking
) NeedWake
= TRUE
;
644 /* We are the first waiter, so loop the wait block */
645 WaitBlock
.Last
= &WaitBlock
;
647 /* Point to our wait block */
648 NewValue
.Value
= (OldValue
.Value
& (EX_PUSH_LOCK_MULTIPLE_SHARED
|
649 EX_PUSH_LOCK_WAKING
)) |
650 EX_PUSH_LOCK_WAITING
|
651 PtrToUlong(&WaitBlock
);
655 ASSERT(NewValue
.Waiting
);
658 /* Setup the Debug Wait Block */
659 WaitBlock
.Signaled
= 0;
660 WaitBlock
.OldValue
= OldValue
;
661 WaitBlock
.NewValue
= NewValue
;
662 WaitBlock
.PushLock
= PushLock
;
665 /* Write the new value */
666 if (InterlockedCompareExchangePointer(PushLock
,
668 OldValue
.Ptr
) != OldValue
.Ptr
)
675 /* Update the value now */
678 /* Check if the pushlock needed waking */
681 /* Scan the Waiters and Wake PushLocks */
682 ExpOptimizePushLockList(PushLock
, OldValue
);
685 /* Set up the Wait Gate */
686 KeInitializeGate(&WaitBlock
.WakeGate
);
688 /* Now spin on the push lock if necessary */
689 i
= ExPushLockSpinCount
;
690 if ((i
) && (WaitBlock
.Flags
& EX_PUSH_LOCK_WAITING
))
693 while (--i
) YieldProcessor();
696 /* Now try to remove the wait bit */
697 if (InterlockedBitTestAndReset(&WaitBlock
.Flags
, 1))
699 /* Fast-path did not work, we need to do a full wait */
700 KeWaitForGate(&WaitBlock
.WakeGate
, WrPushLock
, KernelMode
);
701 ASSERT(WaitBlock
.Signaled
);
704 /* We shouldn't be shared anymore */
705 ASSERT((WaitBlock
.ShareCount
== 0));
711 * @name ExfReleasePushLock
714 * The ExReleasePushLockExclusive routine releases a previously
715 * exclusively acquired PushLock.
718 * Pointer to a previously acquired pushlock.
722 * @remarks Callers of ExReleasePushLockExclusive must be running at IRQL <= APC_LEVEL.
723 * This macro should usually be paired up with KeLeaveCriticalRegion.
728 ExfReleasePushLock(PEX_PUSH_LOCK PushLock
)
730 EX_PUSH_LOCK OldValue
= *PushLock
;
731 EX_PUSH_LOCK NewValue
;
732 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
735 ASSERT(OldValue
.Locked
);
737 /* Check if someone is waiting on the lock */
738 if (!OldValue
.Waiting
)
740 /* Nobody is waiting on it, so we'll try a quick release */
743 /* Check if it's shared */
744 if (OldValue
.Shared
> 1)
746 /* Write the Old Value but decrease share count */
752 /* Simply clear the lock */
756 /* Write the New Value */
757 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
760 if (NewValue
.Value
== OldValue
.Value
)
762 /* No waiters left, we're done */
766 /* Did it enter a wait state? */
768 if (NewValue
.Waiting
) break;
772 /* Ok, we do know someone is waiting on it. Are there more then one? */
773 if (OldValue
.MultipleShared
)
775 /* Find the last Wait Block */
776 for (WaitBlock
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)OldValue
.Ptr
&
777 ~EX_PUSH_LOCK_PTR_BITS
);
779 WaitBlock
= WaitBlock
->Next
);
781 /* Make sure the Share Count is above 0 */
782 if (WaitBlock
->ShareCount
)
784 /* This shouldn't be an exclusive wait block */
785 ASSERT(WaitBlock
->Flags
&EX_PUSH_LOCK_FLAGS_EXCLUSIVE
);
787 /* Do the decrease and check if the lock isn't shared anymore */
788 if (InterlockedExchangeAdd(&WaitBlock
->ShareCount
, -1))
790 /* Someone is still holding the lock */
797 * If nobody was waiting on the block, then we possibly reduced the number
798 * of times the pushlock was shared, and we unlocked it.
799 * If someone was waiting, and more then one person is waiting, then we
800 * reduced the number of times the pushlock is shared in the wait block.
801 * Therefore, at this point, we can now 'satisfy' the wait.
805 /* Now we need to see if it's waking */
808 /* Remove the lock and multiple shared bits */
809 NewValue
.Value
= OldValue
.Value
;
810 NewValue
.MultipleShared
= FALSE
;
811 NewValue
.Locked
= FALSE
;
814 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
&& !NewValue
.MultipleShared
);
816 /* Write the new value */
817 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
820 if (NewValue
.Value
== OldValue
.Value
) break;
822 /* The value changed, try the unlock again */
827 /* Remove the lock and multiple shared bits */
828 NewValue
.Value
= OldValue
.Value
;
829 NewValue
.MultipleShared
= FALSE
;
830 NewValue
.Locked
= FALSE
;
832 /* It's not already waking, so add the wake bit */
833 NewValue
.Waking
= TRUE
;
836 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
&& !NewValue
.MultipleShared
);
838 /* Write the new value */
839 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
842 if (NewValue
.Value
!= OldValue
.Value
) continue;
844 /* The write was successful. The pushlock is Unlocked and Waking */
845 ExfWakePushLock(PushLock
, NewValue
);
855 * @name ExfReleasePushLockShared
858 * The ExfReleasePushLockShared macro releases a previously acquired PushLock.
861 * Pointer to a previously acquired pushlock.
865 * @remarks Callers of ExReleasePushLockShared must be running at IRQL <= APC_LEVEL.
866 * This macro should usually be paired up with KeLeaveCriticalRegion.
871 ExfReleasePushLockShared(PEX_PUSH_LOCK PushLock
)
873 EX_PUSH_LOCK OldValue
= *PushLock
;
874 EX_PUSH_LOCK NewValue
;
875 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
;
877 /* Check if someone is waiting on the lock */
878 if (!OldValue
.Waiting
)
880 /* Nobody is waiting on it, so we'll try a quick release */
883 /* Check if it's shared */
884 if (OldValue
.Shared
> 1)
886 /* Write the Old Value but decrease share count */
892 /* Simply clear the lock */
896 /* Write the New Value */
897 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
900 if (NewValue
.Value
== OldValue
.Value
)
902 /* No waiters left, we're done */
906 /* Did it enter a wait state? */
908 if (NewValue
.Waiting
) break;
912 /* Ok, we do know someone is waiting on it. Are there more then one? */
913 if (OldValue
.MultipleShared
)
915 /* Find the last Wait Block */
916 for (WaitBlock
= (PEX_PUSH_LOCK_WAIT_BLOCK
)((ULONG_PTR
)OldValue
.Ptr
&
917 ~EX_PUSH_LOCK_PTR_BITS
);
919 WaitBlock
= WaitBlock
->Next
);
922 ASSERT(WaitBlock
->ShareCount
> 0);
923 ASSERT(WaitBlock
->Flags
&EX_PUSH_LOCK_FLAGS_EXCLUSIVE
);
925 /* Do the decrease and check if the lock isn't shared anymore */
926 if (InterlockedExchangeAdd(&WaitBlock
->ShareCount
, -1)) goto quit
;
930 * If nobody was waiting on the block, then we possibly reduced the number
931 * of times the pushlock was shared, and we unlocked it.
932 * If someone was waiting, and more then one person is waiting, then we
933 * reduced the number of times the pushlock is shared in the wait block.
934 * Therefore, at this point, we can now 'satisfy' the wait.
938 /* Now we need to see if it's waking */
941 /* Remove the lock and multiple shared bits */
942 NewValue
.Value
= OldValue
.Value
;
943 NewValue
.MultipleShared
= FALSE
;
944 NewValue
.Locked
= FALSE
;
947 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
&& !NewValue
.MultipleShared
);
949 /* Write the new value */
950 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
953 if (NewValue
.Value
== OldValue
.Value
) break;
955 /* The value changed, try the unlock again */
960 /* Remove the lock and multiple shared bits */
961 NewValue
.Value
= OldValue
.Value
;
962 NewValue
.MultipleShared
= FALSE
;
963 NewValue
.Locked
= FALSE
;
965 /* It's not already waking, so add the wake bit */
966 NewValue
.Waking
= TRUE
;
969 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
&& !NewValue
.MultipleShared
);
971 /* Write the new value */
972 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
975 if (NewValue
.Value
!= OldValue
.Value
) continue;
977 /* The write was successful. The pushlock is Unlocked and Waking */
978 ExfWakePushLock(PushLock
, NewValue
);
988 * ExfReleasePushLockExclusive
991 * The ExfReleasePushLockExclusive routine releases a previously
992 * exclusively acquired PushLock.
995 * Pointer to a previously acquired pushlock.
999 * @remarks Callers of ExReleasePushLockExclusive must be running at IRQL <= APC_LEVEL.
1000 * This macro should usually be paired up with KeLeaveCriticalRegion.
1005 ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock
)
1007 EX_PUSH_LOCK NewValue
;
1008 EX_PUSH_LOCK OldValue
= *PushLock
;
1010 /* Loop until we can change */
1014 ASSERT(OldValue
.Locked
);
1015 ASSERT(OldValue
.Waiting
|| OldValue
.Shared
== 0);
1017 /* Check if it's waiting and not yet waking */
1018 if ((OldValue
.Waiting
) && !(OldValue
.Waking
))
1020 /* Remove the lock bit, and add the wake bit */
1021 NewValue
.Value
= (OldValue
.Value
&~ EX_PUSH_LOCK_LOCK
) |
1022 EX_PUSH_LOCK_WAKING
;
1025 ASSERT(NewValue
.Waking
&& !NewValue
.Locked
);
1027 /* Write the New Value */
1028 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
1032 /* Check if the value changed behind our back */
1033 if (NewValue
.Value
!= OldValue
.Value
)
1036 OldValue
= NewValue
;
1040 /* Wake the Pushlock */
1041 ExfWakePushLock(PushLock
, NewValue
);
1046 /* A simple unlock */
1047 NewValue
.Value
= OldValue
.Value
&~ EX_PUSH_LOCK_LOCK
;
1050 ASSERT(NewValue
.Waking
&& !NewValue
.Waiting
);
1052 /* Write the New Value */
1053 NewValue
.Ptr
= InterlockedCompareExchangePointer(PushLock
,
1057 /* Check if the value changed behind our back */
1058 if (NewValue
.Value
== OldValue
.Value
) break;
1061 OldValue
= NewValue
;
1067 * @name ExfTryToWakePushLock
1068 * @implemented NT5.2
1070 * The ExfTryToWakePushLock attemps to wake a waiting pushlock.
1073 * Pointer to a PushLock which is in the wait state.
1077 * @remarks The pushlock must be in a wait state and must not be already waking.
1082 ExfTryToWakePushLock(PEX_PUSH_LOCK PushLock
)
1084 EX_PUSH_LOCK OldValue
= *PushLock
, NewValue
;
1087 * If the Pushlock is not waiting on anything, or if it's already waking up
1088 * and locked, don't do anything
1090 if (!(OldValue
.Value
== (EX_PUSH_LOCK_WAKING
| EX_PUSH_LOCK_LOCK
)) &&
1093 /* Make it Waking */
1094 NewValue
= OldValue
;
1095 NewValue
.Waking
= TRUE
;
1097 /* Write the New Value */
1098 if (InterlockedCompareExchangePointer(PushLock
,
1100 OldValue
.Ptr
) == OldValue
.Ptr
)
1102 /* Wake the Pushlock */
1103 ExfWakePushLock(PushLock
, NewValue
);
1109 * @name ExfUnblockPushLock
1110 * @implemented NT5.1
1112 * The ExfUnblockPushLock routine unblocks a previously blocked PushLock.
1115 * Pointer to a previously blocked PushLock.
1119 * @remarks Callers of ExfUnblockPushLock can be running at any IRQL.
1124 ExfUnblockPushLock(PEX_PUSH_LOCK PushLock
,
1125 PVOID CurrentWaitBlock
)
1127 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock
, NextWaitBlock
;
1128 KIRQL OldIrql
= DISPATCH_LEVEL
;
1130 /* Get the wait block and erase the previous one */
1131 WaitBlock
= InterlockedExchangePointer(PushLock
->Ptr
, 0);
1134 /* Check if there is a linked pushlock and raise IRQL appropriately */
1135 if (WaitBlock
->Next
) KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
1137 /* Start block loop */
1140 /* Get the next block */
1141 NextWaitBlock
= WaitBlock
->Next
;
1143 /* Remove the wait flag from the Wait block */
1144 if (InterlockedBitTestAndReset(&WaitBlock
->Flags
, 1))
1146 /* Nobody removed the flag before us, so signal the event */
1147 KeSetEventBoostPriority(&WaitBlock
->WakeEvent
, IO_NO_INCREMENT
);
1150 /* Check if there was a next block */
1151 if (!NextWaitBlock
) break;
1154 /* Lower IRQL if needed */
1155 if (OldIrql
!= DISPATCH_LEVEL
) KeLowerIrql(OldIrql
);
1158 /* Check if we got a wait block that's pending */
1159 if ((CurrentWaitBlock
) &&
1160 (((PEX_PUSH_LOCK_WAIT_BLOCK
)CurrentWaitBlock
)->Flags
&
1161 EX_PUSH_LOCK_FLAGS_WAIT
))
1163 /* Wait for the pushlock to be unblocked */
1164 ExTimedWaitForUnblockPushLock(PushLock
, CurrentWaitBlock
, NULL
);