- Implement a cute little hack called DEFINE_WAIT_BLOCK which makes pushlocks work on GCC 3.4.5 as well as 4.1.2+ (with no perf-hit on the latter).

- Implement ExWaitForUnblockPushLock (just a wrapper around ExTimedWaitForUnblockPushLock).
- Simplfy ExBlockPushLock and fix some bugs.
- Fix a bug in ExfReleasePushLockExclusive when we have to wake the lock.
- Fix a bug in ExfUnblockPushLock which was touching the wrong pointer.
- Fix ExWaitOnPushLock to verify that the pushlock is actually locked.

svn path=/trunk/; revision=25584
This commit is contained in:
Alex Ionescu 2007-01-22 06:47:44 +00:00
parent 2f75cefc88
commit a0d7a72c1a
3 changed files with 161 additions and 78 deletions

View file

@ -1,11 +1,11 @@
/* /*
* PROJECT: ReactOS Kernel * PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory * LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ex/init.c * FILE: ntoskrnl/ex/init.c
* PURPOSE: Executive Initialization Code * PURPOSE: Executive Initialization Code
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Eric Kohl (ekohl@rz-online.de) * Eric Kohl (ekohl@rz-online.de)
*/ */
/* INCLUDES ******************************************************************/ /* INCLUDES ******************************************************************/

View file

