ReactOS 0.4.17-dev-804-g023d8af
pushlock.c File Reference
#include <ntoskrnl.h>
#include <debug.h>
Include dependency graph for pushlock.c:

Go to the source code of this file.

Macros

#define NDEBUG
 
#define InterlockedAndPointer(ptr, val)   InterlockedAnd((PLONG)ptr,(LONG)val)
 

Functions

VOID NTAPI ExpInitializePushLocks (VOID)
 
VOID FASTCALL ExfWakePushLock (PEX_PUSH_LOCK PushLock, EX_PUSH_LOCK OldValue)
 
VOID FASTCALL ExpOptimizePushLockList (PEX_PUSH_LOCK PushLock, EX_PUSH_LOCK InitialValue)
 
NTSTATUS FASTCALL ExTimedWaitForUnblockPushLock (IN PEX_PUSH_LOCK PushLock, IN PVOID WaitBlock, IN PLARGE_INTEGER Timeout)
 
VOID FASTCALL ExWaitForUnblockPushLock (IN PEX_PUSH_LOCK PushLock, IN PVOID WaitBlock)
 
VOID FASTCALL ExBlockPushLock (PEX_PUSH_LOCK PushLock, PVOID pWaitBlock)
 
VOID FASTCALL ExfAcquirePushLockExclusive (PEX_PUSH_LOCK PushLock)
 
VOID FASTCALL ExfAcquirePushLockShared (PEX_PUSH_LOCK PushLock)
 
VOID FASTCALL ExfReleasePushLock (PEX_PUSH_LOCK PushLock)
 
VOID FASTCALL ExfReleasePushLockShared (PEX_PUSH_LOCK PushLock)
 
VOID FASTCALL ExfReleasePushLockExclusive (PEX_PUSH_LOCK PushLock)
 
VOID FASTCALL ExfTryToWakePushLock (PEX_PUSH_LOCK PushLock)
 
VOID FASTCALL ExfUnblockPushLock (PEX_PUSH_LOCK PushLock, PVOID CurrentWaitBlock)
 

Variables

ULONG ExPushLockSpinCount = 0
 

Macro Definition Documentation

◆ InterlockedAndPointer

#define InterlockedAndPointer (   ptr,
  val 
)    InterlockedAnd((PLONG)ptr,(LONG)val)

Definition at line 27 of file pushlock.c.

◆ NDEBUG

#define NDEBUG

Definition at line 12 of file pushlock.c.

Function Documentation

◆ ExBlockPushLock()

VOID FASTCALL ExBlockPushLock ( PEX_PUSH_LOCK  PushLock,
PVOID  pWaitBlock 
)

Definition at line 445 of file pushlock.c.

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}
#define InterlockedCompareExchangePointer
Definition: interlocked.h:144
#define ASSERT(a)
Definition: mode.c:44
* PEX_PUSH_LOCK_WAIT_BLOCK
Definition: extypes.h:672
#define EX_PUSH_LOCK_FLAGS_WAIT
Definition: extypes.h:164
PVOID Ptr
Definition: extypes.h:637
uint32_t ULONG_PTR
Definition: typedefs.h:65

Referenced by ExpBlockOnLockedHandleEntry().

◆ ExfAcquirePushLockExclusive()

VOID FASTCALL ExfAcquirePushLockExclusive ( PEX_PUSH_LOCK  PushLock)

Definition at line 496 of file pushlock.c.

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}
#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 NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define ULONG_PTR
Definition: config.h:101
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 InterlockedBitTestAndReset
Definition: interlocked.h:35
if(dx< 0)
Definition: linetemp.h:194
#define EX_PUSH_LOCK_FLAGS_EXCLUSIVE
Definition: extypes.h:162
EX_PUSH_LOCK_WAIT_BLOCK
Definition: extypes.h:672
#define KernelMode
Definition: asm.h:38
VOID FASTCALL KeWaitForGate(PKGATE Gate, KWAIT_REASON WaitReason, KPROCESSOR_MODE WaitMode)
VOID FASTCALL KeInitializeGate(PKGATE Gate)
long LONG
Definition: pedump.c:60
ULONG ExPushLockSpinCount
Definition: pushlock.c:17
VOID FASTCALL ExpOptimizePushLockList(PEX_PUSH_LOCK PushLock, EX_PUSH_LOCK InitialValue)
Definition: pushlock.c:240
#define YieldProcessor
Definition: ke.h:48
ULONG_PTR Value
Definition: extypes.h:636
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
uint32_t ULONG
Definition: typedefs.h:59
@ WrPushLock
Definition: ketypes.h:495

Referenced by _Acquires_lock_(), ExAcquirePushLockExclusive(), ExWaitOnPushLock(), PushLockAcquire(), TestPushLockUncontended(), and TestPushLockWaiterSelection().

◆ ExfAcquirePushLockShared()

VOID FASTCALL ExfAcquirePushLockShared ( PEX_PUSH_LOCK  PushLock)

Definition at line 670 of file pushlock.c.

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}

Referenced by ExAcquirePushLockShared(), PushLockAcquire(), TestPushLockSharedOwnerDrain(), TestPushLockUncontended(), and TestPushLockWaiterArrivalDuringRelease().

◆ ExfReleasePushLock()

VOID FASTCALL ExfReleasePushLock ( PEX_PUSH_LOCK  PushLock)

