/*
- * 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
-FASTCALL
-InterlockedClearBit(PLONG Destination,
- LONG Bit);
-
-typedef enum _KGUARDED_MUTEX_BITS
-{
- GM_LOCK_BIT = 1,
- GM_LOCK_WAITER_WOKEN = 2,
- GM_LOCK_WAITER_INC = 4
-} KGUARDED_MUTEX_BITS;
-
-/* FUNCTIONS *****************************************************************/
-
-/**
- * @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--;
-}
-
-/**
- * @name KeLeaveGuardedRegion
- *
- * Leaves a guarded region and delivers pending APCs if possible.
- */
-VOID
-STDCALL
-KeLeaveGuardedRegion(VOID)
-{
- PKTHREAD Thread = KeGetCurrentThread();
-
- /* Boost the enable count and check if Special APCs are enabled */
- if (++Thread->SpecialApcDisable == 0)
- {
- /* Check if there are Kernel APCs on the list */
- if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
- {
- /* Check for APC Delivery */
- KiCheckForKernelApcDelivery();
- }
- }
-}
-
-VOID
-FASTCALL
-KeInitializeGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
-{
- /* Setup the Initial Data */
- GuardedMutex->Count = GM_LOCK_BIT;
- GuardedMutex->Owner = NULL;
- GuardedMutex->Contention = 0;
-
- /* Initialize the Wait Gate */
- KeInitializeGate(&GuardedMutex->Gate);
-}
+/* PRIVATE FUNCTIONS *********************************************************/
VOID
FASTCALL
-KiAcquireGuardedMutexContented(PKGUARDED_MUTEX GuardedMutex)
+KiAcquireGuardedMutexContented(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
- ULONG BitsToRemove;
- ULONG BitsToAdd;
- LONG OldValue;
+ ULONG BitsToRemove, BitsToAdd;
+ LONG OldValue, NewValue;
/* Increase the contention count */
- InterlockedIncrement((PLONG)&GuardedMutex->Contention);
+ GuardedMutex->Contention++;
/* Start by unlocking the Guarded Mutex */
BitsToRemove = GM_LOCK_BIT;
BitsToAdd = GM_LOCK_WAITER_INC;
- while (1)
+ /* Get the Count Bits */
+ OldValue = GuardedMutex->Count;
+
+ /* Start change loop */
+ for (;;)
{
- /* Get the Count Bits */
- OldValue = (volatile LONG)GuardedMutex->Count;
+ /* 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)
{
+ /* Sanity check */
+ ASSERT((BitsToRemove == GM_LOCK_BIT) ||
+ ((OldValue & GM_LOCK_WAITER_WOKEN) != 0));
+
/* Unlock it by removing the Lock Bit */
- if (InterlockedCompareExchange(&GuardedMutex->Count,
- OldValue &~ BitsToRemove,
- OldValue) == OldValue)
- {
- /* The Guarded Mutex is now unlocked */
- break;
- }
+ NewValue = InterlockedCompareExchange((PLONG)&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 */
- if (InterlockedCompareExchange(&GuardedMutex->Count,
- OldValue | BitsToAdd,
- OldValue) != OldValue)
+ NewValue = InterlockedCompareExchange((PLONG)&GuardedMutex->Count,
+ OldValue + BitsToAdd,
+ OldValue);
+ if (NewValue != OldValue)
{
- /* The Guarded Mutex value changed behind our back, start over */
+ /* 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;
}
VOID
+FORCEINLINE
FASTCALL
-KeAcquireGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
+KiAcquireGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
+ BOOLEAN OldBit;
+
/* Remove the lock */
- if (!InterlockedBitTestAndReset(&GuardedMutex->Count, 0))
+ OldBit = InterlockedBitTestAndReset(&GuardedMutex->Count, GM_LOCK_BIT_V);
+ if (!OldBit)
{
/* The Guarded Mutex was already locked, enter contented case */
KiAcquireGuardedMutexContented(GuardedMutex);
}
-
- /* Set the Owner */
- GuardedMutex->Owner = KeGetCurrentThread();
}
VOID
+FORCEINLINE
FASTCALL
-KeReleaseGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
+KiReleaseGuardedMutex(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
LONG OldValue;
GuardedMutex->Owner = NULL;
/* Add the Lock Bit */
- OldValue = InterlockedExchangeAdd(&GuardedMutex->Count, 1);
+ OldValue = InterlockedExchangeAdd((PLONG)&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))
+ 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,
+ if (InterlockedCompareExchange((PLONG)&GuardedMutex->Count,
+ OldValue - GM_LOCK_WAITER_WOKEN,
OldValue) == OldValue)
{
/* Signal the Gate */
}
}
+/* PUBLIC FUNCTIONS **********************************************************/
+
+VOID
+FASTCALL
+KeInitializeGuardedMutex(OUT PKGUARDED_MUTEX GuardedMutex)
+{
+ /* Setup the Initial Data */
+ GuardedMutex->Count = GM_LOCK_BIT;
+ GuardedMutex->Owner = NULL;
+ GuardedMutex->Contention = 0;
+
+ /* Initialize the Wait Gate */
+ KeInitializeGate(&GuardedMutex->Gate);
+}
+
VOID
FASTCALL
-KeAcquireGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
+KeAcquireGuardedMutexUnsafe(IN OUT PKGUARDED_MUTEX GuardedMutex)
{
+ PKTHREAD Thread = KeGetCurrentThread();
+
+ /* Sanity checks */
+ ASSERT((KeGetCurrentIrql() == APC_LEVEL) ||
+ (Thread->SpecialApcDisable < 0) ||
+ (Thread->Teb == NULL) ||
+ (Thread->Teb >= (PTEB)MM_SYSTEM_RANGE_START));
+ ASSERT(GuardedMutex->Owner != Thread);
+
+ /* Do the actual acquire */
+ KiAcquireGuardedMutex(GuardedMutex);
+
+ /* Set the Owner */
+ GuardedMutex->Owner = Thread;
+}
+
+VOID
+FASTCALL
+KeReleaseGuardedMutexUnsafe(IN OUT PKGUARDED_MUTEX GuardedMutex)
+{
+ /* Sanity checks */
+ ASSERT((KeGetCurrentIrql() == APC_LEVEL) ||
+ (KeGetCurrentThread()->SpecialApcDisable < 0) ||
+ (KeGetCurrentThread()->Teb == NULL) ||
+ (KeGetCurrentThread()->Teb >= (PTEB)MM_SYSTEM_RANGE_START));
+ ASSERT(GuardedMutex->Owner == KeGetCurrentThread());
+
+ /* Release the mutex */
+ KiReleaseGuardedMutex(GuardedMutex);
+}
+
+VOID
+FASTCALL
+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 */