- Fix KiAcquireMutexContended, it was broken in contended cases.

- Inline Acquiring/Leaving guarded regions and the gmutex code, instead of going through 3-4 indirect calls.
- Add a mountain of ASSERTs to detect incorrect usage/state.
- Set ->SpecialApcDisable in the Guarded Mutex.
- Fix broken KTHREAD definition. SpecialApcDisable and KernelApcDisable were incorrectly marked as USHORT instead of SHORT, which could cause severe trouble under optimized builds (At least under MSVC, since MSVC wouldn't allow a negative number in an unsigned short).
- Use GM_LOCK_BIT_V.
- Fix broken KeTryToAcquireGuardedMutex prototype.
- Fix broken KGUARDED_MUTEX typedef and add bit values.
- Fix broken Interlocked* prototypes in regards to volatileness.

svn path=/trunk/; revision=23037
This commit is contained in:
Alex Ionescu 2006-07-13 04:20:18 +00:00
parent b71f24ddc1
commit 5ecb728b0e
7 changed files with 325 additions and 177 deletions

View file

@ -1167,20 +1167,27 @@ typedef struct _KGATE
DISPATCHER_HEADER Header;
} KGATE, *PKGATE, *RESTRICTED_POINTER PRKGATE;
#define GM_LOCK_BIT 0x1
#define GM_LOCK_BIT_V 0x0
#define GM_LOCK_WAITER_WOKEN 0x2
#define GM_LOCK_WAITER_INC 0x4
typedef struct _KGUARDED_MUTEX
{
LONG Count;
struct _KTHREAD* Owner;
volatile LONG Count;
PKTHREAD Owner;
ULONG Contention;
KGATE Gate;
union {
struct {
union
{
struct
{
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};
ULONG CombinedApcDisable;
};
} KGUARDED_MUTEX, *PKGUARDED_MUTEX, *RESTRICTED_POINTER PRKGUARDED_MUTEX;
} KGUARDED_MUTEX, *PKGUARDED_MUTEX;
typedef struct _KTIMER {
DISPATCHER_HEADER Header;
@ -5195,19 +5202,19 @@ NTOSAPI
LONG
DDKFASTAPI
InterlockedIncrement(
IN PLONG VOLATILE Addend);
IN OUT LONG volatile *Addend);
NTOSAPI
LONG
DDKFASTAPI
InterlockedDecrement(
IN PLONG VOLATILE Addend);
IN OUT LONG volatile *Addend);
NTOSAPI
LONG
DDKFASTAPI
InterlockedCompareExchange(
IN OUT PLONG VOLATILE Destination,
IN OUT LONG volatile *Destination,
IN LONG Exchange,
IN LONG Comparand);
@ -5215,14 +5222,14 @@ NTOSAPI
LONG
DDKFASTAPI
InterlockedExchange(
IN OUT PLONG VOLATILE Target,
IN OUT LONG volatile *Destination,
IN LONG Value);
NTOSAPI
LONG
DDKFASTAPI
InterlockedExchangeAdd(
IN OUT PLONG VOLATILE Addend,
IN OUT LONG volatile *Addend,
IN LONG Value);
/*
@ -6315,48 +6322,52 @@ RtlxUnicodeStringToAnsiSize(
/* Guarded Mutex routines */
VOID
VOID
FASTCALL
KeAcquireGuardedMutex(
PKGUARDED_MUTEX GuardedMutex
IN OUT PKGUARDED_MUTEX GuardedMutex
);
VOID
FASTCALL
KeAcquireGuardedMutexUnsafe(
PKGUARDED_MUTEX GuardedMutex
IN OUT PKGUARDED_MUTEX GuardedMutex
);
VOID
STDCALL
KeEnterGuardedRegion(VOID);
VOID
STDCALL
KeLeaveGuardedRegion(VOID);
NTAPI
KeEnterGuardedRegion(
VOID
);
VOID
VOID
NTAPI
KeLeaveGuardedRegion(
VOID
);
VOID
FASTCALL
KeInitializeGuardedMutex(
PKGUARDED_MUTEX GuardedMutex
OUT PKGUARDED_MUTEX GuardedMutex
);
VOID
VOID
FASTCALL
KeReleaseGuardedMutexUnsafe(
PKGUARDED_MUTEX GuardedMutex
IN OUT PKGUARDED_MUTEX GuardedMutex
);
VOID
VOID
FASTCALL
KeReleaseGuardedMutex(
PKGUARDED_MUTEX GuardedMutex
IN OUT PKGUARDED_MUTEX GuardedMutex
);
BOOL
BOOLEAN
FASTCALL
KeTryToAcquireGuardedMutex(
PKGUARDED_MUTEX GuardedMutex
IN OUT PKGUARDED_MUTEX GuardedMutex
);
/* Fast Mutex */