@ -341,6 +341,33 @@ ExTimedWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock,
return Status; return Status;
} }
/*++
* @name ExWaitForUnblockPushLock
*
* The ExWaitForUnblockPushLock 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.
*
* @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.
*
*--*/
VOID
FASTCALL
ExWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock,
IN PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock)
{
/* Call the timed function with no timeout */
ExTimedWaitForUnblockPushLock(PushLock, WaitBlock, NULL);
}
/*++ /*++
* @name ExBlockPushLock * @name ExBlockPushLock
* *
@ -360,25 +387,33 @@ ExTimedWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock,
VOID VOID
FASTCALL FASTCALL
ExBlockPushLock(PEX_PUSH_LOCK PushLock, ExBlockPushLock(PEX_PUSH_LOCK PushLock,
PVOID WaitBlock) PVOID pWaitBlock)
{ {
PVOID NewValue, OldValue; PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock = pWaitBlock;
EX_PUSH_LOCK NewValue, OldValue;
/* Detect invalid wait block alignment */
ASSERT((ULONG_PTR)pWaitBlock & 0x10);
/* Set the waiting bit */ /* Set the waiting bit */
((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Flags |= EX_PUSH_LOCK_FLAGS_WAIT; WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_WAIT;
/* Get the old value */
OldValue = *PushLock;
/* Link the wait blocks */ /* Start block loop */
((PEX_PUSH_LOCK_WAIT_BLOCK)WaitBlock)->Next = PushLock->Ptr;
/* Try to set this one as the wait block now */
NewValue = PushLock->Ptr;
for (;;) for (;;)
{ {
/* Link the wait blocks */
WaitBlock->Next = OldValue.Ptr;
/* Set the new wait block value */ /* Set the new wait block value */
OldValue = InterlockedCompareExchangePointer(&PushLock->Ptr, NewValue.Ptr = InterlockedCompareExchangePointer(&PushLock->Ptr,
WaitBlock, WaitBlock,
NewValue); OldValue.Ptr);
if (OldValue == NewValue) break; if (OldValue.Ptr == NewValue.Ptr) break;
/* Try again with the new value */
NewValue = OldValue; NewValue = OldValue;
} }
} }
@ -404,7 +439,7 @@ VOID
FASTCALL FASTCALL
ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock) ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock)
{ {
EX_PUSH_LOCK_WAIT_BLOCK WaitBlock; DEFINE_WAIT_BLOCK(WaitBlock);
EX_PUSH_LOCK OldValue = *PushLock, NewValue, TempValue; EX_PUSH_LOCK OldValue = *PushLock, NewValue, TempValue;
BOOLEAN NeedWake; BOOLEAN NeedWake;
ULONG i; ULONG i;
@ -435,23 +470,23 @@ ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock)
else else
{ {
/* We'll have to create a Waitblock */ /* We'll have to create a Waitblock */
WaitBlock.Flags = EX_PUSH_LOCK_FLAGS_EXCLUSIVE | WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_EXCLUSIVE |
EX_PUSH_LOCK_FLAGS_WAIT; EX_PUSH_LOCK_FLAGS_WAIT;
WaitBlock.Previous = NULL; WaitBlock->Previous = NULL;
NeedWake = FALSE; NeedWake = FALSE;
/* Check if there is already a waiter */ /* Check if there is already a waiter */
if (OldValue.Waiting) if (OldValue.Waiting)
{ {
/* Nobody is the last waiter yet */ /* Nobody is the last waiter yet */
WaitBlock.Last = NULL; WaitBlock->Last = NULL;
/* We are an exclusive waiter */ /* We are an exclusive waiter */
WaitBlock.ShareCount = 0; WaitBlock->ShareCount = 0;
/* Set the current Wait Block pointer */ /* Set the current Wait Block pointer */
WaitBlock.Next = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR) WaitBlock->Next = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR)
OldValue.Ptr &~ EX_PUSH_LOCK_PTR_BITS); OldValue.Ptr &~ EX_PUSH_LOCK_PTR_BITS);
/* Point to ours */ /* Point to ours */
NewValue.Value = (OldValue.Value & EX_PUSH_LOCK_MULTIPLE_SHARED) | NewValue.Value = (OldValue.Value & EX_PUSH_LOCK_MULTIPLE_SHARED) |
@ -466,10 +501,10 @@ ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock)
else else
{ {
/* We are the first waiter, so loop the wait block */ /* We are the first waiter, so loop the wait block */
WaitBlock.Last = &WaitBlock; WaitBlock->Last = WaitBlock;
/* Set the share count */ /* Set the share count */
WaitBlock.ShareCount = OldValue.Shared; WaitBlock->ShareCount = OldValue.Shared;
/* Check if someone is sharing this pushlock */ /* Check if someone is sharing this pushlock */
if (OldValue.Shared > 1) if (OldValue.Shared > 1)
@ -483,7 +518,7 @@ ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock)
else else
{ {
/* No shared count */ /* No shared count */
WaitBlock.ShareCount = 0; WaitBlock->ShareCount = 0;
/* Point to our wait block */ /* Point to our wait block */
NewValue.Value = EX_PUSH_LOCK_LOCK | NewValue.Value = EX_PUSH_LOCK_LOCK |
@ -494,10 +529,10 @@ ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock)
#if DBG #if DBG
/* Setup the Debug Wait Block */ /* Setup the Debug Wait Block */
WaitBlock.Signaled = 0; WaitBlock->Signaled = 0;
WaitBlock.OldValue = OldValue; WaitBlock->OldValue = OldValue;
WaitBlock.NewValue = NewValue; WaitBlock->NewValue = NewValue;
WaitBlock.PushLock = PushLock; WaitBlock->PushLock = PushLock;
#endif #endif
/* Sanity check */ /* Sanity check */
@ -524,26 +559,26 @@ ExfAcquirePushLockExclusive(PEX_PUSH_LOCK PushLock)
} }
/* Set up the Wait Gate */ /* Set up the Wait Gate */
KeInitializeGate(&WaitBlock.WakeGate); KeInitializeGate(&WaitBlock->WakeGate);
/* Now spin on the push lock if necessary */ /* Now spin on the push lock if necessary */
i = ExPushLockSpinCount; i = ExPushLockSpinCount;
if ((i) && (WaitBlock.Flags & EX_PUSH_LOCK_WAITING)) if ((i) && (WaitBlock->Flags & EX_PUSH_LOCK_WAITING))
{ {
/* Spin */ /* Spin */
while (--i) YieldProcessor(); while (--i) YieldProcessor();
} }
/* Now try to remove the wait bit */ /* Now try to remove the wait bit */
if (InterlockedBitTestAndReset(&WaitBlock.Flags, 1)) if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1))
{ {
/* Nobody removed it already, let's do a full wait */ /* Nobody removed it already, let's do a full wait */
KeWaitForGate(&WaitBlock.WakeGate, WrPushLock, KernelMode); KeWaitForGate(&WaitBlock->WakeGate, WrPushLock, KernelMode);
ASSERT(WaitBlock.Signaled); ASSERT(WaitBlock->Signaled);
} }
/* We shouldn't be shared anymore */ /* We shouldn't be shared anymore */
ASSERT((WaitBlock.ShareCount == 0)); ASSERT((WaitBlock->ShareCount == 0));
/* Loop again */ /* Loop again */
OldValue = NewValue; OldValue = NewValue;
@ -570,7 +605,7 @@ VOID
FASTCALL FASTCALL
ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock) ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock)
{ {
EX_PUSH_LOCK_WAIT_BLOCK WaitBlock; DEFINE_WAIT_BLOCK(WaitBlock);
EX_PUSH_LOCK OldValue = *PushLock, NewValue; EX_PUSH_LOCK OldValue = *PushLock, NewValue;
BOOLEAN NeedWake; BOOLEAN NeedWake;
ULONG i; ULONG i;
@ -614,20 +649,20 @@ ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock)
else else
{ {
/* We'll have to create a Waitblock */ /* We'll have to create a Waitblock */
WaitBlock.Flags = EX_PUSH_LOCK_FLAGS_WAIT; WaitBlock->Flags = EX_PUSH_LOCK_FLAGS_WAIT;
WaitBlock.ShareCount = 0; WaitBlock->ShareCount = 0;
NeedWake = FALSE; NeedWake = FALSE;
WaitBlock.Previous = NULL; WaitBlock->Previous = NULL;
/* Check if there is already a waiter */ /* Check if there is already a waiter */
if (OldValue.Waiting) if (OldValue.Waiting)
{ {
/* Set the current Wait Block pointer */ /* Set the current Wait Block pointer */
WaitBlock.Next = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR) WaitBlock->Next = (PEX_PUSH_LOCK_WAIT_BLOCK)((ULONG_PTR)
OldValue.Ptr &~ EX_PUSH_LOCK_PTR_BITS); OldValue.Ptr &~ EX_PUSH_LOCK_PTR_BITS);
/* Nobody is the last waiter yet */ /* Nobody is the last waiter yet */
WaitBlock.Last = NULL; WaitBlock->Last = NULL;
/* Point to ours */ /* Point to ours */
NewValue.Value = (OldValue.Value & (EX_PUSH_LOCK_MULTIPLE_SHARED | NewValue.Value = (OldValue.Value & (EX_PUSH_LOCK_MULTIPLE_SHARED |
@ -642,7 +677,7 @@ ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock)
else else
{ {
/* We are the first waiter, so loop the wait block */ /* We are the first waiter, so loop the wait block */
WaitBlock.Last = &WaitBlock; WaitBlock->Last = WaitBlock;
/* Point to our wait block */ /* Point to our wait block */
NewValue.Value = (OldValue.Value & (EX_PUSH_LOCK_MULTIPLE_SHARED | NewValue.Value = (OldValue.Value & (EX_PUSH_LOCK_MULTIPLE_SHARED |
@ -656,10 +691,10 @@ ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock)
#if DBG #if DBG
/* Setup the Debug Wait Block */ /* Setup the Debug Wait Block */
WaitBlock.Signaled = 0; WaitBlock->Signaled = 0;
WaitBlock.OldValue = OldValue; WaitBlock->OldValue = OldValue;
WaitBlock.NewValue = NewValue; WaitBlock->NewValue = NewValue;
WaitBlock.PushLock = PushLock; WaitBlock->PushLock = PushLock;
#endif #endif
/* Write the new value */ /* Write the new value */
@ -683,26 +718,26 @@ ExfAcquirePushLockShared(PEX_PUSH_LOCK PushLock)
} }
/* Set up the Wait Gate */ /* Set up the Wait Gate */
KeInitializeGate(&WaitBlock.WakeGate); KeInitializeGate(&WaitBlock->WakeGate);
/* Now spin on the push lock if necessary */ /* Now spin on the push lock if necessary */
i = ExPushLockSpinCount; i = ExPushLockSpinCount;
if ((i) && (WaitBlock.Flags & EX_PUSH_LOCK_WAITING)) if ((i) && (WaitBlock->Flags & EX_PUSH_LOCK_WAITING))
{ {
/* Spin */ /* Spin */
while (--i) YieldProcessor(); while (--i) YieldProcessor();
} }
/* Now try to remove the wait bit */ /* Now try to remove the wait bit */
if (InterlockedBitTestAndReset(&WaitBlock.Flags, 1)) if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1))
{ {
/* Fast-path did not work, we need to do a full wait */ /* Fast-path did not work, we need to do a full wait */
KeWaitForGate(&WaitBlock.WakeGate, WrPushLock, KernelMode); KeWaitForGate(&WaitBlock->WakeGate, WrPushLock, KernelMode);
ASSERT(WaitBlock.Signaled); ASSERT(WaitBlock->Signaled);
} }
/* We shouldn't be shared anymore */ /* We shouldn't be shared anymore */
ASSERT((WaitBlock.ShareCount == 0)); ASSERT((WaitBlock->ShareCount == 0));
} }
} }
} }
@ -1004,7 +1039,7 @@ VOID
FASTCALL FASTCALL
ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock) ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock)
{ {
EX_PUSH_LOCK NewValue; EX_PUSH_LOCK NewValue, WakeValue;
EX_PUSH_LOCK OldValue = *PushLock; EX_PUSH_LOCK OldValue = *PushLock;
/* Loop until we can change */ /* Loop until we can change */
@ -1024,7 +1059,8 @@ ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock)
/* Sanity check */ /* Sanity check */
ASSERT(NewValue.Waking && !NewValue.Locked); ASSERT(NewValue.Waking && !NewValue.Locked);
/* Write the New Value */ /* Write the New Value. Save our original value for waking */
WakeValue = NewValue;
NewValue.Ptr = InterlockedCompareExchangePointer(PushLock, NewValue.Ptr = InterlockedCompareExchangePointer(PushLock,
NewValue.Ptr, NewValue.Ptr,
OldValue.Ptr); OldValue.Ptr);
@ -1032,14 +1068,10 @@ ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock)
/* Check if the value changed behind our back */ /* Check if the value changed behind our back */
if (NewValue.Value != OldValue.Value) if (NewValue.Value != OldValue.Value)
{ {
/* Loop again */ /* Wake the Pushlock */
OldValue = NewValue; ExfWakePushLock(PushLock, WakeValue);
continue; break;
} }
/* Wake the Pushlock */
ExfWakePushLock(PushLock, NewValue);
break;
} }
else else
{ {
@ -1056,10 +1088,10 @@ ExfReleasePushLockExclusive(PEX_PUSH_LOCK PushLock)
/* Check if the value changed behind our back */ /* Check if the value changed behind our back */
if (NewValue.Value == OldValue.Value) break; if (NewValue.Value == OldValue.Value) break;
/* Loop again */
OldValue = NewValue;
} }
/* Loop again */
OldValue = NewValue;
} }
} }
@ -1128,7 +1160,7 @@ ExfUnblockPushLock(PEX_PUSH_LOCK PushLock,
KIRQL OldIrql = DISPATCH_LEVEL; KIRQL OldIrql = DISPATCH_LEVEL;
/* Get the wait block and erase the previous one */ /* Get the wait block and erase the previous one */
WaitBlock = InterlockedExchangePointer(PushLock->Ptr, 0); WaitBlock = InterlockedExchangePointer(&PushLock->Ptr, NULL);
if (WaitBlock) if (WaitBlock)
{ {
/* Check if there is a linked pushlock and raise IRQL appropriately */ /* Check if there is a linked pushlock and raise IRQL appropriately */
@ -1144,7 +1176,7 @@ ExfUnblockPushLock(PEX_PUSH_LOCK PushLock,
if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1)) if (InterlockedBitTestAndReset(&WaitBlock->Flags, 1))
{ {
/* Nobody removed the flag before us, so signal the event */ /* Nobody removed the flag before us, so signal the event */
KeSetEventBoostPriority(&WaitBlock->WakeEvent, IO_NO_INCREMENT); KeSetEventBoostPriority(&WaitBlock->WakeEvent, NULL);
} }
/* Check if there was a next block */ /* Check if there was a next block */
@ -1161,6 +1193,6 @@ ExfUnblockPushLock(PEX_PUSH_LOCK PushLock,
EX_PUSH_LOCK_FLAGS_WAIT)) EX_PUSH_LOCK_FLAGS_WAIT))
{ {
/* Wait for the pushlock to be unblocked */ /* Wait for the pushlock to be unblocked */
ExTimedWaitForUnblockPushLock(PushLock, CurrentWaitBlock, NULL); ExWaitForUnblockPushLock(PushLock, CurrentWaitBlock);
} }
} }

