ReactOS 0.4.17-dev-683-g0dafdc5
pushlock.c
Go to the documentation of this file.
1/*
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)
7 */
8
9/* INCLUDES *****************************************************************/
10
11#include <ntoskrnl.h>
12#define NDEBUG
13#include <debug.h>
14
15/* DATA **********************************************************************/
16
18
19#undef EX_PUSH_LOCK
20#undef PEX_PUSH_LOCK
21
22/* PRIVATE FUNCTIONS *********************************************************/
23
24#ifdef _WIN64
25#define InterlockedAndPointer(ptr,val) InterlockedAnd64((PLONGLONG)ptr,(LONGLONG)val)
26#else
27#define InterlockedAndPointer(ptr,val) InterlockedAnd((PLONG)ptr,(LONG)val)
28#endif
29
30/*++
31 * @name ExpInitializePushLocks
32 *
33 * The ExpInitializePushLocks routine initialized Pushlock support.
34 *
35 * @param None.
36 *
37 * @return None.
38 *
39 * @remarks The ExpInitializePushLocks routine sets up the spin on SMP machines.
40 *
41 *--*/
42CODE_SEG("INIT")
43VOID
46{
47#ifdef CONFIG_SMP
48 /* Initialize an internal 1024-iteration spin for MP CPUs */
49 if (KeNumberProcessors > 1)
51#endif
52}
53
54/*++
55 * @name ExfWakePushLock
56 *
57 * The ExfWakePushLock routine wakes a Pushlock that is in the waiting
58 * state.
59 *
60 * @param PushLock
61 * Pointer to a pushlock that is waiting.
62 *
63 * @param OldValue
64 * Last known value of the pushlock before this routine was called.
65 *
66 * @return None.
67 *
68 * @remarks This is an internal routine; do not call it manually. Only the system
69 * can properly know if the pushlock is ready to be awakened or not.
70 * External callers should use ExfTrytoWakePushLock.
71 *
72 *--*/
73VOID
76 EX_PUSH_LOCK OldValue)
77{
78 EX_PUSH_LOCK NewValue;
79 PEX_PUSH_LOCK_WAIT_BLOCK PreviousWaitBlock, FirstWaitBlock, LastWaitBlock;
82
83 /* Start main wake loop */
84 for (;;)
85 {
86 /* Sanity checks */
87 ASSERT(!OldValue.MultipleShared);
88
89 /* Check if it's locked */
90 while (OldValue.Locked)
91 {
92 /* It's not waking anymore */
93 NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_WAKING;
94
95 /* Sanity checks */
96 ASSERT(!NewValue.Waking);
97 ASSERT(NewValue.Locked);
98 ASSERT(NewValue.Waiting);
99
100 /* Write the New Value */
101 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
102 NewValue.Ptr,
103 OldValue.Ptr);
104 if (NewValue.Value == OldValue.Value) return;
105
106 /* Someone changed the value behind our back, update it*/
107 OldValue = NewValue;
108 }
109
110 /* Save the First Block */
111 FirstWaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value &
113 WaitBlock = FirstWaitBlock;
114
115 /* Try to find the last block */
116 while (TRUE)
117 {
118 /* Get the last wait block */
119 LastWaitBlock = WaitBlock->Last;
120
121 /* Check if we found it */
122 if (LastWaitBlock)
123 {
124 /* Use it */
125 WaitBlock = LastWaitBlock;
126 break;
127 }
128
129 /* Save the previous block */
130 PreviousWaitBlock = WaitBlock;
131
132 /* Move to next block */
133 WaitBlock = WaitBlock->Next;
134
135 /* Save the previous block */
136 WaitBlock->Previous = PreviousWaitBlock;
137 }
138
139 /* Check if the last Wait Block is not Exclusive or if it's the only one */
140 PreviousWaitBlock = WaitBlock->Previous;
141 if (!(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE) ||
142 !(PreviousWaitBlock))
143 {
144 /* Destroy the pushlock */
145 NewValue.Value = 0;
146 ASSERT(!NewValue.Waking);
147
148 /* Write the New Value */
149 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
150 NewValue.Ptr,
151 OldValue.Ptr);
152 if (NewValue.Value == OldValue.Value) break;
153
154 /* Someone changed the value behind our back, update it*/
155 OldValue = NewValue;
156 }
157 else
158 {
159 /* Link the wait blocks */
160 FirstWaitBlock->Last = PreviousWaitBlock;
161 WaitBlock->Previous = NULL;
162
163 /* Sanity checks */
164 ASSERT(FirstWaitBlock != WaitBlock);
165 ASSERT(PushLock->Waiting);
166
167 /* Remove waking bit from pushlock */
169
170 /* Leave the loop */
171 break;
172 }
173 }
174
175 /* Check if there's a previous block */
177 if (WaitBlock->Previous)
178 {
179 /* Raise to Dispatch */
181 }
182
183 /* Signaling loop */
184 for (;;)
185 {
186 /* Get the previous Wait block */
187 PreviousWaitBlock = WaitBlock->Previous;
188
189 /* Sanity check */
190 ASSERT(!WaitBlock->Signaled);
191
192#if DBG
193 /* We are about to get signaled */
194 WaitBlock->Signaled = TRUE;
195#endif
196
197 /* Set the Wait Bit in the Wait Block */
198 if (!InterlockedBitTestAndReset(&WaitBlock->Flags, 1))
199 {
200 /* Nobody signaled us, so do it */
201 KeSignalGateBoostPriority(&WaitBlock->WakeGate);
202 }
203
204 /* Set the wait block and check if there still is one to loop*/
205 WaitBlock = PreviousWaitBlock;
206 if (!WaitBlock) break;
207 }
208
209 /* Check if we have to lower back the IRQL */
211}
212
213/*++
214 * @name ExpOptimizePushLockList
215 *
216 * The ExpOptimizePushLockList routine optimizes the list of waiters
217 * associated to a pushlock's wait block.
218 *
219 * @param PushLock
220 * Pointer to a pushlock whose waiter list needs to be optimized.
221 *
222 * @param OldValue
223 * Last known value of the pushlock before this routine was called.
224 *
225 * @return None.
226 *
227 * @remarks At the end of the optimization, the pushlock will also be wakened.
228 *
229 *--*/
230VOID
233 EX_PUSH_LOCK OldValue)
234{
235 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock, PreviousWaitBlock, FirstWaitBlock;
236 EX_PUSH_LOCK NewValue;
237
238 /* Start main loop */
239 for (;;)
240 {
241 /* Check if we've been unlocked */
242 if (!OldValue.Locked)
243 {
244 /* Wake us up and leave */
245 ExfWakePushLock(PushLock, OldValue);
246 break;
247 }
248
249 /* Get the wait block */
250 WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value &
252
253 /* Loop the blocks */
254 FirstWaitBlock = WaitBlock;
255 while (TRUE)
256 {
257 /* Get the last wait block */
258 LastWaitBlock = WaitBlock->Last;
259 if (LastWaitBlock)
260 {
261 /* Set this as the new last block, we're done */
262 FirstWaitBlock->Last = LastWaitBlock;
263 break;
264 }
265
266 /* Save the block */
267 PreviousWaitBlock = WaitBlock;
268
269 /* Get the next block */
270 WaitBlock = WaitBlock->Next;
271
272 /* Save the previous */
273 WaitBlock->Previous = PreviousWaitBlock;
274 }
275
276 /* Remove the wake bit */
277 NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_WAKING;
278
279 /* Sanity checks */
280 ASSERT(NewValue.Locked);
281 ASSERT(!NewValue.Waking);
282
283 /* Update the value */
284 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
285 NewValue.Ptr,
286 OldValue.Ptr);
287
288 /* If we updated correctly, leave */
289 if (NewValue.Value == OldValue.Value) break;
290
291 /* Update value */
292 OldValue = NewValue;
293 }
294}
295
296/*++
297 * @name ExTimedWaitForUnblockPushLock
298 *
299 * The ExTimedWaitForUnblockPushLock routine waits for a pushlock
300 * to be unblocked, for a specified internal.
301 *
302 * @param PushLock
303 * Pointer to a pushlock whose waiter list needs to be optimized.
304 *
305 * @param WaitBlock
306 * Pointer to the pushlock's wait block.
307 *
308 * @param Timeout
309 * Amount of time to wait for this pushlock to be unblocked.
310 *
311 * @return STATUS_SUCCESS is the pushlock is now unblocked, otherwise the error
312 * code returned by KeWaitForSingleObject.
313 *
314 * @remarks If the wait fails, then a manual unblock is attempted.
315 *
316 *--*/
320 IN PVOID WaitBlock,
322{
324
325 /* Initialize the wait event */
326 KeInitializeEvent(&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->WakeEvent,
328 FALSE);
329
330#ifdef CONFIG_SMP
331 /* Spin on the push lock if necessary */
333 {
335
336 do
337 {
338 /* Check if we got lucky and can leave early */
339 if (!(*(volatile LONG *)&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Flags & EX_PUSH_LOCK_WAITING))
340 return STATUS_SUCCESS;
341
343 } while (--i);
344 }
345#endif
346
347 /* Now try to remove the wait bit */
350 {
351 /* Nobody removed it already, let's do a full wait */
353 WakeEvent,
356 FALSE,
357 Timeout);
358 /* Check if the wait was satisfied */
359 if (Status != STATUS_SUCCESS)
360 {
361 /* Try unblocking the pushlock if it was not */
362 ExfUnblockPushLock(PushLock, WaitBlock);
363 }
364 }
365 else
366 {
367 /* Someone beat us to it, no need to wait */
369 }
370
371 /* Return status */
372 return Status;
373}
374
375/*++
376 * @name ExWaitForUnblockPushLock
377 *
378 * The ExWaitForUnblockPushLock routine waits for a pushlock
379 * to be unblocked, for a specified internal.
380 *
381 * @param PushLock
382 * Pointer to a pushlock whose waiter list needs to be optimized.
383 *
384 * @param WaitBlock
385 * Pointer to the pushlock's wait block.
386 *
387 * @return STATUS_SUCCESS is the pushlock is now unblocked, otherwise the error
388 * code returned by KeWaitForSingleObject.
389 *
390 * @remarks If the wait fails, then a manual unblock is attempted.
391 *
392 *--*/
393VOID
396 IN PVOID WaitBlock)
397{
398 /* Call the timed function with no timeout */
399 ExTimedWaitForUnblockPushLock(PushLock, WaitBlock, NULL);
400}
401
402/*++
403 * @name ExBlockPushLock
404 *
405 * The ExBlockPushLock routine blocks a pushlock.
406 *
407 * @param PushLock
408 * Pointer to a pushlock whose waiter list needs to be optimized.
409 *
410 * @param WaitBlock
411 * Pointer to the pushlock's wait block.
412 *
413 * @return None.
414 *
415 * @remarks None.
416 *
417 *--*/
418VOID
421 PVOID pWaitBlock)
422{
423 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock = pWaitBlock;
424 EX_PUSH_LOCK NewValue, OldValue;
425
426 /* Detect invalid wait block alignment */
427 ASSERT(((ULONG_PTR)pWaitBlock & 0xF) == 0);
428
429 /* Set the waiting bit */
430 WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_WAIT;
431
432 /* Get the old value */
433 OldValue = *PushLock;
434
435 /* Start block loop */
436 for (;;)
437 {
438 /* Link the wait blocks */
439 WaitBlock->Next = OldValue.Ptr;
440
441 /* Set the new wait block value */
442 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
443 WaitBlock,
444 OldValue.Ptr);
445 if (OldValue.Ptr == NewValue.Ptr) break;
446
447 /* Try again with the new value */
448 OldValue = NewValue;
449 }
450}
451
452/* PUBLIC FUNCTIONS **********************************************************/
453
454/*++
455 * @name ExAcquirePushLockExclusive
456 * @implemented NT5.1
457 *
458 * The ExAcquirePushLockExclusive macro exclusively acquires a PushLock.
459 *
460 * @params PushLock
461 * Pointer to the pushlock which is to be acquired.
462 *
463 * @return None.
464 *
465 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
466 * This macro should usually be paired up with KeAcquireCriticalRegion.
467 *
468 *--*/
469VOID
472{
473 EX_PUSH_LOCK OldValue = *PushLock, NewValue, TempValue;
474 BOOLEAN NeedWake;
476 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock = &Block;
477
478 /* Start main loop */
479 for (;;)
480 {
481 /* Check if it's unlocked */
482 if (!OldValue.Locked)
483 {
484 /* Lock it */
485 NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK;
486 ASSERT(NewValue.Locked);
487
488 /* Set the new value */
490 NewValue.Ptr,
491 OldValue.Ptr) != OldValue.Ptr)
492 {
493 /* Retry */
494 OldValue = *PushLock;
495 continue;
496 }
497
498 /* Break out of the loop */
499 break;
500 }
501 else
502 {
503 /* We'll have to create a Waitblock */
504 WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_EXCLUSIVE |
506 WaitBlock->Previous = NULL;
507 NeedWake = FALSE;
508
509 /* Check if there is already a waiter */
510 if (OldValue.Waiting)
511 {
512 /* Nobody is the last waiter yet */
513 WaitBlock->Last = NULL;
514
515 /* We are an exclusive waiter */
516 WaitBlock->ShareCount = 0;
517
518 /* Set the current Wait Block pointer */
519 WaitBlock->Next = (PEX_PUSH_LOCK_WAIT_BLOCK)(
520 OldValue.Value &~ EX_PUSH_LOCK_PTR_BITS);
521
522 /* Point to ours */
523 NewValue.Value = (OldValue.Value & EX_PUSH_LOCK_MULTIPLE_SHARED) |
527 (ULONG_PTR)WaitBlock;
528
529 /* Check if the pushlock was already waking */
530 if (!OldValue.Waking) NeedWake = TRUE;
531 }
532 else
533 {
534 /* We are the first waiter, so loop the wait block */
535 WaitBlock->Last = WaitBlock;
536
537 /* Set the share count */
538 WaitBlock->ShareCount = (LONG)OldValue.Shared;
539
540 /* Check if someone is sharing this pushlock */
541 if (OldValue.Shared > 1)
542 {
543 /* Point to our wait block */
544 NewValue.Value = EX_PUSH_LOCK_MULTIPLE_SHARED |
547 (ULONG_PTR)WaitBlock;
548 }
549 else
550 {
551 /* No shared count */
552 WaitBlock->ShareCount = 0;
553
554 /* Point to our wait block */
555 NewValue.Value = EX_PUSH_LOCK_LOCK |
557 (ULONG_PTR)WaitBlock;
558 }
559 }
560
561#if DBG
562 /* Setup the Debug Wait Block */
563 WaitBlock->Signaled = 0;
564 WaitBlock->OldValue = OldValue;
565 WaitBlock->NewValue = NewValue;
566 WaitBlock->PushLock = PushLock;
567#endif
568
569 /* Sanity check */
570 ASSERT(NewValue.Waiting);
571 ASSERT(NewValue.Locked);
572
573 /* Write the new value */
574 TempValue = NewValue;
575 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
576 NewValue.Ptr,
577 OldValue.Ptr);
578 if (NewValue.Value != OldValue.Value)
579 {
580 /* Retry */
581 OldValue = *PushLock;
582 continue;
583 }
584
585 /* Check if the pushlock needed waking */
586 if (NeedWake)
587 {
588 /* Scan the Waiters and Wake PushLocks */
589 ExpOptimizePushLockList(PushLock, TempValue);
590 }
591
592 /* Set up the Wait Gate */
593 KeInitializeGate(&WaitBlock->WakeGate);
594
595#ifdef CONFIG_SMP
596 /* Now spin on the push lock if necessary */
598 {
600
601 do
602 {
603 if (!(*(volatile LONG *)&WaitBlock->Flags & EX_PUSH_LOCK_WAITING))
604 break;
605
607 } while (--i);
608 }
609#endif
610
611 /* Now try to remove the wait bit */
612 if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1))
613 {
614 /* Nobody removed it already, let's do a full wait */
615 KeWaitForGate(&WaitBlock->WakeGate, WrPushLock, KernelMode);
616 ASSERT(WaitBlock->Signaled);
617 }
618
619 /* We shouldn't be shared anymore */
620 ASSERT((WaitBlock->ShareCount == 0));
621
622 /* Loop again */
623 OldValue = NewValue;
624 }
625 }
626}
627
628/*++
629 * @name ExAcquirePushLockShared
630 * @implemented NT5.1
631 *
632 * The ExAcquirePushLockShared routine acquires a shared PushLock.
633 *
634 * @params PushLock
635 * Pointer to the pushlock which is to be acquired.
636 *
637 * @return None.
638 *
639 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
640 * This macro should usually be paired up with KeAcquireCriticalRegion.
641 *
642 *--*/
643VOID
646{
647 EX_PUSH_LOCK OldValue = *PushLock, NewValue;
648 BOOLEAN NeedWake;
650 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock = &Block;
651
652 /* Start main loop */
653 for (;;)
654 {
655 /* Check if it's unlocked or if it's waiting without any sharers */
656 if (!(OldValue.Locked) || (!(OldValue.Waiting) && (OldValue.Shared > 0)))
657 {
658 /* Check if anyone is waiting on it */
659 if (!OldValue.Waiting)
660 {
661 /* Increase the share count and lock it */
662 NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK;
663 NewValue.Shared++;
664 }
665 else
666 {
667 /* Simply set the lock bit */
668 NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK;
669 }
670
671 /* Sanity check */
672 ASSERT(NewValue.Locked);
673
674 /* Set the new value */
675 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
676 NewValue.Ptr,
677 OldValue.Ptr);
678 if (NewValue.Value != OldValue.Value)
679 {
680 /* Retry */
681 OldValue = *PushLock;
682 continue;
683 }
684
685 /* Break out of the loop */
686 break;
687 }
688 else
689 {
690 /* We'll have to create a Waitblock */
691 WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_WAIT;
692 WaitBlock->ShareCount = 0;
693 NeedWake = FALSE;
694 WaitBlock->Previous = NULL;
695
696 /* Check if there is already a waiter */
697 if (OldValue.Waiting)
698 {
699 /* Set the current Wait Block pointer */
700 WaitBlock->Next = (PEX_PUSH_LOCK_WAIT_BLOCK)(
701 OldValue.Value &~ EX_PUSH_LOCK_PTR_BITS);
702
703 /* Nobody is the last waiter yet */
704 WaitBlock->Last = NULL;
705
706 /* Point to ours */
707 NewValue.Value = (OldValue.Value & (EX_PUSH_LOCK_MULTIPLE_SHARED |
711 (ULONG_PTR)WaitBlock;
712
713 /* Check if the pushlock was already waking */
714 if (!OldValue.Waking) NeedWake = TRUE;
715 }
716 else
717 {
718 /* We are the first waiter, so loop the wait block */
719 WaitBlock->Last = WaitBlock;
720
721 /* Point to our wait block */
722 NewValue.Value = (OldValue.Value & EX_PUSH_LOCK_PTR_BITS) |
724 (ULONG_PTR)WaitBlock;
725 }
726
727 /* Sanity check */
728 ASSERT(NewValue.Waiting);
729
730#if DBG
731 /* Setup the Debug Wait Block */
732 WaitBlock->Signaled = 0;
733 WaitBlock->OldValue = OldValue;
734 WaitBlock->NewValue = NewValue;
735 WaitBlock->PushLock = PushLock;
736#endif
737
738 /* Write the new value */
739 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
740 NewValue.Ptr,
741 OldValue.Ptr);
742 if (NewValue.Ptr != OldValue.Ptr)
743 {
744 /* Retry */
745 OldValue = *PushLock;
746 continue;
747 }
748
749 /* Update the value now */
750 OldValue = NewValue;
751
752 /* Check if the pushlock needed waking */
753 if (NeedWake)
754 {
755 /* Scan the Waiters and Wake PushLocks */
756 ExpOptimizePushLockList(PushLock, OldValue);
757 }
758
759 /* Set up the Wait Gate */
760 KeInitializeGate(&WaitBlock->WakeGate);
761
762#ifdef CONFIG_SMP
763 /* Now spin on the push lock if necessary */
765 {
767
768 do
769 {
770 if (!(*(volatile LONG *)&WaitBlock->Flags & EX_PUSH_LOCK_WAITING))
771 break;
772
774 } while (--i);
775 }
776#endif
777
778 /* Now try to remove the wait bit */
779 if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1))
780 {
781 /* Fast-path did not work, we need to do a full wait */
782 KeWaitForGate(&WaitBlock->WakeGate, WrPushLock, KernelMode);
783 ASSERT(WaitBlock->Signaled);
784 }
785
786 /* We shouldn't be shared anymore */
787 ASSERT((WaitBlock->ShareCount == 0));
788 }
789 }
790}
791
792/*++
793 * @name ExfReleasePushLock
794 * @implemented NT5.1
795 *
796 * The ExReleasePushLock routine releases a previously acquired PushLock.
797 *
798 *
799 * @params PushLock
800 * Pointer to a previously acquired pushlock.
801 *
802 * @return None.
803 *
804 * @remarks Callers of ExfReleasePushLock must be running at IRQL <= APC_LEVEL.
805 * This macro should usually be paired up with KeLeaveCriticalRegion.
806 *
807 *--*/
808VOID
811{
812 EX_PUSH_LOCK OldValue = *PushLock, NewValue, WakeValue;
813 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock;
814
815 /* The caller must hold the push lock */
816 ASSERT(OldValue.Locked);
817
818 while (TRUE)
819 {
820 /*
821 * Without waiters, the upper bits contain the shared acquisition count.
822 * Drop one shared acquisition, or clear the lock word when releasing the
823 * final owner.
824 *
825 * A waiter may be inserted before the CMPXCHG completes. If that happens,
826 * continue using the value returned by the failed operation.
827 */
828 if (!OldValue.Waiting)
829 {
830 if (OldValue.Shared > 1)
831 {
832 NewValue = OldValue;
833 NewValue.Shared--;
834 }
835 else
836 {
837 NewValue.Value = 0;
838 }
839
840 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
841 NewValue.Ptr,
842 OldValue.Ptr);
843 if (NewValue.Value == OldValue.Value) return;
844
845 OldValue = NewValue;
846 }
847 else
848 {
849 /*
850 * Once waiters are queued, the upper bits hold a wait block pointer.
851 * If multiple shared owners existed when the first waiter was queued,
852 * their remaining count is stored in the oldest exclusive wait block,
853 * and MultipleShared marks that representation.
854 *
855 * If this call releases one of those shared owners, only the final
856 * shared release continues to unlock the push lock.
857 */
858 if (OldValue.MultipleShared)
859 {
860 WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value &
862
863 /*
864 * The push lock points to the newest wait block. Last contains
865 * the oldest block once the list has been optimized; otherwise
866 * follow Next until a wait block with Last set is found.
867 */
868 while (TRUE)
869 {
870 LastWaitBlock = WaitBlock->Last;
871 if (LastWaitBlock)
872 {
873 WaitBlock = LastWaitBlock;
874 break;
875 }
876
877 WaitBlock = WaitBlock->Next;
878 }
879
880 /*
881 * A generic release may reach this path after the saved shared
882 * acquisition count has already been exhausted.
883 */
884 if (WaitBlock->ShareCount > 0)
885 {
886 /* The outstanding shared count is stored in the oldest exclusive waiter */
887 ASSERT(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE);
888
889 if (InterlockedDecrement(&WaitBlock->ShareCount) > 0) return;
890 }
891 }
892
893 /*
894 * The final owner must clear Locked and MultipleShared. If no wakeup is
895 * in progress, it must also set Waking and process the wait list. A new
896 * waiter may be inserted while this update is being attempted, so both
897 * cases use CMPXCHG.
898 */
899 for (;;)
900 {
901 if (OldValue.Waking)
902 {
903 /*
904 * Another thread owns wakeup. Leave Waking set and
905 * clear only the ownership state.
906 */
907 NewValue.Value = OldValue.Value;
908 NewValue.MultipleShared = FALSE;
909 NewValue.Locked = FALSE;
910
911 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
912
913 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
914 NewValue.Ptr,
915 OldValue.Ptr);
916 if (NewValue.Value == OldValue.Value) return;
917 }
918 else
919 {
920 /*
921 * No thread is responsible for wakeup yet. Clear the ownership state
922 * and set Waking in the same atomic update so this thread becomes
923 * responsible for processing the wait list.
924 */
925 NewValue.Value = OldValue.Value;
926 NewValue.MultipleShared = FALSE;
927 NewValue.Locked = FALSE;
928
929 NewValue.Waking = TRUE;
930
931 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
932
933 WakeValue = NewValue;
934 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
935 NewValue.Ptr,
936 OldValue.Ptr);
937 if (NewValue.Value == OldValue.Value)
938 {
939 /* Wake the waiters after the state update succeeds */
940 ExfWakePushLock(PushLock, WakeValue);
941 return;
942 }
943 }
944
945 /*
946 * The failed CMPXCHG returned the current lock value, use it for
947 * the next attempt.
948 */
949 OldValue = NewValue;
950 }
951 }
952 }
953}
954
955/*++
956 * @name ExfReleasePushLockShared
957 * @implemented NT5.2
958 *
959 * The ExfReleasePushLockShared macro releases a previously acquired PushLock.
960 *
961 * @params PushLock
962 * Pointer to a previously acquired pushlock.
963 *
964 * @return None.
965 *
966 * @remarks Callers of ExReleasePushLockShared must be running at IRQL <= APC_LEVEL.
967 * This macro should usually be paired up with KeLeaveCriticalRegion.
968 *
969 *--*/
970VOID
973{
974 EX_PUSH_LOCK OldValue = *PushLock, NewValue, WakeValue;
975 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock;
976
977 /* The caller must hold the push lock */
978 ASSERT(OldValue.Locked);
979
980 /*
981 * Without waiters, the upper bits contain the shared acquisition count.
982 * Drop one shared acquisition, or clear the lock word when releasing the
983 * final owner.
984 *
985 * A waiter may be inserted before the CMPXCHG completes. If that happens,
986 * continue using the value returned by the failed operation.
987 */
988 while (!OldValue.Waiting)
989 {
990 if (OldValue.Shared > 1)
991 {
992 NewValue = OldValue;
993 NewValue.Shared--;
994 }
995 else
996 {
997 NewValue.Value = 0;
998 }
999
1000 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1001 NewValue.Ptr,
1002 OldValue.Ptr);
1003 if (NewValue.Value == OldValue.Value) return;
1004
1005 OldValue = NewValue;
1006 }
1007
1008 /*
1009 * Once waiters are queued, the upper bits hold a wait block pointer.
1010 * If multiple shared owners existed when the first waiter was queued,
1011 * their remaining count is stored in the oldest exclusive wait block,
1012 * and MultipleShared marks that representation.
1013 *
1014 * Only the final shared release continues to unlock the push lock.
1015 */
1016 if (OldValue.MultipleShared)
1017 {
1018 WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value &
1020
1021 /*
1022 * The push lock points to the newest wait block. Last contains the
1023 * oldest block once the list has been optimized; otherwise follow
1024 * Next until a wait block with Last set is found.
1025 */
1026 while (TRUE)
1027 {
1028 LastWaitBlock = WaitBlock->Last;
1029 if (LastWaitBlock)
1030 {
1031 WaitBlock = LastWaitBlock;
1032 break;
1033 }
1034
1035 WaitBlock = WaitBlock->Next;
1036 }
1037
1038 /* The oldest exclusive waiter stores the remaining shared acquisitions */
1039 ASSERT(WaitBlock->ShareCount > 0);
1040 ASSERT(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE);
1041
1042 if (InterlockedDecrement(&WaitBlock->ShareCount) > 0) return;
1043 }
1044
1045 /*
1046 * The final owner must clear Locked and MultipleShared. If no wakeup is
1047 * in progress, it must also set Waking and process the wait list. A new
1048 * waiter may be inserted while this update is being attempted, so both
1049 * cases use CMPXCHG.
1050 */
1051 for (;;)
1052 {
1053 if (OldValue.Waking)
1054 {
1055 /*
1056 * Another thread owns wakeup. Leave Waking set and
1057 * clear only the ownership state.
1058 */
1059 NewValue.Value = OldValue.Value;
1060 NewValue.MultipleShared = FALSE;
1061 NewValue.Locked = FALSE;
1062
1063 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
1064
1065 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1066 NewValue.Ptr,
1067 OldValue.Ptr);
1068 if (NewValue.Value == OldValue.Value) return;
1069 }
1070 else
1071 {
1072 /*
1073 * No thread is responsible for wakeup yet. Clear the ownership state
1074 * and set Waking in the same atomic update so this thread becomes
1075 * responsible for processing the wait list.
1076 */
1077 NewValue.Value = OldValue.Value;
1078 NewValue.MultipleShared = FALSE;
1079 NewValue.Locked = FALSE;
1080
1081 NewValue.Waking = TRUE;
1082
1083 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
1084
1085 WakeValue = NewValue;
1086 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1087 NewValue.Ptr,
1088 OldValue.Ptr);
1089 if (NewValue.Value == OldValue.Value)
1090 {
1091 /* Wake the waiters after the state update succeeds */
1092 ExfWakePushLock(PushLock, WakeValue);
1093 return;
1094 }
1095 }
1096
1097 /*
1098 * The failed CMPXCHG returned the current lock value, use it for
1099 * the next attempt.
1100 */
1101 OldValue = NewValue;
1102 }
1103}
1104
1105/*++
1106 * ExfReleasePushLockExclusive
1107 * @implemented NT5.2
1108 *
1109 * The ExfReleasePushLockExclusive routine releases a previously
1110 * exclusively acquired PushLock.
1111 *
1112 * @params PushLock
1113 * Pointer to a previously acquired pushlock.
1114 *
1115 * @return None.
1116 *
1117 * @remarks Callers of ExReleasePushLockExclusive must be running at IRQL <= APC_LEVEL.
1118 * This macro should usually be paired up with KeLeaveCriticalRegion.
1119 *
1120 *--*/
1121VOID
1124{
1125 EX_PUSH_LOCK NewValue, WakeValue;
1126 EX_PUSH_LOCK OldValue = *PushLock;
1127
1128 /* Loop until we can change */
1129 for (;;)
1130 {
1131 /* Sanity checks */
1132 ASSERT(OldValue.Locked);
1133 ASSERT(OldValue.Waiting || OldValue.Shared == 0);
1134
1135 /* Check if it's waiting and not yet waking */
1136 if ((OldValue.Waiting) && !(OldValue.Waking))
1137 {
1138 /* Remove the lock bit, and add the wake bit */
1139 NewValue.Value = (OldValue.Value &~ EX_PUSH_LOCK_LOCK) |
1141
1142 /* Sanity check */
1143 ASSERT(NewValue.Waking && !NewValue.Locked);
1144
1145 /* Write the New Value. Save our original value for waking */
1146 WakeValue = NewValue;
1147 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1148 NewValue.Ptr,
1149 OldValue.Ptr);
1150
1151 /* Check if the value changed behind our back */
1152 if (NewValue.Value == OldValue.Value)
1153 {
1154 /* Wake the Pushlock */
1155 ExfWakePushLock(PushLock, WakeValue);
1156 break;
1157 }
1158 }
1159 else
1160 {
1161 /* A simple unlock */
1162 NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_LOCK;
1163
1164 /* Sanity check */
1165 ASSERT(NewValue.Waking || !NewValue.Waiting);
1166
1167 /* Write the New Value */
1168 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1169 NewValue.Ptr,
1170 OldValue.Ptr);
1171
1172 /* Check if the value changed behind our back */
1173 if (NewValue.Value == OldValue.Value) break;
1174 }
1175
1176 /* Loop again */
1177 OldValue = NewValue;
1178 }
1179}
1180
1181/*++
1182 * @name ExfTryToWakePushLock
1183 * @implemented NT5.2
1184 *
1185 * The ExfTryToWakePushLock attempts to wake a waiting pushlock.
1186 *
1187 * @param PushLock
1188 * Pointer to a PushLock which is in the wait state.
1189 *
1190 * @return None.
1191 *
1192 * @remarks The pushlock must be in a wait state and must not be already waking.
1193 *
1194 *--*/
1195VOID
1198{
1199 EX_PUSH_LOCK OldValue = *PushLock, NewValue;
1200
1201 /*
1202 * If the Pushlock is not waiting on anything, or if it's already waking up
1203 * and locked, don't do anything
1204 */
1205 if ((OldValue.Waking) || (OldValue.Locked) || !(OldValue.Waiting)) return;
1206
1207 /* Make it Waking */
1208 NewValue = OldValue;
1209 NewValue.Waking = TRUE;
1210
1211 /* Write the New Value */
1213 NewValue.Ptr,
1214 OldValue.Ptr) == OldValue.Ptr)
1215 {
1216 /* Wake the Pushlock */
1217 ExfWakePushLock(PushLock, NewValue);
1218 }
1219}
1220
1221/*++
1222 * @name ExfUnblockPushLock
1223 * @implemented NT5.1
1224 *
1225 * The ExfUnblockPushLock routine unblocks a previously blocked PushLock.
1226 *
1227 * @param PushLock
1228 * Pointer to a previously blocked PushLock.
1229 *
1230 * @return None.
1231 *
1232 * @remarks Callers of ExfUnblockPushLock can be running at any IRQL.
1233 *
1234 *--*/
1235VOID
1238 PVOID CurrentWaitBlock)
1239{
1240 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, NextWaitBlock;
1242
1243 /* Get the wait block and erase the previous one */
1244 WaitBlock = InterlockedExchangePointer(&PushLock->Ptr, NULL);
1245 if (WaitBlock)
1246 {
1247 /* Check if there is a linked pushlock and raise IRQL appropriately */
1248 if (WaitBlock->Next) KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
1249
1250 /* Start block loop */
1251 while (WaitBlock)
1252 {
1253 /* Get the next block */
1254 NextWaitBlock = WaitBlock->Next;
1255
1256 /* Remove the wait flag from the Wait block */
1258 {
1259 /* Nobody removed the flag before us, so signal the event */
1260 KeSetEventBoostPriority(&WaitBlock->WakeEvent, NULL);
1261 }
1262
1263 /* Try the next one */
1264 WaitBlock = NextWaitBlock;
1265 }
1266
1267 /* Lower IRQL if needed */
1269 }
1270
1271 /* Check if we got a wait block that's pending */
1272 if ((CurrentWaitBlock) &&
1273 (((PEX_PUSH_LOCK_WAIT_BLOCK)CurrentWaitBlock)->Flags &
1275 {
1276 /* Wait for the pushlock to be unblocked */
1277 ExWaitForUnblockPushLock(PushLock, CurrentWaitBlock);
1278 }
1279}
#define CODE_SEG(...)
#define EX_PUSH_LOCK_PTR_BITS
Definition: Object.c:34
#define EX_PUSH_LOCK_LOCK
Definition: Object.c:29
#define EX_PUSH_LOCK_MULTIPLE_SHARED
Definition: Object.c:32
#define EX_PUSH_LOCK_WAITING
Definition: Object.c:30
#define EX_PUSH_LOCK_WAKING
Definition: Object.c:31
unsigned char BOOLEAN
Definition: actypes.h:127
#define InterlockedDecrement
Definition: armddk.h:52
LONG NTSTATUS
Definition: precomp.h:26
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define ULONG_PTR
Definition: config.h:101
#define InterlockedExchangePointer(Target, Value)
Definition: dshow.h:45
UCHAR KIRQL
Definition: env_spec_w32.h:591
#define KeRaiseIrql(irql, oldIrql)
Definition: env_spec_w32.h:597
#define KeWaitForSingleObject(pEvt, foo, a, b, c)
Definition: env_spec_w32.h:478
#define KeInitializeEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:477
#define KeLowerIrql(oldIrql)
Definition: env_spec_w32.h:602
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
VOID NTAPI KeSetEventBoostPriority(IN PKEVENT Event, IN PKTHREAD *WaitingThread OPTIONAL)
Definition: eventobj.c:230
Status
Definition: gdiplustypes.h:24
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
#define InterlockedCompareExchangePointer
Definition: interlocked.h:144
#define InterlockedBitTestAndReset
Definition: interlocked.h:35
if(dx< 0)
Definition: linetemp.h:194
#define ASSERT(a)
Definition: mode.c:44
#define EX_PUSH_LOCK_FLAGS_WAIT_V
Definition: extypes.h:163
* PEX_PUSH_LOCK_WAIT_BLOCK
Definition: extypes.h:672
#define EX_PUSH_LOCK_FLAGS_EXCLUSIVE
Definition: extypes.h:162
#define EX_PUSH_LOCK_FLAGS_WAIT
Definition: extypes.h:164
EX_PUSH_LOCK_WAIT_BLOCK
Definition: extypes.h:672
#define KernelMode
Definition: asm.h:38
#define FASTCALL
Definition: nt_native.h:50
@ SynchronizationEvent
VOID FASTCALL KeWaitForGate(PKGATE Gate, KWAIT_REASON WaitReason, KPROCESSOR_MODE WaitMode)
VOID FASTCALL KeSignalGateBoostPriority(PKGATE Gate)
VOID FASTCALL KeInitializeGate(PKGATE Gate)
CCHAR KeNumberProcessors
Definition: processor.c:19
long LONG
Definition: pedump.c:60
static ULONG Timeout
Definition: ping.c:61
NTSTATUS FASTCALL ExTimedWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock, IN PVOID WaitBlock, IN PLARGE_INTEGER Timeout)
Definition: pushlock.c:319
VOID FASTCALL ExfReleasePushLock(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:810
VOID NTAPI ExpInitializePushLocks(VOID)
Definition: pushlock.c:45
VOID FASTCALL ExpOptimizePushLockList(PEX_PUSH_LOCK PushLock, EX_PUSH_LOCK OldValue)
Definition: pushlock.c:232
VOID FASTCALL ExWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock, IN PVOID WaitBlock)
Definition: pushlock.c:395
VOID FASTCALL ExfTryToWakePushLock(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:1197
VOID FASTCALL ExfReleasePushLockShared(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:972
VOID FASTCALL ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:1123
ULONG ExPushLockSpinCount
Definition: pushlock.c:17
VOID FASTCALL ExfWakePushLock(PEX_PUSH_LOCK PushLock, EX_PUSH_LOCK OldValue)
Definition: pushlock.c:75
VOID FASTCALL ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:471
VOID FASTCALL ExfUnblockPushLock(PEX_PUSH_LOCK PushLock, PVOID CurrentWaitBlock)
Definition: pushlock.c:1237
VOID FASTCALL ExBlockPushLock(PEX_PUSH_LOCK PushLock, PVOID pWaitBlock)
Definition: pushlock.c:420
#define InterlockedAndPointer(ptr, val)
Definition: pushlock.c:27
VOID FASTCALL ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:645
#define YieldProcessor
Definition: ke.h:48
#define STATUS_SUCCESS
Definition: shellext.h:65
ULONG_PTR Value
Definition: extypes.h:636
ULONG_PTR MultipleShared
Definition: extypes.h:633
PVOID Ptr
Definition: extypes.h:637
ULONG_PTR Waiting
Definition: extypes.h:631
ULONG_PTR Waking
Definition: extypes.h:632
ULONG_PTR Shared
Definition: extypes.h:634
ULONG_PTR Locked
Definition: extypes.h:630
#define NTAPI
Definition: typedefs.h:36
uint32_t ULONG_PTR
Definition: typedefs.h:65
#define IN
Definition: typedefs.h:39
uint32_t ULONG
Definition: typedefs.h:59
_Must_inspect_result_ _In_ ULONG Flags
Definition: wsk.h:170
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:778
@ WrPushLock
Definition: ketypes.h:495