- Implement Pushlocks. Only Waking, and Exclusive Acquire/Release + Waits have been tested. The shared paths and block/unblock are still untested and may probably contain bugs. Pushlocks are not yet used in the kernel and they won't be until all paths are tested and validated.
- Public DDIs Implemented: ExfAcquirePushLockExclusive, ExfAcquirePushLockShared, ExfTryToWakePushLock, ExfReleasePushLock, ExfReleasePushLockExclusive, ExfReleasePushLockShared, ExfUnblockPushLock.
Added: trunk/reactos/ntoskrnl/ex/pushlock.c
Modified: trunk/reactos/ntoskrnl/include/internal/ex.h
Modified: trunk/reactos/ntoskrnl/include/internal/ntoskrnl.h
Modified: trunk/reactos/ntoskrnl/ntoskrnl.xml

Copied: trunk/reactos/ntoskrnl/ex/pushlock.c (from rev 20362, trunk/reactos/ntoskrnl/ex/synch.c)
--- trunk/reactos/ntoskrnl/ex/synch.c	2005-12-27 01:14:55 UTC (rev 20362)
+++ trunk/reactos/ntoskrnl/ex/pushlock.c	2006-01-03 23:36:05 UTC (rev 20557)
@@ -0,0 +1,1164 @@
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS Kernel
+ * FILE:            ntoskrnl/ex/pushlock.c
+ * PURPOSE:         Pushlock and Cache-Aware Pushlock Implementation
+ * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.com)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <internal/debug.h>
+
+/* DATA **********************************************************************/
+
+ULONG ExPushLockSpinCount;
+
+/* PRIVATE FUNCTIONS *********************************************************/
+
+/*++
+ * @name ExpInitializePushLocks
+ *
+ *     The ExpInitializePushLocks routine initialized Pushlock support.
+ *
+ * @param None.
+ *
+ * @return None.
+ *
+ * @remarks The ExpInitializePushLocks routine sets up the spin on SMP machines.
+ *
+ *--*/
+VOID
+NTAPI
+ExpInitializePushLocks(VOID)
+{
+    /* Initialize an internal 1024-iteration spin for MP CPUs */
+    ExPushLockSpinCount = (KeNumberProcessors == 1) ? 0 : 1024;
+}
+
+/*++
+ * @name ExfWakePushLock
+ *
+ *     The ExfWakePushLock routine wakes a Pushlock that is in the waiting
+ *     state.
+ *
+ * @param PushLock
+ *        Pointer to a pushlock that is waiting.
+ *
+ * @param OldValue
+ *        Last known value of the pushlock before this routine was called.
+ *
+ * @return None.
+ *
+ * @remarks This is an internal routine; do not call it manually. Only the system
+ *          can properly know if the pushlock is ready to be awakened or not.
+ *          External callers should use ExfTrytoWakePushLock.
+ *
+ *--*/
+VOID
+FASTCALL
+ExfWakePushLock(PEX_PUSH_LOCK PushLock,
+                EX_PUSH_LOCK OldValue)
+{
+    EX_PUSH_LOCK NewValue;
+    PEX_PUSH_LOCK_WAIT_BLOCK PreviousWaitBlock, FirstWaitBlock, NextWaitBlock;
+    PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
+    KIRQL OldIrql;
+
+    /* Start main wake loop */
+    for (;;)
+    {
+        /* Sanity checks */
+        ASSERT(!OldValue.MultipleShared);
+
+        /* Check if it's locked */
+        if (OldValue.Locked)
+        {
+            /* If it's locked we must simply un-wake it*/
+            for (;;)
+            {
+                /* It's not waking anymore */
+                NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_WAKING;
+
+                /* Sanity checks */
+                ASSERT(!NewValue.Waking);
+                ASSERT(NewValue.Locked);
+                ASSERT(NewValue.Waiting);
+
+                /* Write the New Value */
+                NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                                 NewValue.Ptr,
+                                                                 OldValue.Ptr);
+                if (NewValue.Value == OldValue.Value) return;
+
+                /* Someone changed the value behind our back, update it*/
+                OldValue = NewValue;
+
+                /* Check if it's still locked */
+                if (OldValue.Locked) continue;
+            }
+        }
+
+        /* Save the First Block */
+        FirstWaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR)OldValue.Ptr &
+                          ~EX_PUSH_LOCK_PTR_BITS);
+        NextWaitBlock = FirstWaitBlock;
+        WaitBlock = NextWaitBlock->Last;
+
+        /* Try to find a wait block */
+        while (!WaitBlock)
+        {
+            /* Save the previous block */
+            PreviousWaitBlock = NextWaitBlock;
+
+            /* Move to next block */
+            NextWaitBlock = NextWaitBlock->Next;
+
+            /* Save the previous block */
+            NextWaitBlock->Previous = PreviousWaitBlock;
+
+            /* Move to the next one */
+            WaitBlock = NextWaitBlock->Last;
+        }
+
+        /* Check if the last Wait Block is not Exclusive or if it's the only one */
+        PreviousWaitBlock = WaitBlock->Previous;
+        if (!(WaitBlock->Flags & EX_PUSH_LOCK_FLAGS_EXCLUSIVE) ||
+            !(PreviousWaitBlock))
+        {
+            /* Destroy the pushlock */
+            if (InterlockedCompareExchangePointer(PushLock, 0, OldValue.Ptr) ==
+                OldValue.Ptr) break;
+        }
+        else
+        {
+            /* Link the wait blocks */
+            FirstWaitBlock->Last = PreviousWaitBlock;
+            WaitBlock->Previous = NULL;
+
+            /* Sanity checks */
+            ASSERT(FirstWaitBlock != WaitBlock);
+            ASSERT(PushLock->Waiting);
+
+            /* Remove waking bit from pushlock */
+            InterlockedAnd((PLONG)PushLock, ~EX_PUSH_LOCK_WAKING);
+        }
+    }
+
+    /* Check if there's a previous block */
+    OldIrql = DISPATCH_LEVEL;
+    if (WaitBlock->Previous)
+    {
+        /* Raise to Dispatch */
+        KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+    }
+
+    /* Signaling loop */
+    for (;;)
+    {
+        /* Get the previous Wait block */
+        PreviousWaitBlock = WaitBlock->Previous;
+
+        /* Sanity check */
+        ASSERT(!WaitBlock->Signaled);
+
+        /* We are about to get signaled */
+        WaitBlock->Signaled = TRUE;
+
+        /* Set the Wait Bit in the Wait Block */
+        if (!InterlockedBitTestAndReset(&WaitBlock->Flags, 1))
+        {
+            /* Nobody signaled us, so do it */
+            KeSignalGateBoostPriority(&WaitBlock->WakeGate);
+        }
+
+        /* Set the wait block and check if there still is one to loop*/
+        WaitBlock = PreviousWaitBlock;
+        if (!WaitBlock) break;
+    }
+
+    /* Check if we have to lower back the IRQL */
+    if (OldIrql != DISPATCH_LEVEL) KeLowerIrql(OldIrql);
+}
+
+/*++
+ * @name ExpOptimizePushLockList
+ *
+ *     The ExpOptimizePushLockList routine optimizes the list of waiters
+ *     associated to a pushlock's wait block.
+ *
+ * @param PushLock
+ *        Pointer to a pushlock whose waiter list needs to be optimized.
+ *
+ * @param OldValue
+ *        Last known value of the pushlock before this routine was called.
+ *
+ * @return None.
+ *
+ * @remarks At the end of the optimization, the pushlock will also be wakened.
+ *
+ *--*/
+VOID
+FASTCALL
+ExpOptimizePushLockList(PEX_PUSH_LOCK PushLock,
+                        EX_PUSH_LOCK OldValue)
+{
+    PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock, LastWaitBlock, PreviousWaitBlock;
+    EX_PUSH_LOCK NewValue;
+
+    /* Check if the pushlock is locked */
+    if (OldValue.Locked)
+    {
+        /* Start main loop */
+        for (;;)
+        {
+            /* Get the wait block */
+            WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR)OldValue.Ptr &
+                        ~EX_PUSH_LOCK_PTR_BITS);
+
+            /* Loop the blocks */
+            LastWaitBlock = WaitBlock->Last;
+            while (LastWaitBlock)
+            {
+                /* Save the block */
+                PreviousWaitBlock = WaitBlock;
+
+                /* Get the next block */
+                WaitBlock = WaitBlock->Next;
+
+                /* Save the previous */
+                WaitBlock->Previous = PreviousWaitBlock;
+
+                /* Move to the next */
+                LastWaitBlock = WaitBlock->Last;
+            }
+
+            /* Remove the wake bit */
+            NewValue.Value = OldValue.Value &~ EX_PUSH_LOCK_WAKING;
+
+            /* Sanity checks */
+            ASSERT(NewValue.Locked);
+            ASSERT(!NewValue.Waking);
+
+            /* Update the value */
+            NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                             NewValue.Ptr,
+                                                             OldValue.Ptr);
+
+            /* If we updated correctly, leave */
+            if (NewValue.Value == OldValue.Value) return;
+
+            /* If the value is now locked, loop again */
+            if (NewValue.Locked) continue;
+        }
+    }
+
+    /* Wake the push lock */
+    ExfWakePushLock(PushLock, OldValue);
+}
+
+/*++
+ * @name ExTimedWaitForUnblockPushLock
+ *
+ *     The ExTimedWaitForUnblockPushLock routine waits for a pushlock
+ *     to be unblocked, for a specified internal.
+ *
+ * @param PushLock
+ *        Pointer to a pushlock whose waiter list needs to be optimized.
+ *
+ * @param WaitBlock
+ *        Pointer to the pushlock's wait block.
+ *
+ * @param Timeout
+ *        Amount of time to wait for this pushlock to be unblocked.
+ *
+ * @return STATUS_SUCCESS is the pushlock is now unblocked, otherwise the error
+ *         code returned by KeWaitForSingleObject.
+ *
+ * @remarks If the wait fails, then a manual unblock is attempted.
+ *
+ *--*/
+NTSTATUS
+FASTCALL
+ExTimedWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock,
+                              IN PVOID WaitBlock,
+                              IN PLARGE_INTEGER Timeout)
+{
+    ULONG i;
+    NTSTATUS Status;
+
+    /* Initialize the wait event */
+    KeInitializeEvent(&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->WakeEvent,
+                      NotificationEvent,
+                      FALSE);
+
+    /* Spin on the push lock if necessary */
+    i = ExPushLockSpinCount;
+    if (i)
+    {
+        /* Spin */
+        while (--i)
+        {
+            /* Check if we got lucky and can leave early */
+            if (!(((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Flags &
+                    EX_PUSH_LOCK_WAITING))
+            {
+                /* This wait block isn't waiting anymore, we can leave */
+                return STATUS_SUCCESS;
+            }
+            YieldProcessor();
+        }
+    }
+
+    /* Now try to remove the wait bit */
+    if (InterlockedBitTestAndReset(&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Flags,
+                                   1))
+    {
+        /* Nobody removed it already, let's do a full wait */
+        Status = KeWaitForSingleObject(&((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->
+                                       WakeEvent,
+                                       WrPushLock,
+                                       KernelMode,
+                                       FALSE,
+                                       Timeout);
+        if (!NT_SUCCESS(Status))
+        {
+            /* Try unblocking the pushlock */
+            ExfUnblockPushLock(PushLock, WaitBlock);
+        }
+    }
+    else
+    {
+        /* Someone beat us to it, no need to wait */
+        Status = STATUS_SUCCESS;
+    }
+
+    /* Return status */
+    return Status;
+}
+
+/*++
+ * @name ExBlockPushLock
+ *
+ *     The ExBlockPushLock routine blocks a pushlock.
+ *
+ * @param PushLock
+ *        Pointer to a pushlock whose waiter list needs to be optimized.
+ *
+ * @param WaitBlock
+ *        Pointer to the pushlock's wait block.
+ *
+ * @return None.
+ *
+ * @remarks None.
+ *
+ *--*/
+VOID
+FASTCALL
+ExBlockPushLock(PEX_PUSH_LOCK PushLock,
+                PVOID WaitBlock)
+{
+    PVOID NewValue, OldValue;
+
+    /* Set the waiting bit */
+    ((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Flags |= EX_PUSH_LOCK_FLAGS_WAIT;
+
+    /* Link the wait blocks */
+    ((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Next = PushLock->Ptr;
+
+    /* Try to set this one as the wait block now */
+    NewValue = PushLock->Ptr;
+    for (;;)
+    {
+        /* Set the new wait block value */
+        OldValue = InterlockedCompareExchangePointer(&PushLock->Ptr,
+                                                     WaitBlock,
+                                                     NewValue);
+        if (OldValue == NewValue) break;
+        NewValue = OldValue;
+    }
+}
+
+/* PUBLIC FUNCTIONS **********************************************************/
+
+/*++
+ * @name ExAcquirePushLockExclusive
+ * @implemented NT5.1
+ *
+ *     The ExAcquirePushLockExclusive macro exclusively acquires a PushLock.
+ *
+ * @params PushLock
+ *         Pointer to the pushlock which is to be acquired.
+ *
+ * @return None.
+ *
+ * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
+ *          This macro should usually be paired up with KeAcquireCriticalRegion.
+ *
+ *--*/
+VOID
+FASTCALL
+ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock)
+{
+    EX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
+    EX_PUSH_LOCK OldValue = *PushLock, NewValue, TempValue;
+    BOOLEAN NeedWake;
+    ULONG i;
+
+    /* Start main loop */
+    for (;;)
+    {
+        /* Check if it's unlocked */
+        if (!OldValue.Locked)
+        {
+            /* Lock it */
+            NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK;
+            ASSERT(NewValue.Locked);
+
+            /* Set the new value */
+            if (InterlockedCompareExchangePointer(PushLock,
+                                                  NewValue.Ptr,
+                                                  OldValue.Ptr) != OldValue.Ptr)
+            {
+                /* Retry */
+                OldValue = *PushLock;
+                continue;
+            }
+
+            /* Break out of the loop */
+            break;
+        }
+        else
+        {
+            /* We'll have to create a Waitblock */
+            WaitBlock.Flags = EX_PUSH_LOCK_FLAGS_EXCLUSIVE |
+                              EX_PUSH_LOCK_FLAGS_WAIT;
+            WaitBlock.Previous = NULL;
+            NeedWake = FALSE;
+
+            /* Check if there is already a waiter */
+            if (OldValue.Waiting)
+            {
+                /* Nobody is the last waiter yet */
+                WaitBlock.Last = NULL;
+
+                /* We are an exclusive waiter */
+                WaitBlock.ShareCount = 0;
+
+                /* Set the current Wait Block pointer */
+                WaitBlock.Next = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR)
+                                  OldValue.Ptr &~ EX_PUSH_LOCK_PTR_BITS);
+
+                /* Point to ours */
+                NewValue.Value = (OldValue.Value & EX_PUSH_LOCK_MULTIPLE_SHARED) |
+                                 EX_PUSH_LOCK_LOCK |
+                                 EX_PUSH_LOCK_WAKING |
+                                 EX_PUSH_LOCK_WAITING |
+                                 PtrToUlong(&WaitBlock);
+
+                /* Check if the pushlock was already waking */
+                if (OldValue.Waking) NeedWake = TRUE;
+            }
+            else
+            {
+                /* We are the first waiter, so loop the wait block */
+                WaitBlock.Last = &WaitBlock;
+
+                /* Set the share count */
+                WaitBlock.ShareCount = OldValue.Shared;
+
+                /* Check if someone is sharing this pushlock */
+                if (OldValue.Shared > 1)
+                {
+                    /* Point to our wait block */
+                    NewValue.Value = EX_PUSH_LOCK_MULTIPLE_SHARED |
+                                     EX_PUSH_LOCK_LOCK |
+                                     EX_PUSH_LOCK_WAITING |
+                                     PtrToUlong(&WaitBlock);
+                }
+                else
+                {
+                    /* No shared count */
+                    WaitBlock.ShareCount = 0;
+
+                    /* Point to our wait block */
+                    NewValue.Value = EX_PUSH_LOCK_LOCK |
+                                     EX_PUSH_LOCK_WAITING |
+                                     PtrToUlong(&WaitBlock);
+                }
+            }
+
+#if DBG
+            /* Setup the Debug Wait Block */
+            WaitBlock.Signaled = 0;
+            WaitBlock.OldValue = OldValue;
+            WaitBlock.NewValue = NewValue;
+            WaitBlock.PushLock = PushLock;
+#endif
+
+            /* Sanity check */
+            ASSERT(NewValue.Waiting);
+            ASSERT(NewValue.Locked);
+
+            /* Write the new value */
+            TempValue = NewValue;
+            NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                             NewValue.Ptr,
+                                                             OldValue.Ptr);
+            if (NewValue.Value != OldValue.Value)
+            {
+                /* Retry */
+                OldValue = *PushLock;
+                continue;
+            }
+
+            /* Check if the pushlock needed waking */
+            if (NeedWake)
+            {
+                /* Scan the Waiters and Wake PushLocks */
+                ExpOptimizePushLockList(PushLock, TempValue);
+            }
+
+            /* Set up the Wait Gate */
+            KeInitializeGate(&WaitBlock.WakeGate);
+
+            /* Now spin on the push lock if necessary */
+            i = ExPushLockSpinCount;
+            if ((i) && (WaitBlock.Flags & EX_PUSH_LOCK_WAITING))
+            {
+                /* Spin */
+                while (--i) YieldProcessor();
+            }
+
+            /* Now try to remove the wait bit */
+            if (InterlockedBitTestAndReset(&WaitBlock.Flags, 1))
+            {
+                /* Nobody removed it already, let's do a full wait */
+                KeWaitForGate(&WaitBlock.WakeGate, WrPushLock, KernelMode);
+                ASSERT(WaitBlock.Signaled);
+            }
+
+            /* We shouldn't be shared anymore */
+            ASSERT((WaitBlock.ShareCount == 0));
+
+            /* Loop again */
+            OldValue = NewValue;
+        }
+    }
+}
+
+/*++
+ * @name ExAcquirePushLockExclusive
+ * @implemented NT5.1
+ *
+ *     The ExAcquirePushLockShared macro acquires a shared PushLock.
+ *
+ * @params PushLock
+ *         Pointer to the pushlock which is to be acquired.
+ *
+ * @return None.
+ *
+ * @remarks Callers of ExAcquirePushLockShared must be running at IRQL <= APC_LEVEL.
+ *          This macro should usually be paired up with KeAcquireCriticalRegion.
+ *
+ *--*/
+VOID
+FASTCALL
+ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock)
+{
+    EX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
+    EX_PUSH_LOCK OldValue = *PushLock, NewValue;
+    BOOLEAN NeedWake;
+    ULONG i;
+
+    /* Start main loop */
+    for (;;)
+    {
+        /* Check if it's unlocked or if it's waiting without any sharers */
+        if (!(OldValue.Locked) || (OldValue.Waiting && OldValue.Shared == 0))
+        {
+            /* Check if anyone is waiting on it */
+            if (!OldValue.Waiting)
+            {
+                /* Increase the share count and lock it */
+                NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK;
+                NewValue.Shared++;
+            }
+            else
+            {
+                /* Simply set the lock bit */
+                NewValue.Value = OldValue.Value | EX_PUSH_LOCK_LOCK;
+            }
+
+            /* Sanity check */
+            ASSERT(NewValue.Locked);
+
+            /* Set the new value */
+            NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                             NewValue.Ptr,
+                                                             OldValue.Ptr);
+            if (NewValue.Value != OldValue.Value)
+            {
+                /* Retry */
+                OldValue = NewValue;
+                continue;
+            }
+
+            /* Break out of the loop */
+            break;
+        }
+        else
+        {
+            /* We'll have to create a Waitblock */
+            WaitBlock.Flags = EX_PUSH_LOCK_FLAGS_WAIT;
+            WaitBlock.ShareCount = 0;
+            NeedWake = FALSE;
+            WaitBlock.Previous = NULL;
+
+            /* Check if there is already a waiter */
+            if (OldValue.Waiting)
+            {
+                /* Set the current Wait Block pointer */
+                WaitBlock.Next = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR)
+                                 OldValue.Ptr &~ EX_PUSH_LOCK_PTR_BITS);
+
+                /* Nobody is the last waiter yet */
+                WaitBlock.Last = NULL;
+
+                /* Point to ours */
+                NewValue.Value = (OldValue.Value & (EX_PUSH_LOCK_MULTIPLE_SHARED |
+                                                    EX_PUSH_LOCK_LOCK)) |
+                                  EX_PUSH_LOCK_WAKING |
+                                  EX_PUSH_LOCK_WAITING |
+                                  PtrToUlong(&WaitBlock);
+
+                /* Check if the pushlock was already waking */
+                if (OldValue.Waking) NeedWake = TRUE;
+            }
+            else
+            {
+                /* We are the first waiter, so loop the wait block */
+                WaitBlock.Last = &WaitBlock;
+
+                /* Point to our wait block */
+                NewValue.Value = (OldValue.Value & (EX_PUSH_LOCK_MULTIPLE_SHARED |
+                                                    EX_PUSH_LOCK_WAKING)) |
+                                  EX_PUSH_LOCK_WAITING |
+                                  PtrToUlong(&WaitBlock);
+            }
+
+            /* Sanity check */
+            ASSERT(NewValue.Waiting);
+
+#if DBG
+            /* Setup the Debug Wait Block */
+            WaitBlock.Signaled = 0;
+            WaitBlock.OldValue = OldValue;
+            WaitBlock.NewValue = NewValue;
+            WaitBlock.PushLock = PushLock;
+#endif
+
+            /* Write the new value */
+            if (InterlockedCompareExchangePointer(PushLock,
+                                                  NewValue.Ptr,
+                                                  OldValue.Ptr) != OldValue.Ptr)
+            {
+                /* Retry */
+                OldValue = NewValue;
+                continue;
+            }
+
+            /* Update the value now */
+            OldValue = NewValue;
+
+            /* Check if the pushlock needed waking */
+            if (NeedWake)
+            {
+                /* Scan the Waiters and Wake PushLocks */
+                ExpOptimizePushLockList(PushLock, OldValue);
+            }
+
+            /* Set up the Wait Gate */
+            KeInitializeGate(&WaitBlock.WakeGate);
+
+            /* Now spin on the push lock if necessary */
+            i = ExPushLockSpinCount;
+            if ((i) && (WaitBlock.Flags & EX_PUSH_LOCK_WAITING))
+            {
+                /* Spin */
+                while (--i) YieldProcessor();
+            }
+
+            /* Now try to remove the wait bit */
+            if (InterlockedBitTestAndReset(&WaitBlock.Flags, 1))
+            {
+                /* Fast-path did not work, we need to do a full wait */
+                KeWaitForGate(&WaitBlock.WakeGate, WrPushLock, KernelMode);
+                ASSERT(WaitBlock.Signaled);
+            }
+
+            /* We shouldn't be shared anymore */
+            ASSERT((WaitBlock.ShareCount == 0));
+        }
+    }
+}
+
+/*++
+ * @name ExfReleasePushLock
+ * @implemented NT5.1
+ *
+ *     The ExReleasePushLockExclusive routine releases a previously
+ *     exclusively acquired PushLock.
+ *
+ * @params PushLock
+ *         Pointer to a previously acquired pushlock.
+ *
+ * @return None.
+ *
+ * @remarks Callers of ExReleasePushLockExclusive must be running at IRQL <= APC_LEVEL.
+ *          This macro should usually be paired up with KeLeaveCriticalRegion.
+ *
+ *--*/
+VOID
+FASTCALL
+ExfReleasePushLock(PEX_PUSH_LOCK PushLock)
+{
+    EX_PUSH_LOCK OldValue = *PushLock;
+    EX_PUSH_LOCK NewValue;
+    PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
+
+    /* Sanity check */
+    ASSERT(OldValue.Locked);
+
+    /* Check if someone is waiting on the lock */
+    if (!OldValue.Waiting)
+    {
+        /* Nobody is waiting on it, so we'll try a quick release */
+        for (;;)
+        {
+            /* Check if it's shared */
+            if (OldValue.Shared > 1)
+            {
+                /* Write the Old Value but decrease share count */
+                NewValue = OldValue;
+                NewValue.Shared--;
+            }
+            else
+            {
+                /* Simply clear the lock */
+                NewValue.Value = 0;
+            }
+
+            /* Write the New Value */
+            NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                             NewValue.Ptr,
+                                                             OldValue.Ptr);
+            if (NewValue.Value == OldValue.Value)
+            {
+                /* No waiters left, we're done */
+                goto quit;
+            }
+
+            /* Did it enter a wait state? */
+            OldValue = NewValue;
+            if (NewValue.Waiting) break;
+        }
+    }
+
+    /* Ok, we do know someone is waiting on it. Are there more then one? */
+    if (OldValue.MultipleShared)
+    {
+        /* Find the last Wait Block */
+        for (WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR)OldValue.Ptr &
+                                                    ~EX_PUSH_LOCK_PTR_BITS);
+             WaitBlock->Last;
+             WaitBlock = WaitBlock->Next);
+
+        /* Make sure the Share Count is above 0 */
+        if (WaitBlock->ShareCount)
+        {
+            /* This shouldn't be an exclusive wait block */
+            ASSERT(WaitBlock->Flags&EX_PUSH_LOCK_FLAGS_EXCLUSIVE);
+
+            /* Do the decrease and check if the lock isn't shared anymore */
+            if (InterlockedExchangeAdd(&WaitBlock->ShareCount, -1))
+            {
+                /* Someone is still holding the lock */
+                goto quit;
+            }
+        }
+    }
+
+    /* 
+     * If nobody was waiting on the block, then we possibly reduced the number
+     * of times the pushlock was shared, and we unlocked it.
+     * If someone was waiting, and more then one person is waiting, then we
+     * reduced the number of times the pushlock is shared in the wait block.
+     * Therefore, at this point, we can now 'satisfy' the wait.
+     */
+    for (;;)
+    {
+        /* Now we need to see if it's waking */
+        if (OldValue.Waking)
+        {
+            /* Remove the lock and multiple shared bits */
+            NewValue.Value = OldValue.Value;
+            NewValue.MultipleShared = FALSE;
+            NewValue.Locked = FALSE;
+
+            /* Sanity check */
+            ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
+
+            /* Write the new value */
+            NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                             NewValue.Ptr,
+                                                             OldValue.Ptr);
+            if (NewValue.Value == OldValue.Value) break;
+
+            /* The value changed, try the unlock again */
+            continue;
+        }
+        else
+        {
+            /* Remove the lock and multiple shared bits */
+            NewValue.Value = OldValue.Value;
+            NewValue.MultipleShared = FALSE;
+            NewValue.Locked = FALSE;
+
+            /* It's not already waking, so add the wake bit */
+            NewValue.Waking = TRUE;
+
+            /* Sanity check */
+            ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
+
+            /* Write the new value */
+            NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                             NewValue.Ptr,
+                                                             OldValue.Ptr);
+            if (NewValue.Value != OldValue.Value) continue;
+
+            /* The write was successful. The pushlock is Unlocked and Waking */
+            ExfWakePushLock(PushLock, NewValue);
+            break;
+        }
+    }
+quit:
+    /* Done! */
+    return;
+}
+
+/*++
+ * @name ExfReleasePushLockShared
+ * @implemented NT5.2
+ *
+ *     The ExfReleasePushLockShared macro releases a previously acquired PushLock.
+ *
+ * @params PushLock
+ *         Pointer to a previously acquired pushlock.
+ *
+ * @return None.
+ *
+ * @remarks Callers of ExReleasePushLockShared must be running at IRQL <= APC_LEVEL.
+ *          This macro should usually be paired up with KeLeaveCriticalRegion.
+ *
+ *--*/
+VOID
+FASTCALL
+ExfReleasePushLockShared(PEX_PUSH_LOCK PushLock)
+{
+    EX_PUSH_LOCK OldValue = *PushLock;
+    EX_PUSH_LOCK NewValue;
+    PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock;
+
+    /* Check if someone is waiting on the lock */
+    if (!OldValue.Waiting)
+    {
+        /* Nobody is waiting on it, so we'll try a quick release */
+        for (;;)
+        {
+            /* Check if it's shared */
+            if (OldValue.Shared > 1) 
+            {
+                /* Write the Old Value but decrease share count */
+                NewValue = OldValue;
+                NewValue.Shared--;
+            }
+            else
+            {
+                /* Simply clear the lock */
+                NewValue.Value = 0;
+            }
+
+            /* Write the New Value */
+            NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                             NewValue.Ptr,
+                                                             OldValue.Ptr);
+            if (NewValue.Value == OldValue.Value)
+            {
+                /* No waiters left, we're done */
+                goto quit;
+            }
+
+            /* Did it enter a wait state? */
+            OldValue = NewValue;
+            if (NewValue.Waiting) break;
+        }
+    }
+
+    /* Ok, we do know someone is waiting on it. Are there more then one? */
+    if (OldValue.MultipleShared)
+    {
+        /* Find the last Wait Block */
+        for (WaitBlock = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR)OldValue.Ptr &
+                                                    ~EX_PUSH_LOCK_PTR_BITS);
+             WaitBlock->Last;
+             WaitBlock = WaitBlock->Next);
+
+        /* Sanity checks */
+        ASSERT(WaitBlock->ShareCount > 0);
+        ASSERT(WaitBlock->Flags&EX_PUSH_LOCK_FLAGS_EXCLUSIVE);
+
+        /* Do the decrease and check if the lock isn't shared anymore */
+        if (InterlockedExchangeAdd(&WaitBlock->ShareCount, -1)) goto quit;
+    }
+
+    /* 
+     * If nobody was waiting on the block, then we possibly reduced the number
+     * of times the pushlock was shared, and we unlocked it.
+     * If someone was waiting, and more then one person is waiting, then we
+     * reduced the number of times the pushlock is shared in the wait block.
+     * Therefore, at this point, we can now 'satisfy' the wait.
+     */
+    for (;;)
+    {
+        /* Now we need to see if it's waking */
+        if (OldValue.Waking)
+        {
+            /* Remove the lock and multiple shared bits */
+            NewValue.Value = OldValue.Value;
+            NewValue.MultipleShared = FALSE;
+            NewValue.Locked = FALSE;
+
+            /* Sanity check */
+            ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
+
+            /* Write the new value */
+            NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                             NewValue.Ptr,
+                                                             OldValue.Ptr);
+            if (NewValue.Value == OldValue.Value) break;
+
+            /* The value changed, try the unlock again */
+            continue;
+        }
+        else
+        {
+            /* Remove the lock and multiple shared bits */
+            NewValue.Value = OldValue.Value;
+            NewValue.MultipleShared = FALSE;
+            NewValue.Locked = FALSE;
+
+            /* It's not already waking, so add the wake bit */
+            NewValue.Waking = TRUE;
+
+            /* Sanity check */
+            ASSERT(NewValue.Waking && !NewValue.Locked && !NewValue.MultipleShared);
+
+            /* Write the new value */
+            NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
+                                                             NewValue.Ptr,
+                                                             OldValue.Ptr);
+            if (NewValue.Value != OldValue.Value) continue;
+
+            /* The write was successful. The pushlock is Unlocked and Waking */
+            ExfWakePushLock(PushLock, NewValue);
+            break;
+        }
+    }
+quit:
+    /* Done! */
+    return;
+}
+
+/*++
+ * ExfReleasePushLockExclusive
+ * @implemented NT5.2
+ *
+ *     The ExfReleasePushLockExclusive routine releases a previously
+ *     exclusively acquired PushLock.
+ *
+ * @params PushLock
+ *         Pointer to a previously acquired pushlock.
+ *
+ * @return None.
[truncated at 1000 lines; 463 more skipped]