Big merge: thanks alex and greatlord. Not a complete merge but most
[reactos.git] / reactos / ntoskrnl / ke / gmutex.c
index 25f1eb5..44466ec 100644 (file)
 /*
- * 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;
@@ -131,23 +83,25 @@ KiAcquireGuardedMutexContented(PKGUARDED_MUTEX GuardedMutex)
 }
 
 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;
 
@@ -155,17 +109,18 @@ KeReleaseGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
     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 */
@@ -174,48 +129,172 @@ KeReleaseGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
     }
 }
 
+/* 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 */