{
EX_PUSH_LOCK OldValue = *PushLock, NewValue;
BOOLEAN NeedWake;
DEFINE_WAIT_BLOCK(WaitBlock);
/* 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->Ptr,
NewValue.Ptr,
OldValue.Ptr);
if (NewValue.Value != OldValue.Value)
{
/* Retry */
OldValue = *PushLock;
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)(
OldValue.Value &~ 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 |
(ULONG_PTR)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_PTR_BITS) |
EX_PUSH_LOCK_WAITING |
(ULONG_PTR)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 */
NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
NewValue.Ptr,
OldValue.Ptr);
if (NewValue.Ptr != OldValue.Ptr)
{
/* Retry */
OldValue = *PushLock;
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);
#ifdef CONFIG_SMP/* Now spin on the push lock if necessary */if (ExPushLockSpinCount)
{
ULONG i = ExPushLockSpinCount;
do
{
if (!(*(volatileLONG *)&WaitBlock->Flags & EX_PUSH_LOCK_WAITING))
break;
YieldProcessor();
} while (--i);
}
#endif/* 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));
}
}
}
Generated on Sun May 27 2012 06:06:41 for ReactOS by
1.7.6.1
ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.