View file

@ -695,8 +695,8 @@ typedef struct _KTHREAD
{
struct
{
USHORT KernelApcDisable;
USHORT SpecialApcDisable;
SHORT KernelApcDisable;
SHORT SpecialApcDisable;
};
ULONG CombinedApcDisable;
};

View file

@ -3785,7 +3785,7 @@ static __inline PVOID GetFiberData(void)
#if defined(__GNUC__)
static __inline__ BOOLEAN
InterlockedBitTestAndSet(IN LONG *Base,
InterlockedBitTestAndSet(IN LONG volatile *Base,
IN LONG Bit)
{
LONG OldBit;
@ -3800,7 +3800,7 @@ InterlockedBitTestAndSet(IN LONG *Base,
}
static __inline__ BOOLEAN
InterlockedBitTestAndReset(IN LONG *Base,
InterlockedBitTestAndReset(IN LONG volatile *Base,
IN LONG Bit)
{
LONG OldBit;

View file

@ -709,4 +709,6 @@ KeV86Exception(
ULONG address
);
#include "ke_x.h"
#endif /* __NTOSKRNL_INCLUDE_INTERNAL_KE_H */

View file

@ -0,0 +1,59 @@
/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/include/ke_x.h
* PURPOSE: Internal Inlined Functions for the Kernel
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
//
// Guarded Region Routines
//
#define KeEnterGuardedRegion() \
{ \
PKTHREAD Thread = KeGetCurrentThread(); \
\
/* Sanity checks */ \
ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL); \
ASSERT(Thread == KeGetCurrentThread()); \
ASSERT((Thread->SpecialApcDisable <= 0) && \
(Thread->SpecialApcDisable != -32768)); \
\
/* Disable Special APCs */ \
Thread->SpecialApcDisable--; \
}
#define KeLeaveGuardedRegion() \
{ \
PKTHREAD Thread = KeGetCurrentThread(); \
\
/* Sanity checks */ \
ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL); \
ASSERT(Thread == KeGetCurrentThread()); \
ASSERT(Thread->SpecialApcDisable < 0); \
\
/* Leave region and check if APCs are OK now */ \
if (!(++Thread->SpecialApcDisable)) \
{ \
/* Check for Kernel APCs on the list */ \
if (!IsListEmpty(&Thread->ApcState. \
ApcListHead[KernelMode])) \
{ \
/* Check for APC Delivery */ \
KiCheckForKernelApcDelivery(); \
} \
} \
}
//
// TODO: Guarded Mutex Routines
//
//
// TODO: Critical Region Routines
//
//
// TODO: Wait Routines
//

View file

@ -1,10 +1,9 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/gate.c
* PURPOSE: Implements the Gate Dispatcher Object
*
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES *****************************************************************/
@ -18,10 +17,8 @@
VOID
FASTCALL
KeInitializeGate(PKGATE Gate)
KeInitializeGate(IN PKGATE Gate)
{
DPRINT("KeInitializeGate(Gate %x)\n", Gate);
/* Initialize the Dispatcher Header */
KeInitializeDispatcherHeader(&Gate->Header,
GateObject,
@ -98,7 +95,7 @@ KeWaitForGate(IN PKGATE Gate,
VOID
FASTCALL
KeSignalGateBoostPriority(PKGATE Gate)
KeSignalGateBoostPriority(IN PKGATE Gate)
{
PKTHREAD WaitThread;
PKWAIT_BLOCK WaitBlock;

View file

@ -1,72 +1,139 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Kernel
* FILE: ntoskrnl/ke/gmutex.c
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/gate.c
* PURPOSE: Implements Guarded Mutex
*
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net) and
* Filip Navara (xnavara@volny.cz)
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
* Filip Navara (navaraf@reactos.org)
*/
/* INCLUDES *****************************************************************/
/* INCLUDES ******************************************************************/
#define NTDDI_VERSION NTDDI_WS03SP1
#include <ntoskrnl.h>
#define NDEBUG
#include <internal/debug.h>
UCHAR
/* PRIVATE FUNCTIONS *********************************************************/
VOID
FASTCALL
InterlockedClearBit(PLONG Destination,
LONG Bit);
typedef enum _KGUARDED_MUTEX_BITS
KiAcquireGuardedMutexContented(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
GM_LOCK_BIT = 1,
GM_LOCK_WAITER_WOKEN = 2,
GM_LOCK_WAITER_INC = 4
} KGUARDED_MUTEX_BITS;
ULONG BitsToRemove, BitsToAdd;
LONG OldValue, NewValue;
/* FUNCTIONS *****************************************************************/
/* Increase the contention count */
GuardedMutex->Contention++;
/**
* @name KeEnterGuardedRegion
*
* Enters a guarded region. This causes all (incl. special kernel) APCs
* to be disabled.
*/
VOID
STDCALL
KeEnterGuardedRegion(VOID)
{
/* Disable Special APCs */
KeGetCurrentThread()->SpecialApcDisable--;
}
/* Start by unlocking the Guarded Mutex */
BitsToRemove = GM_LOCK_BIT;
BitsToAdd = GM_LOCK_WAITER_INC;
/**
* @name KeLeaveGuardedRegion
*
* Leaves a guarded region and delivers pending APCs if possible.
*/
VOID
STDCALL
KeLeaveGuardedRegion(VOID)
{
PKTHREAD Thread = KeGetCurrentThread();
/* Get the Count Bits */
OldValue = GuardedMutex->Count;
/* Boost the enable count and check if Special APCs are enabled */
if (++Thread->SpecialApcDisable == 0)
/* Start change loop */
for (;;)
{
/* Check if there are Kernel APCs on the list */
if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
/* Loop sanity checks */
ASSERT((BitsToRemove == GM_LOCK_BIT) ||
(BitsToRemove == (GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN)));
ASSERT((BitsToAdd == GM_LOCK_WAITER_INC) ||
(BitsToAdd == GM_LOCK_WAITER_WOKEN));
/* Check if the Guarded Mutex is locked */
if (OldValue & GM_LOCK_BIT)
{
/* Check for APC Delivery */
KiCheckForKernelApcDelivery();
/* Sanity check */
ASSERT((BitsToRemove == GM_LOCK_BIT) ||
((OldValue & GM_LOCK_WAITER_WOKEN) != 0));
/* Unlock it by removing the Lock Bit */
NewValue = InterlockedCompareExchange(&GuardedMutex->Count,
OldValue ^ BitsToRemove,
OldValue);
if (NewValue == OldValue) break;
/* Value got changed behind our backs, start over */
OldValue = NewValue;
}
else
{
/* The Guarded Mutex isn't locked, so simply set the bits */
NewValue = InterlockedCompareExchange(&GuardedMutex->Count,
OldValue + BitsToAdd,
OldValue);
if (NewValue != OldValue)
{
/* Value got changed behind our backs, start over */
OldValue = NewValue;
continue;
}
/* Now we have to wait for it */
KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode);
ASSERT((GuardedMutex->Count & GM_LOCK_WAITER_WOKEN) != 0);
/* Ok, the wait is done, so set the new bits */
BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN;
BitsToAdd = GM_LOCK_WAITER_WOKEN;
}
}
}
VOID
FORCEINLINE
FASTCALL
KeInitializeGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
KiAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
BOOLEAN OldBit;
/* Remove the lock */
OldBit = InterlockedBitTestAndReset(&GuardedMutex->Count, GM_LOCK_BIT_V);
if (!OldBit)
{
/* The Guarded Mutex was already locked, enter contented case */
KiAcquireGuardedMutexContented(GuardedMutex);
}
}
VOID
FORCEINLINE
FASTCALL
KiReleaseGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
LONG OldValue;
/* Destroy the Owner */
GuardedMutex->Owner = NULL;
/* Add the Lock Bit */
OldValue = InterlockedExchangeAdd(&GuardedMutex->Count, 1);
ASSERT((OldValue & GM_LOCK_BIT) == 0);
/* Check if it was already locked, but not woken */
if ((OldValue) && !(OldValue & GM_LOCK_WAITER_WOKEN))
{
/* Update the Oldvalue to what it should be now */
OldValue |= GM_LOCK_BIT;
/* Remove the Woken bit */
if (InterlockedCompareExchange(&GuardedMutex->Count,
OldValue - GM_LOCK_WAITER_WOKEN,
OldValue) == OldValue)
{
/* Signal the Gate */
KeSignalGateBoostPriority(&GuardedMutex->Gate);
}
}
}
/* PUBLIC FUNCTIONS **********************************************************/
VOID
FASTCALL
KeInitializeGuardedMutex(OUT PKGUARDED_MUTEX GuardedMutex)
{
/* Setup the Initial Data */
GuardedMutex->Count = GM_LOCK_BIT;
@ -79,143 +146,155 @@ KeInitializeGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
VOID
FASTCALL
KiAcquireGuardedMutexContented(PKGUARDED_MUTEX GuardedMutex)
KeAcquireGuardedMutexUnsafe(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
ULONG BitsToRemove;
ULONG BitsToAdd;
LONG OldValue;
PKTHREAD Thread = KeGetCurrentThread();
/* Increase the contention count */
InterlockedIncrement((PLONG)&GuardedMutex->Contention);
/* Sanity checks */
ASSERT((KeGetCurrentIrql() == APC_LEVEL) ||
(Thread->SpecialApcDisable < 0) ||
(Thread->Teb == NULL) ||
(Thread->Teb >= (PTEB)MM_SYSTEM_RANGE_START));
ASSERT(GuardedMutex->Owner != Thread);
/* Start by unlocking the Guarded Mutex */
BitsToRemove = GM_LOCK_BIT;
BitsToAdd = GM_LOCK_WAITER_INC;
while (1)
{
/* Get the Count Bits */
OldValue = (volatile LONG)GuardedMutex->Count;
/* Check if the Guarded Mutex is locked */
if (OldValue & GM_LOCK_BIT)
{
/* Unlock it by removing the Lock Bit */
if (InterlockedCompareExchange(&GuardedMutex->Count,
OldValue &~ BitsToRemove,
OldValue) == OldValue)
{
/* The Guarded Mutex is now unlocked */
break;
}
}
else
{
/* The Guarded Mutex isn't locked, so simply set the bits */
if (InterlockedCompareExchange(&GuardedMutex->Count,
OldValue | BitsToAdd,
OldValue) != OldValue)
{
/* The Guarded Mutex value changed behind our back, start over */
continue;
}
/* Now we have to wait for it */
KeWaitForGate(&GuardedMutex->Gate, WrGuardedMutex, KernelMode);
/* Ok, the wait is done, so set the new bits */
BitsToRemove = GM_LOCK_BIT | GM_LOCK_WAITER_WOKEN;
BitsToAdd = GM_LOCK_WAITER_WOKEN;
}
}
}
VOID
FASTCALL
KeAcquireGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
{
/* Remove the lock */
if (!InterlockedBitTestAndReset(&GuardedMutex->Count, 0))
{
/* The Guarded Mutex was already locked, enter contented case */
KiAcquireGuardedMutexContented(GuardedMutex);
}
/* Do the actual acquire */
KiAcquireGuardedMutex(GuardedMutex);
/* Set the Owner */
GuardedMutex->Owner = KeGetCurrentThread();
GuardedMutex->Owner = Thread;
}
VOID
FASTCALL
KeReleaseGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
KeReleaseGuardedMutexUnsafe(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
LONG OldValue;
/* Sanity checks */
ASSERT((KeGetCurrentIrql() == APC_LEVEL) ||
(KeGetCurrentThread()->SpecialApcDisable < 0) ||
(KeGetCurrentThread()->Teb == NULL) ||
(KeGetCurrentThread()->Teb >= (PTEB)MM_SYSTEM_RANGE_START));
ASSERT(GuardedMutex->Owner == KeGetCurrentThread());
/* Destroy the Owner */
GuardedMutex->Owner = NULL;
/* Add the Lock Bit */
OldValue = InterlockedExchangeAdd(&GuardedMutex->Count, 1);
/* Check if it was already locked, but not woken */
if (OldValue && !(OldValue & GM_LOCK_WAITER_WOKEN))
{
/* Update the Oldvalue to what it should be now */
OldValue |= GM_LOCK_BIT;
/* Remove the Woken bit */
if (InterlockedCompareExchange(&GuardedMutex->Count,
OldValue &~ GM_LOCK_WAITER_WOKEN,
OldValue) == OldValue)
{
/* Signal the Gate */
KeSignalGateBoostPriority(&GuardedMutex->Gate);
}
}
/* Release the mutex */
KiReleaseGuardedMutex(GuardedMutex);
}
VOID
FASTCALL
KeAcquireGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
KeAcquireGuardedMutex(IN PKGUARDED_MUTEX GuardedMutex)
{
PKTHREAD Thread = KeGetCurrentThread();
/* Sanity checks */
ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL);
ASSERT(GuardedMutex->Owner != Thread);
/* Disable Special APCs */
KeEnterGuardedRegion();
/* Do the Unsafe Acquire */
KeAcquireGuardedMutexUnsafe(GuardedMutex);
/* Do the actual acquire */
KiAcquireGuardedMutex(GuardedMutex);
/* Set the Owner and Special APC Disable state */
GuardedMutex->Owner = Thread;
GuardedMutex->SpecialApcDisable = Thread->SpecialApcDisable;
}
VOID
FASTCALL
KeReleaseGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
KeReleaseGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
/* Do the actual release */
KeReleaseGuardedMutexUnsafe(GuardedMutex);
/* Sanity checks */
ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL);
ASSERT(GuardedMutex->Owner == KeGetCurrentThread());
ASSERT(GuardedMutex->SpecialApcDisable ==
KeGetCurrentThread()->SpecialApcDisable);
/* Release the mutex */
KiReleaseGuardedMutex(GuardedMutex);
/* Re-enable APCs */
KeLeaveGuardedRegion();
}
BOOL
BOOLEAN
FASTCALL
KeTryToAcquireGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
KeTryToAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
PKTHREAD Thread = KeGetCurrentThread();
BOOLEAN OldBit;
/* Block APCs */
KeEnterGuardedRegion();
/* Remove the lock */
if (InterlockedBitTestAndReset(&GuardedMutex->Count, 0))
OldBit = InterlockedBitTestAndReset(&GuardedMutex->Count, GM_LOCK_BIT_V);
if (OldBit)
{
/* Re-enable APCs */
KeLeaveGuardedRegion();
YieldProcessor();
/* Return failure */
return FALSE;
}
/* Set the Owner */
GuardedMutex->Owner = KeGetCurrentThread();
/* Set the Owner and APC State */
GuardedMutex->Owner = Thread;
GuardedMutex->SpecialApcDisable = Thread->SpecialApcDisable;
return TRUE;
}
/**
* @name KeEnterGuardedRegion
*
* Enters a guarded region. This causes all (incl. special kernel) APCs
* to be disabled.
*/
#undef KeEnterGuardedRegion
VOID
NTAPI
KeEnterGuardedRegion(VOID)
{
PKTHREAD Thread = KeGetCurrentThread();
/* Sanity checks */
ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL);
ASSERT(Thread == KeGetCurrentThread());
ASSERT((Thread->SpecialApcDisable <= 0) &&
(Thread->SpecialApcDisable != -32768));
/* Disable Special APCs */
Thread->SpecialApcDisable--;
}
/**
* @name KeLeaveGuardedRegion
*
* Leaves a guarded region and delivers pending APCs if possible.
*/
#undef KeLeaveGuardedRegion
VOID
NTAPI
KeLeaveGuardedRegion(VOID)
{
PKTHREAD Thread = KeGetCurrentThread();
/* Sanity checks */
ASSERT_IRQL_LESS_OR_EQUAL(APC_LEVEL);
ASSERT(Thread == KeGetCurrentThread());
ASSERT(Thread->SpecialApcDisable < 0);
/* Boost the enable count and check if Special APCs are enabled */
if (!(++Thread->SpecialApcDisable))
{
/* Check if there are Kernel APCs on the list */
if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* Check for APC Delivery */
KiCheckForKernelApcDelivery();
}
}
}
/* EOF */