ReactOS 0.4.17-dev-806-gffa4164
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 completes the links in a push
217 * lock waiter chain.
218 *
219 * Wait blocks are inserted at the head using their Next link. This
220 * routine fills the corresponding Previous links and caches the tail
221 * pointer in the new head, allowing the wakeup to traverse the
222 * chain without first scanning it from the head.
223 *
224 * @param PushLock
225 * Pointer to a pushlock whose waiter list needs to be optimized.
226 *
227 * @param InitialValue
228 * Last known value of the pushlock installed by the caller when
229 * it inserted a new wait block and set the Waking bit.
230 *
231 * @return None.
232 *
233 * @remarks If the pushlock remains locked, this routine completes
234 * the wait block links and clears Waking. If the lock has
235 * been released, the pushlock will be wakened.
236 *
237 *--*/
238VOID
241 EX_PUSH_LOCK InitialValue)
242{
243 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock, PreviousWaitBlock, FirstWaitBlock;
244 EX_PUSH_LOCK OldValue, NewValue;
245
246 ASSERT(InitialValue.Locked);
247 ASSERT(InitialValue.Waiting);
248 ASSERT(InitialValue.Waking);
249
250 OldValue = InitialValue;
251
252 /* Start main loop */
253 for (;;)
254 {
255 /*
256 * A release may have cleared Locked while this thread owned
257 * Waking. In that case, retain responsibility for the chain
258 * and proceed directly to wakeup.
259 */
260 if (!OldValue.Locked)
261 {
262 /* Wake us up and leave */
263 ExfWakePushLock(PushLock, OldValue);
264 break;
265 }
266
267 /* Start at the waiter chain head encoded in the lock value. */
268 WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value &
270
271 FirstWaitBlock = WaitBlock;
272
273 /*
274 * Follow Next links until reaching an already completed part
275 * of the chain. Complete the reverse links along the way.
276 */
277 while (TRUE)
278 {
279 LastWaitBlock = WaitBlock->Last;
280 if (LastWaitBlock)
281 {
282 /* Cache the chain tail in the current head. */
283 FirstWaitBlock->Last = LastWaitBlock;
284 break;
285 }
286
287 PreviousWaitBlock = WaitBlock;
288
289 WaitBlock = WaitBlock->Next;
290
291 /* Link the older waiter back toward the chain head. */
292 WaitBlock->Previous = PreviousWaitBlock;
293 }
294
295 /*
296 * Release responsibility for the waiter chain now that its
297 * reverse links and cached tail are complete.
298 */
299 NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_WAKING;
300
301 /* Sanity checks */
302 ASSERT(NewValue.Locked);
303 ASSERT(!NewValue.Waking);
304
305 /* Update the value */
306 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
307 NewValue.Ptr,
308 OldValue.Ptr);
309
310 /* If waking was cleared successfully, we're done */
311 if (NewValue.Value == OldValue.Value) break;
312
313 /*
314 * A waiter was inserted or the lock was released. Retry from
315 * the current lock value while keeping Waking ownership.
316 */
317 OldValue = NewValue;
318 }
319}
320
321/*++
322 * @name ExTimedWaitForUnblockPushLock
323 *
324 * The ExTimedWaitForUnblockPushLock routine waits for a pushlock
325 * to be unblocked, for a specified internal.
326 *
327 * @param PushLock
328 * Pointer to a pushlock whose waiter list needs to be optimized.
329 *
330 * @param WaitBlock
331 * Pointer to the pushlock's wait block.
332 *
333 * @param Timeout
334 * Amount of time to wait for this pushlock to be unblocked.
335 *
336 * @return STATUS_SUCCESS is the pushlock is now unblocked, otherwise the error
337 * code returned by KeWaitForSingleObject.
338 *
339 * @remarks If the wait fails, then a manual unblock is attempted.
340 *
341 *--*/
345 IN PVOID WaitBlock,
347{
349
350 /* Initialize the wait event */
351 KeInitializeEvent(&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->WakeEvent,
353 FALSE);
354
355#ifdef CONFIG_SMP
356 /* Spin on the push lock if necessary */
358 {
360
361 do
362 {
363 /* Check if we got lucky and can leave early */
364 if (!(*(volatile LONG *)&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Flags & EX_PUSH_LOCK_WAITING))
365 return STATUS_SUCCESS;
366
368 } while (--i);
369 }
370#endif
371
372 /* Now try to remove the wait bit */
375 {
376 /* Nobody removed it already, let's do a full wait */
378 WakeEvent,
381 FALSE,
382 Timeout);
383 /* Check if the wait was satisfied */
384 if (Status != STATUS_SUCCESS)
385 {
386 /* Try unblocking the pushlock if it was not */
387 ExfUnblockPushLock(PushLock, WaitBlock);
388 }
389 }
390 else
391 {
392 /* Someone beat us to it, no need to wait */
394 }
395
396 /* Return status */
397 return Status;
398}
399
400/*++
401 * @name ExWaitForUnblockPushLock
402 *
403 * The ExWaitForUnblockPushLock routine waits for a pushlock
404 * to be unblocked, for a specified internal.
405 *
406 * @param PushLock
407 * Pointer to a pushlock whose waiter list needs to be optimized.
408 *
409 * @param WaitBlock
410 * Pointer to the pushlock's wait block.
411 *
412 * @return STATUS_SUCCESS is the pushlock is now unblocked, otherwise the error
413 * code returned by KeWaitForSingleObject.
414 *
415 * @remarks If the wait fails, then a manual unblock is attempted.
416 *
417 *--*/
418VOID
421 IN PVOID WaitBlock)
422{
423 /* Call the timed function with no timeout */
424 ExTimedWaitForUnblockPushLock(PushLock, WaitBlock, NULL);
425}
426
427/*++
428 * @name ExBlockPushLock
429 *
430 * The ExBlockPushLock routine blocks a pushlock.
431 *
432 * @param PushLock
433 * Pointer to a pushlock whose waiter list needs to be optimized.
434 *
435 * @param WaitBlock
436 * Pointer to the pushlock's wait block.
437 *
438 * @return None.
439 *
440 * @remarks None.
441 *
442 *--*/
443VOID
446 PVOID pWaitBlock)
447{
448 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock = pWaitBlock;
449 EX_PUSH_LOCK NewValue, OldValue;
450
451 /* Detect invalid wait block alignment */
452 ASSERT(((ULONG_PTR)pWaitBlock & 0xF) == 0);
453
454 /* Set the waiting bit */
455 WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_WAIT;
456
457 /* Get the old value */
458 OldValue = *PushLock;
459
460 /* Start block loop */
461 for (;;)
462 {
463 /* Link the wait blocks */
464 WaitBlock->Next = OldValue.Ptr;
465
466 /* Set the new wait block value */
467 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
468 WaitBlock,
469 OldValue.Ptr);
470 if (OldValue.Ptr == NewValue.Ptr) break;
471
472 /* Try again with the new value */
473 OldValue = NewValue;
474 }
475}
476
477/* PUBLIC FUNCTIONS **********************************************************/
478
479/*++
480 * @name ExAcquirePushLockExclusive
481 * @implemented NT5.1
482 *
483 * The ExAcquirePushLockExclusive macro exclusively acquires a PushLock.
484 *
485 * @params PushLock
486 * Pointer to the pushlock which is to be acquired.
487 *
488 * @return None.
489 *
490 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
491 * This macro should usually be paired up with KeAcquireCriticalRegion.
492 *
493 *--*/
494VOID
497{
498 EX_PUSH_LOCK OldValue = *PushLock, NewValue, TempValue;
499 BOOLEAN NeedWake;
501 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock = &Block;
502
503 /* Start main loop */
504 for (;;)
505 {
506 /* Check if it's unlocked */
507 if (!OldValue.Locked)
508 {
509 /* Lock it */
510 NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK;
511 ASSERT(NewValue.Locked);
512
513 /* Set the new value */
515 NewValue.Ptr,
516 OldValue.Ptr) != OldValue.Ptr)
517 {
518 /* Retry */
519 OldValue = *PushLock;
520 continue;
521 }
522
523 /* Break out of the loop */
524 break;
525 }
526 else
527 {
528 /* We'll have to create a Waitblock */
529 WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_EXCLUSIVE |
531 WaitBlock->Previous = NULL;
532 NeedWake = FALSE;
533
534 /* Check if there is already a waiter */
535 if (OldValue.Waiting)
536 {
537 /* The tail pointer is filled in when the wait list is linked */
538 WaitBlock->Last = NULL;
539
540 /* We are an exclusive waiter */
541 WaitBlock->ShareCount = 0;
542
543 /* Set the current Wait Block pointer */
544 WaitBlock->Next = (PEX_PUSH_LOCK_WAIT_BLOCK)(
545 OldValue.Value &~ EX_PUSH_LOCK_PTR_BITS);
546
547 /* Point to ours */
548 NewValue.Value = (OldValue.Value & EX_PUSH_LOCK_MULTIPLE_SHARED) |
552 (ULONG_PTR)WaitBlock;
553
554 /* Check if the pushlock was already waking */
555 if (!OldValue.Waking) NeedWake = TRUE;
556 }
557 else
558 {
559 /* The first waiter is both the head and the tail */
560 WaitBlock->Last = WaitBlock;
561
562 /* Set the share count */
563 WaitBlock->ShareCount = (LONG)OldValue.Shared;
564
565 /* Check if someone is sharing this pushlock */
566 if (OldValue.Shared > 1)
567 {
568 /* Point to our wait block */
569 NewValue.Value = EX_PUSH_LOCK_MULTIPLE_SHARED |
572 (ULONG_PTR)WaitBlock;
573 }
574 else
575 {
576 /* No shared count */
577 WaitBlock->ShareCount = 0;
578
579 /* Point to our wait block */
580 NewValue.Value = EX_PUSH_LOCK_LOCK |
582 (ULONG_PTR)WaitBlock;
583 }
584 }
585
586#if DBG
587 /* Setup the Debug Wait Block */
588 WaitBlock->Signaled = 0;
589 WaitBlock->OldValue = OldValue;
590 WaitBlock->NewValue = NewValue;
591 WaitBlock->PushLock = PushLock;
592#endif
593
594 /* Sanity check */
595 ASSERT(NewValue.Waiting);
596 ASSERT(NewValue.Locked);
597
598 /* Write the new value */
599 TempValue = NewValue;
600 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
601 NewValue.Ptr,
602 OldValue.Ptr);
603 if (NewValue.Value != OldValue.Value)
604 {
605 /* Retry */
606 OldValue = *PushLock;
607 continue;
608 }
609
610 /* Check if the pushlock needed waking */
611 if (NeedWake)
612 {
613 /* Scan the Waiters and Wake PushLocks */
614 ExpOptimizePushLockList(PushLock, TempValue);
615 }
616
617 /* Set up the Wait Gate */
618 KeInitializeGate(&WaitBlock->WakeGate);
619
620#ifdef CONFIG_SMP
621 /* Now spin on the push lock if necessary */
623 {
625
626 do
627 {
628 if (!(*(volatile LONG *)&WaitBlock->Flags & EX_PUSH_LOCK_WAITING))
629 break;
630
632 } while (--i);
633 }
634#endif
635
636 /* Now try to remove the wait bit */
637 if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1))
638 {
639 /* Nobody removed it already, let's do a full wait */
640 KeWaitForGate(&WaitBlock->WakeGate, WrPushLock, KernelMode);
641 ASSERT(WaitBlock->Signaled);
642 }
643
644 /* We shouldn't be shared anymore */
645 ASSERT((WaitBlock->ShareCount == 0));
646
647 /* Loop again */
648 OldValue = NewValue;
649 }
650 }
651}
652
653/*++
654 * @name ExAcquirePushLockShared
655 * @implemented NT5.1
656 *
657 * The ExAcquirePushLockShared routine acquires a shared PushLock.
658 *
659 * @params PushLock
660 * Pointer to the pushlock which is to be acquired.
661 *
662 * @return None.
663 *
664 * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
665 * This macro should usually be paired up with KeAcquireCriticalRegion.
666 *
667 *--*/
668VOID
671{
672 EX_PUSH_LOCK OldValue = *PushLock, NewValue, TempValue;
673 BOOLEAN NeedWake;
675 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock = &Block;
676
677 /* Start main loop */
678 for (;;)
679 {
680 /* Check if it's unlocked or if it's waiting without any sharers */
681 if (!(OldValue.Locked) || (!(OldValue.Waiting) && (OldValue.Shared > 0)))
682 {
683 /* Check if anyone is waiting on it */
684 if (!OldValue.Waiting)
685 {
686 /* Increase the share count and lock it */
687 NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK;
688 NewValue.Shared++;
689 }
690 else
691 {
692 /* Simply set the lock bit */
693 NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK;
694 }
695
696 /* Sanity check */
697 ASSERT(NewValue.Locked);
698
699 /* Set the new value */
700 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
701 NewValue.Ptr,
702 OldValue.Ptr);
703 if (NewValue.Value != OldValue.Value)
704 {
705 /* Retry */
706 OldValue = *PushLock;
707 continue;
708 }
709
710 /* Break out of the loop */
711 break;
712 }
713 else
714 {
715 /* We'll have to create a Waitblock */
716 WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_WAIT;
717 WaitBlock->ShareCount = 0;
718 NeedWake = FALSE;
719 WaitBlock->Previous = NULL;
720
721 /* Check if there is already a waiter */
722 if (OldValue.Waiting)
723 {
724 /* Set the current Wait Block pointer */
725 WaitBlock->Next = (PEX_PUSH_LOCK_WAIT_BLOCK)(
726 OldValue.Value &~ EX_PUSH_LOCK_PTR_BITS);
727
728 /* The tail pointer is filled in when the wait list is linked */
729 WaitBlock->Last = NULL;
730
731 /* Point to ours */
732 NewValue.Value = (OldValue.Value & (EX_PUSH_LOCK_MULTIPLE_SHARED |
736 (ULONG_PTR)WaitBlock;
737
738 /* Check if the pushlock was already waking */
739 if (!OldValue.Waking) NeedWake = TRUE;
740 }
741 else
742 {
743 /* The first waiter is both the head and the tail */
744 WaitBlock->Last = WaitBlock;
745
746 /* Point to our wait block */
747 NewValue.Value = (OldValue.Value & EX_PUSH_LOCK_PTR_BITS) |
749 (ULONG_PTR)WaitBlock;
750 }
751
752 /* Sanity check */
753 ASSERT(NewValue.Waiting);
754
755#if DBG
756 /* Setup the Debug Wait Block */
757 WaitBlock->Signaled = 0;
758 WaitBlock->OldValue = OldValue;
759 WaitBlock->NewValue = NewValue;
760 WaitBlock->PushLock = PushLock;
761#endif
762
763 /* Write the new value */
764 TempValue = NewValue;
765 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
766 NewValue.Ptr,
767 OldValue.Ptr);
768 if (NewValue.Ptr != OldValue.Ptr)
769 {
770 /* Retry */
771 OldValue = *PushLock;
772 continue;
773 }
774
775 /* Check if the pushlock needed waking */
776 if (NeedWake)
777 {
778 /* Scan the Waiters and Wake PushLocks */
779 ExpOptimizePushLockList(PushLock, TempValue);
780 }
781
782 /* Set up the Wait Gate */
783 KeInitializeGate(&WaitBlock->WakeGate);
784
785#ifdef CONFIG_SMP
786 /* Now spin on the push lock if necessary */
788 {
790
791 do
792 {
793 if (!(*(volatile LONG *)&WaitBlock->Flags & EX_PUSH_LOCK_WAITING))
794 break;
795
797 } while (--i);
798 }
799#endif
800
801 /* Now try to remove the wait bit */
802 if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1))
803 {
804 /* Fast-path did not work, we need to do a full wait */
805 KeWaitForGate(&WaitBlock->WakeGate, WrPushLock, KernelMode);
806 ASSERT(WaitBlock->Signaled);
807 }
808
809 /* We shouldn't be shared anymore */
810 ASSERT((WaitBlock->ShareCount == 0));
811 }
812 }
813}
814
815/*++
816 * @name ExfReleasePushLock
817 * @implemented NT5.1
818 *
819 * The ExReleasePushLock routine releases a previously acquired PushLock.
820 *
821 *
822 * @params PushLock
823 * Pointer to a previously acquired pushlock.
824 *
825 * @return None.
826 *
827 * @remarks Callers of ExfReleasePushLock must be running at IRQL <= APC_LEVEL.
828 * This macro should usually be paired up with KeLeaveCriticalRegion.
829 *
830 *--*/
831VOID
834{
835 EX_PUSH_LOCK OldValue = *PushLock, NewValue, WakeValue;
836 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock;
837
838 /* The caller must hold the push lock */
839 ASSERT(OldValue.Locked);
840
841 while (TRUE)
842 {
843 /*
844 * Without waiters, the upper bits contain the shared acquisition count.
845 * Drop one shared acquisition, or clear the lock word when releasing the
846 * final owner.
847 *
848 * A waiter may be inserted before the CMPXCHG completes. If that happens,
849 * continue using the value returned by the failed operation.
850 */
851 if (!OldValue.Waiting)
852 {
853 if (OldValue.Shared > 1)
854 {
855 NewValue = OldValue;
856 NewValue.Shared--;
857 }
858 else
859 {
860 NewValue.Value = 0;
861 }
862
863 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
864 NewValue.Ptr,
865 OldValue.Ptr);
866 if (NewValue.Value == OldValue.Value) return;
867
868 OldValue = NewValue;
869 }
870 else
871 {
872 /*
873 * Once waiters are queued, the upper bits hold a wait block pointer.
874 * If multiple shared owners existed when the first waiter was queued,
875 * their remaining count is stored in the oldest exclusive wait block,
876 * and MultipleShared marks that representation.
877 *
878 * If this call releases one of those shared owners, only the final
879 * shared release continues to unlock the push lock.
880 */
881 if (OldValue.MultipleShared)
882 {
883 WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value &
885
886 /*
887 * The push lock points to the newest wait block. Last contains
888 * the oldest block once the list has been optimized; otherwise
889 * follow Next until a wait block with Last set is found.
890 */
891 while (TRUE)
892 {
893 LastWaitBlock = WaitBlock->Last;
894 if (LastWaitBlock)
895 {
896 WaitBlock = LastWaitBlock;
897 break;
898 }
899
900 WaitBlock = WaitBlock->Next;
901 }
902
903 /*
904 * A generic release may reach this path after the saved shared
905 * acquisition count has already been exhausted.
906 */
907 if (WaitBlock->ShareCount > 0)
908 {
909 /* The outstanding shared count is stored in the oldest exclusive waiter */
910 ASSERT(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE);
911
912 if (InterlockedDecrement(&WaitBlock->ShareCount) > 0) return;
913 }
914 }
915
916 /*
917 * The final owner must clear Locked and MultipleShared. If no wakeup is
918 * in progress, it must also set Waking and process the wait list. A new
919 * waiter may be inserted while this update is being attempted, so both
920 * cases use CMPXCHG.
921 */
922 for (;;)
923 {
924 if (OldValue.Waking)
925 {
926 /*
927 * Another thread owns wakeup. Leave Waking set and
928 * clear only the ownership state.
929 */
930 NewValue.Value = OldValue.Value;
931 NewValue.MultipleShared = FALSE;
932 NewValue.Locked = FALSE;
933
934 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
935
936 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
937 NewValue.Ptr,
938 OldValue.Ptr);
939 if (NewValue.Value == OldValue.Value) return;
940 }
941 else
942 {
943 /*
944 * No thread is responsible for wakeup yet. Clear the ownership state
945 * and set Waking in the same atomic update so this thread becomes
946 * responsible for processing the wait list.
947 */
948 NewValue.Value = OldValue.Value;
949 NewValue.MultipleShared = FALSE;
950 NewValue.Locked = FALSE;
951
952 NewValue.Waking = TRUE;
953
954 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
955
956 WakeValue = NewValue;
957 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
958 NewValue.Ptr,
959 OldValue.Ptr);
960 if (NewValue.Value == OldValue.Value)
961 {
962 /* Wake the waiters after the state update succeeds */
963 ExfWakePushLock(PushLock, WakeValue);
964 return;
965 }
966 }
967
968 /*
969 * The failed CMPXCHG returned the current lock value, use it for
970 * the next attempt.
971 */
972 OldValue = NewValue;
973 }
974 }
975 }
976}
977
978/*++
979 * @name ExfReleasePushLockShared
980 * @implemented NT5.2
981 *
982 * The ExfReleasePushLockShared macro releases a previously acquired PushLock.
983 *
984 * @params PushLock
985 * Pointer to a previously acquired pushlock.
986 *
987 * @return None.
988 *
989 * @remarks Callers of ExReleasePushLockShared must be running at IRQL <= APC_LEVEL.
990 * This macro should usually be paired up with KeLeaveCriticalRegion.
991 *
992 *--*/
993VOID
996{
997 EX_PUSH_LOCK OldValue = *PushLock, NewValue, WakeValue;
998 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock;
999
1000 /* The caller must hold the push lock */
1001 ASSERT(OldValue.Locked);
1002
1003 /*
1004 * Without waiters, the upper bits contain the shared acquisition count.
1005 * Drop one shared acquisition, or clear the lock word when releasing the
1006 * final owner.
1007 *
1008 * A waiter may be inserted before the CMPXCHG completes. If that happens,
1009 * continue using the value returned by the failed operation.
1010 */
1011 while (!OldValue.Waiting)
1012 {
1013 if (OldValue.Shared > 1)
1014 {
1015 NewValue = OldValue;
1016 NewValue.Shared--;
1017 }
1018 else
1019 {
1020 NewValue.Value = 0;
1021 }
1022
1023 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1024 NewValue.Ptr,
1025 OldValue.Ptr);
1026 if (NewValue.Value == OldValue.Value) return;
1027
1028 OldValue = NewValue;
1029 }
1030
1031 /*
1032 * Once waiters are queued, the upper bits hold a wait block pointer.
1033 * If multiple shared owners existed when the first waiter was queued,
1034 * their remaining count is stored in the oldest exclusive wait block,
1035 * and MultipleShared marks that representation.
1036 *
1037 * Only the final shared release continues to unlock the push lock.
1038 */
1039 if (OldValue.MultipleShared)
1040 {
1041 WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)(OldValue.Value &
1043
1044 /*
1045 * The push lock points to the newest wait block. Last contains the
1046 * oldest block once the list has been optimized; otherwise follow
1047 * Next until a wait block with Last set is found.
1048 */
1049 while (TRUE)
1050 {
1051 LastWaitBlock = WaitBlock->Last;
1052 if (LastWaitBlock)
1053 {
1054 WaitBlock = LastWaitBlock;
1055 break;
1056 }
1057
1058 WaitBlock = WaitBlock->Next;
1059 }
1060
1061 /* The oldest exclusive waiter stores the remaining shared acquisitions */
1062 ASSERT(WaitBlock->ShareCount > 0);
1063 ASSERT(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE);
1064
1065 if (InterlockedDecrement(&WaitBlock->ShareCount) > 0) return;
1066 }
1067
1068 /*
1069 * The final owner must clear Locked and MultipleShared. If no wakeup is
1070 * in progress, it must also set Waking and process the wait list. A new
1071 * waiter may be inserted while this update is being attempted, so both
1072 * cases use CMPXCHG.
1073 */
1074 for (;;)
1075 {
1076 if (OldValue.Waking)
1077 {
1078 /*
1079 * Another thread owns wakeup. Leave Waking set and
1080 * clear only the ownership state.
1081 */
1082 NewValue.Value = OldValue.Value;
1083 NewValue.MultipleShared = FALSE;
1084 NewValue.Locked = FALSE;
1085
1086 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
1087
1088 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1089 NewValue.Ptr,
1090 OldValue.Ptr);
1091 if (NewValue.Value == OldValue.Value) return;
1092 }
1093 else
1094 {
1095 /*
1096 * No thread is responsible for wakeup yet. Clear the ownership state
1097 * and set Waking in the same atomic update so this thread becomes
1098 * responsible for processing the wait list.
1099 */
1100 NewValue.Value = OldValue.Value;
1101 NewValue.MultipleShared = FALSE;
1102 NewValue.Locked = FALSE;
1103
1104 NewValue.Waking = TRUE;
1105
1106 ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
1107
1108 WakeValue = NewValue;
1109 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1110 NewValue.Ptr,
1111 OldValue.Ptr);
1112 if (NewValue.Value == OldValue.Value)
1113 {
1114 /* Wake the waiters after the state update succeeds */
1115 ExfWakePushLock(PushLock, WakeValue);
1116 return;
1117 }
1118 }
1119
1120 /*
1121 * The failed CMPXCHG returned the current lock value, use it for
1122 * the next attempt.
1123 */
1124 OldValue = NewValue;
1125 }
1126}
1127
1128/*++
1129 * ExfReleasePushLockExclusive
1130 * @implemented NT5.2
1131 *
1132 * The ExfReleasePushLockExclusive routine releases a previously
1133 * exclusively acquired PushLock.
1134 *
1135 * @params PushLock
1136 * Pointer to a previously acquired pushlock.
1137 *
1138 * @return None.
1139 *
1140 * @remarks Callers of ExReleasePushLockExclusive must be running at IRQL <= APC_LEVEL.
1141 * This macro should usually be paired up with KeLeaveCriticalRegion.
1142 *
1143 *--*/
1144VOID
1147{
1148 EX_PUSH_LOCK NewValue, WakeValue;
1149 EX_PUSH_LOCK OldValue = *PushLock;
1150
1151 /* Loop until we can change */
1152 for (;;)
1153 {
1154 /* Sanity checks */
1155 ASSERT(OldValue.Locked);
1156 ASSERT(OldValue.Waiting || OldValue.Shared == 0);
1157
1158 /* Check if it's waiting and not yet waking */
1159 if ((OldValue.Waiting) && !(OldValue.Waking))
1160 {
1161 /* Remove the lock bit, and add the wake bit */
1162 NewValue.Value = (OldValue.Value &~ EX_PUSH_LOCK_LOCK) |
1164
1165 /* Sanity check */
1166 ASSERT(NewValue.Waking && !NewValue.Locked);
1167
1168 /* Write the New Value. Save our original value for waking */
1169 WakeValue = NewValue;
1170 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1171 NewValue.Ptr,
1172 OldValue.Ptr);
1173
1174 /* Check if the value changed behind our back */
1175 if (NewValue.Value == OldValue.Value)
1176 {
1177 /* Wake the Pushlock */
1178 ExfWakePushLock(PushLock, WakeValue);
1179 break;
1180 }
1181 }
1182 else
1183 {
1184 /* A simple unlock */
1185 NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_LOCK;
1186
1187 /* Sanity check */
1188 ASSERT(NewValue.Waking || !NewValue.Waiting);
1189
1190 /* Write the New Value */
1191 NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
1192 NewValue.Ptr,
1193 OldValue.Ptr);
1194
1195 /* Check if the value changed behind our back */
1196 if (NewValue.Value == OldValue.Value) break;
1197 }
1198
1199 /* Loop again */
1200 OldValue = NewValue;
1201 }
1202}
1203
1204/*++
1205 * @name ExfTryToWakePushLock
1206 * @implemented NT5.2
1207 *
1208 * The ExfTryToWakePushLock attempts to wake a waiting pushlock.
1209 *
1210 * @param PushLock
1211 * Pointer to a PushLock which is in the wait state.
1212 *
1213 * @return None.
1214 *
1215 * @remarks The pushlock must be in a wait state and must not be already waking.
1216 *
1217 *--*/
1218VOID
1221{
1222 EX_PUSH_LOCK OldValue = *PushLock, NewValue;
1223
1224 /*
1225 * If the Pushlock is not waiting on anything, or if it's already waking up
1226 * and locked, don't do anything
1227 */
1228 if ((OldValue.Waking) || (OldValue.Locked) || !(OldValue.Waiting)) return;
1229
1230 /* Make it Waking */
1231 NewValue = OldValue;
1232 NewValue.Waking = TRUE;
1233
1234 /* Write the New Value */
1236 NewValue.Ptr,
1237 OldValue.Ptr) == OldValue.Ptr)
1238 {
1239 /* Wake the Pushlock */
1240 ExfWakePushLock(PushLock, NewValue);
1241 }
1242}
1243
1244/*++
1245 * @name ExfUnblockPushLock
1246 * @implemented NT5.1
1247 *
1248 * The ExfUnblockPushLock routine unblocks a previously blocked PushLock.
1249 *
1250 * @param PushLock
1251 * Pointer to a previously blocked PushLock.
1252 *
1253 * @return None.
1254 *
1255 * @remarks Callers of ExfUnblockPushLock can be running at any IRQL.
1256 *
1257 *--*/
1258VOID
1261 PVOID CurrentWaitBlock)
1262{
1263 PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, NextWaitBlock;
1265
1266 /* Get the wait block and erase the previous one */
1267 WaitBlock = InterlockedExchangePointer(&PushLock->Ptr, NULL);
1268 if (WaitBlock)
1269 {
1270 /* Check if there is a linked pushlock and raise IRQL appropriately */
1271 if (WaitBlock->Next) KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
1272
1273 /* Start block loop */
1274 while (WaitBlock)
1275 {
1276 /* Get the next block */
1277 NextWaitBlock = WaitBlock->Next;
1278
1279 /* Remove the wait flag from the Wait block */
1281 {
1282 /* Nobody removed the flag before us, so signal the event */
1283 KeSetEventBoostPriority(&WaitBlock->WakeEvent, NULL);
1284 }
1285
1286 /* Try the next one */
1287 WaitBlock = NextWaitBlock;
1288 }
1289
1290 /* Lower IRQL if needed */
1292 }
1293
1294 /* Check if we got a wait block that's pending */
1295 if ((CurrentWaitBlock) &&
1296 (((PEX_PUSH_LOCK_WAIT_BLOCK)CurrentWaitBlock)->Flags &
1298 {
1299 /* Wait for the pushlock to be unblocked */
1300 ExWaitForUnblockPushLock(PushLock, CurrentWaitBlock);
1301 }
1302}
#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
_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID _In_ ULONG _In_ ULONG Flags
Definition: dispmprt.h:245
#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:344
VOID FASTCALL ExfReleasePushLock(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:833
VOID NTAPI ExpInitializePushLocks(VOID)
Definition: pushlock.c:45
VOID FASTCALL ExWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock, IN PVOID WaitBlock)
Definition: pushlock.c:420
VOID FASTCALL ExfTryToWakePushLock(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:1220
VOID FASTCALL ExfReleasePushLockShared(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:995
VOID FASTCALL ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:1146
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:496
VOID FASTCALL ExfUnblockPushLock(PEX_PUSH_LOCK PushLock, PVOID CurrentWaitBlock)
Definition: pushlock.c:1260
VOID FASTCALL ExBlockPushLock(PEX_PUSH_LOCK PushLock, PVOID pWaitBlock)
Definition: pushlock.c:445
#define InterlockedAndPointer(ptr, val)
Definition: pushlock.c:27
VOID FASTCALL ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock)
Definition: pushlock.c:670
VOID FASTCALL ExpOptimizePushLockList(PEX_PUSH_LOCK PushLock, EX_PUSH_LOCK InitialValue)
Definition: pushlock.c:240
#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
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:778
@ WrPushLock
Definition: ketypes.h:495