View file

@ -68,6 +68,36 @@ typedef struct
#define ExRundownCompleted _ExRundownCompleted #define ExRundownCompleted _ExRundownCompleted
#define ExGetPreviousMode KeGetPreviousMode #define ExGetPreviousMode KeGetPreviousMode
//
// Detect GCC 4.1.2+
//
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40102
//
// Broken GCC with Alignment Bug. We'll do alignment ourselves at higher cost.
//
#define DEFINE_WAIT_BLOCK(x) \
struct _AlignHack \
{ \
UCHAR Hack[15]; \
EX_PUSH_LOCK_WAIT_BLOCK UnalignedBlock; \
} WaitBlockBuffer; \
PEX_PUSH_LOCK_WAIT_BLOCK x = (PEX_PUSH_LOCK_WAIT_BLOCK) \
((ULONG_PTR)&WaitBlockBuffer.UnalignedBlock &~ 0xF);
#else
//
// This is only for compatibility; the compiler will optimize the extra
// local variable (the actual pointer) away, so we don't take any perf hit
// by doing this.
//
#define DEFINE_WAIT_BLOCK(x) \
EX_PUSH_LOCK_WAIT_BLOCK WaitBlockBuffer; \
PEX_PUSH_LOCK_WAIT_BLOCK x = &WaitBlockBuffer;
#endif
/* INITIALIZATION FUNCTIONS *************************************************/ /* INITIALIZATION FUNCTIONS *************************************************/
VOID VOID
@ -596,6 +626,23 @@ _ExRundownCompleted(IN PEX_RUNDOWN_REF RunRef)
/* PUSHLOCKS *****************************************************************/ /* PUSHLOCKS *****************************************************************/
/* FIXME: VERIFY THESE! */
VOID
FASTCALL
ExBlockPushLock(PEX_PUSH_LOCK PushLock,
PVOID WaitBlock);
VOID
FASTCALL
ExfUnblockPushLock(PEX_PUSH_LOCK PushLock,
PVOID CurrentWaitBlock);
VOID
FASTCALL
ExWaitForUnblockPushLock(IN PEX_PUSH_LOCK PushLock,
IN PEX_PUSH_LOCK_WAIT_BLOCK WaitBlock);
/*++ /*++
* @name ExInitializePushLock * @name ExInitializePushLock
* INTERNAL MACRO * INTERNAL MACRO
@ -751,12 +798,16 @@ VOID
FORCEINLINE FORCEINLINE
ExWaitOnPushLock(PEX_PUSH_LOCK PushLock) ExWaitOnPushLock(PEX_PUSH_LOCK PushLock)
{ {
/* Acquire the lock */ /* Check if we're locked */
ExfAcquirePushLockExclusive(PushLock); if (PushLock->Locked)
ASSERT(PushLock->Locked); {
/* Acquire the lock */
ExfAcquirePushLockExclusive(PushLock);
ASSERT(PushLock->Locked);
/* Release it */ /* Release it */
ExfReleasePushLockExclusive(PushLock); ExfReleasePushLockExclusive(PushLock);
}
} }
/*++ /*++