Definition at line 833 of file pushlock.c.

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}
#define InterlockedDecrement
Definition: armddk.h:52
VOID FASTCALL ExfWakePushLock(PEX_PUSH_LOCK PushLock, EX_PUSH_LOCK OldValue)
Definition: pushlock.c:75
ULONG_PTR MultipleShared
Definition: extypes.h:633

Referenced by _Releases_lock_(), ExReleasePushLock(), and PushLockRelease().

◆ ExfReleasePushLockExclusive()

VOID FASTCALL ExfReleasePushLockExclusive ( PEX_PUSH_LOCK  PushLock)

Definition at line 1146 of file pushlock.c.

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}

Referenced by ExWaitOnPushLock(), PushLockRelease(), and TestPushLockWaiterSelection().

◆ ExfReleasePushLockShared()

VOID FASTCALL ExfReleasePushLockShared ( PEX_PUSH_LOCK  PushLock)

Definition at line 995 of file pushlock.c.

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}

Referenced by ExReleasePushLockShared(), PushLockRelease(), and TestPushLockWaiterArrivalDuringRelease().

◆ ExfTryToWakePushLock()

VOID FASTCALL ExfTryToWakePushLock ( PEX_PUSH_LOCK  PushLock)

Definition at line 1220 of file pushlock.c.

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}

Referenced by ExReleasePushLockExclusive().

◆ ExfUnblockPushLock()

VOID FASTCALL ExfUnblockPushLock ( PEX_PUSH_LOCK  PushLock,
PVOID  CurrentWaitBlock 
)

Definition at line 1260 of file pushlock.c.

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}
_In_ D3DDDI_VIDEO_PRESENT_TARGET_ID _In_ ULONG _In_ ULONG Flags
Definition: dispmprt.h:245
#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 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
#define EX_PUSH_LOCK_FLAGS_WAIT_V
Definition: extypes.h:163
VOID FASTCALL ExWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock, IN PVOID WaitBlock)
Definition: pushlock.c:420
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:778

Referenced by ExDestroyHandle(), ExpBlockOnLockedHandleEntry(), ExTimedWaitForUnblockPushLock(), and ExUnlockHandleTableEntry().

◆ ExfWakePushLock()

VOID FASTCALL ExfWakePushLock ( PEX_PUSH_LOCK  PushLock,
EX_PUSH_LOCK  OldValue 
)

Definition at line 75 of file pushlock.c.

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}
VOID FASTCALL KeSignalGateBoostPriority(PKGATE Gate)
#define InterlockedAndPointer(ptr, val)
Definition: pushlock.c:27

Referenced by ExfReleasePushLock(), ExfReleasePushLockExclusive(), ExfReleasePushLockShared(), ExfTryToWakePushLock(), and ExpOptimizePushLockList().

◆ ExpInitializePushLocks()

VOID NTAPI ExpInitializePushLocks ( VOID  )

Definition at line 45 of file pushlock.c.

46{
47#ifdef CONFIG_SMP
48 /* Initialize an internal 1024-iteration spin for MP CPUs */
49 if (KeNumberProcessors > 1)
51#endif
52}
CCHAR KeNumberProcessors
Definition: processor.c:19

Referenced by ExpInitSystemPhase1().

◆ ExpOptimizePushLockList()

VOID FASTCALL ExpOptimizePushLockList ( PEX_PUSH_LOCK  PushLock,
EX_PUSH_LOCK  InitialValue 
)

Definition at line 240 of file pushlock.c.

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}

Referenced by ExfAcquirePushLockExclusive(), and ExfAcquirePushLockShared().

◆ ExTimedWaitForUnblockPushLock()

NTSTATUS FASTCALL ExTimedWaitForUnblockPushLock ( IN PEX_PUSH_LOCK  PushLock,
IN PVOID  WaitBlock,
IN PLARGE_INTEGER  Timeout 
)

Definition at line 344 of file pushlock.c.

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}
LONG NTSTATUS
Definition: precomp.h:26
#define KeWaitForSingleObject(pEvt, foo, a, b, c)
Definition: env_spec_w32.h:478
#define KeInitializeEvent(pEvt, foo, foo2)
Definition: env_spec_w32.h:477
Status
Definition: gdiplustypes.h:24
@ SynchronizationEvent
static ULONG Timeout
Definition: ping.c:61
VOID FASTCALL ExfUnblockPushLock(PEX_PUSH_LOCK PushLock, PVOID CurrentWaitBlock)
Definition: pushlock.c:1260
#define STATUS_SUCCESS
Definition: shellext.h:65

Referenced by ExWaitForUnblockPushLock().

◆ ExWaitForUnblockPushLock()

VOID FASTCALL ExWaitForUnblockPushLock ( IN PEX_PUSH_LOCK  PushLock,
IN PVOID  WaitBlock 
)

Definition at line 420 of file pushlock.c.

422{
423 /* Call the timed function with no timeout */
424 ExTimedWaitForUnblockPushLock(PushLock, WaitBlock, NULL);
425}
NTSTATUS FASTCALL ExTimedWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock, IN PVOID WaitBlock, IN PLARGE_INTEGER Timeout)
Definition: pushlock.c:344

Referenced by ExfUnblockPushLock(), and ExpBlockOnLockedHandleEntry().

Variable Documentation

◆ ExPushLockSpinCount