Implemented Guarded Mutex, a drop-in replacement for Fast Mutex (the correct one...
authorAlex Ionescu <aionescu@gmail.com>
Fri, 15 Apr 2005 06:24:35 +0000 (06:24 +0000)
committerAlex Ionescu <aionescu@gmail.com>
Fri, 15 Apr 2005 06:24:35 +0000 (06:24 +0000)
svn path=/trunk/; revision=14625

18 files changed:
reactos/drivers/storage/diskdump/diskdump.c
reactos/hal/hal/hal.c
reactos/hal/halx86/generic/fmutex.c
reactos/hal/halx86/generic/irql.c
reactos/include/ddk/kedef.h
reactos/include/ddk/kefuncs.h
reactos/include/ddk/ketypes.h
reactos/ntoskrnl/Makefile
reactos/ntoskrnl/ex/i386/interlck.c
reactos/ntoskrnl/include/internal/i386/ke.h
reactos/ntoskrnl/include/internal/i386/ps.h
reactos/ntoskrnl/include/internal/ke.h
reactos/ntoskrnl/include/internal/ps.h
reactos/ntoskrnl/ke/apc.c
reactos/ntoskrnl/ke/gmutex.c
reactos/ntoskrnl/ntoskrnl.def
reactos/ntoskrnl/ps/thread.c
reactos/w32api/include/ddk/winddk.h

index 927af75..29c578c 100644 (file)
@@ -41,6 +41,7 @@
 #undef VERSION
 #define VERSION  "0.0.1"
 
+#undef KeGetCurrentIrql
 /* PROTOTYPES ***************************************************************/
 
 NTSTATUS STDCALL
index 1eae032..a28d5c9 100644 (file)
@@ -748,7 +748,7 @@ KeFlushWriteBuffer(VOID)
   UNIMPLEMENTED;
 }
 
-
+#undef KeGetCurrentIrql
 KIRQL
 STDCALL 
 KeGetCurrentIrql(VOID)
index b31e6b8..fc6f61f 100644 (file)
@@ -18,6 +18,8 @@
 
 /* FUNCTIONS *****************************************************************/
 
+#undef KeEnterCriticalRegion
+#undef KeLeaveCriticalRegion
 VOID FASTCALL
 ExAcquireFastMutex (PFAST_MUTEX        FastMutex)
 {
index b9a3ae1..bca26a9 100644 (file)
@@ -65,6 +65,7 @@ KiInterruptDispatch2 (ULONG Irq, KIRQL old_level);
 
 /* FUNCTIONS ****************************************************************/
 
+#undef KeGetCurrentIrql
 KIRQL STDCALL KeGetCurrentIrql (VOID)
 /*
  * PURPOSE: Returns the current irq level
index c1212f8..36be542 100644 (file)
@@ -56,7 +56,7 @@ typedef enum _KWAIT_REASON
    WrPageOut,
    WrRendezvous,
    Spare2,
-   Spare3,
+   WrGuardedMutex,
    Spare4,
    Spare5,
    Spare6,
index 2559e46..dfc2659 100644 (file)
@@ -809,4 +809,48 @@ KeRaiseUserException(
        IN NTSTATUS     ExceptionCode
 );
 
+VOID 
+FASTCALL
+KeAcquireGuardedMutex(
+    PKGUARDED_MUTEX GuardedMutex
+);
+
+VOID
+FASTCALL
+KeAcquireGuardedMutexUnsafe(
+    PKGUARDED_MUTEX GuardedMutex
+);
+
+VOID 
+STDCALL
+KeEnterGuardedRegion(VOID);
+
+VOID
+STDCALL
+KeLeaveGuardedRegion(VOID);
+
+VOID 
+FASTCALL
+KeInitializeGuardedMutex(
+    PKGUARDED_MUTEX GuardedMutex
+);
+
+VOID 
+FASTCALL
+KeReleaseGuardedMutexUnsafe(
+    PKGUARDED_MUTEX GuardedMutex
+);
+
+VOID 
+FASTCALL
+KeReleaseGuardedMutex(
+    PKGUARDED_MUTEX GuardedMutex
+);
+
+BOOL 
+FASTCALL
+KeTryToAcquireGuardedMutex(
+    PKGUARDED_MUTEX GuardedMutex
+);
+
 #endif /* __INCLUDE_DDK_KEFUNCS_H */
index 5b7fa57..952932a 100644 (file)
@@ -180,6 +180,21 @@ typedef struct _KMUTEX
    UCHAR ApcDisable;
 } KMUTEX, *PKMUTEX, KMUTANT, *PKMUTANT;
 
+typedef struct _KGUARDED_MUTEX
+{
+    LONG Count;
+    struct _KTHREAD* Owner;
+    ULONG Contention;
+    KGATE Gate;
+    union {
+        struct {
+            SHORT KernelApcDisable;
+            SHORT SpecialApcDisable;
+        };
+        ULONG CombinedApcDisable;
+    };
+} KGUARDED_MUTEX, *PKGUARDED_MUTEX;
+
 #include <pshpack1.h>
 
 typedef struct _KSEMAPHORE
index 59134f1..89051b3 100644 (file)
@@ -100,6 +100,8 @@ OBJECTS_KE = \
        ke/dpc.o \
        ke/device.o \
        ke/event.o \
+       ke/gate.o \
+       ke/gmutex.o \
        ke/kqueue.o \
        ke/kthread.o \
        ke/ipi.o \
index 12a3c2d..adf4ed9 100644 (file)
@@ -396,6 +396,44 @@ InterlockedExchangeAdd(PLONG Addend,
 #error Unknown compiler for inline assembler
 #endif
 
+/**********************************************************************
+ * FASTCALL: @InterlockedClearBit@8
+ * STDCALL: _InterlockedClearBit@8
+ */
+#if defined(__GNUC__)
+/*
+ * @implemented
+ */
+UCHAR
+FASTCALL
+InterlockedClearBit(PLONG Destination,
+                    LONG Bit);
+
+__asm__("\n\t.global @InterlockedClearBit@8\n\t"
+       "@InterlockedClearBit@8:\n\t"
+       LOCK
+       "btr %edx,(%ecx)\n\t"
+       "setc %al\n\t"
+       "ret $8\n\t");
+
+#elif defined(_MSC_VER)
+/*
+ * @implemented
+ */
+__declspec(naked)
+UCHAR
+FASTCALL
+InterlockedClearBit(PUCHAR Destination,
+                    UCHAR Bit)
+{
+       __asm LOCK btr [ecx], edx
+       __asm setc al
+       __asm ret
+}
+
+#else
+#error Unknown compiler for inline assembler
+#endif
 
 /**********************************************************************
  * FASTCALL: @InterlockedCompareExchange@12
index 5efe824..e8bc96b 100644 (file)
@@ -205,6 +205,7 @@ NtEarlyInitVdm(VOID);
 #define LOCK ""
 #endif
 
+#define KeGetCurrentIrql(X) (((PKPCR)KPCR_BASE)->Irql)
 
 #if defined(__GNUC__)
 #define Ke386DisableInterrupts() __asm__("cli\n\t");
index 6eb1c41..6d86116 100644 (file)
@@ -292,6 +292,7 @@ static inline PKPRCB KeGetCurrentPrcb(VOID)
 
 #define KeGetCurrentKPCR(X) ((PKPCR)KPCR_BASE)
 #define KeGetCurrentPrcb() (((PKPCR)KPCR_BASE)->Prcb)
+#define KeGetCurrentThread(X) (((PKPCR)KPCR_BASE)->PrcbData.CurrentThread)
 
 #endif
 
index 305d46f..043d3f8 100644 (file)
@@ -49,6 +49,26 @@ struct _KEXCEPTION_FRAME;
 #define IPI_REQUEST_DPC                    2
 #define IPI_REQUEST_FREEZE         3
 
+/* MACROS *************************************************************************/
+
+#define KeEnterCriticalRegion(X) \
+{ \
+    PKTHREAD _Thread = KeGetCurrentThread(); \
+    if (_Thread) _Thread->KernelApcDisable--; \
+}
+    
+#define KeLeaveCriticalRegion(X) \
+{ \
+    PKTHREAD _Thread = KeGetCurrentThread(); \
+    if((_Thread) && (++_Thread->KernelApcDisable == 0)) \
+    { \
+        if (!IsListEmpty(&_Thread->ApcState.ApcListHead[KernelMode])) \
+        { \
+            KiKernelApcDeliveryCheck(); \
+        } \
+    } \
+}
+
 /* threadsch.c ********************************************************************/
 
 /* Thread Scheduler Functions */
@@ -77,6 +97,28 @@ STDCALL
 KiUnblockThread(PKTHREAD Thread, 
                 PNTSTATUS WaitStatus, 
                 KPRIORITY Increment);
+       
+/* gmutex.c ********************************************************************/
+
+VOID
+FASTCALL
+KiAcquireGuardedMutexContented(PKGUARDED_MUTEX GuardedMutex);
+
+/* gate.c **********************************************************************/
+         
+VOID 
+FASTCALL
+KeInitializeGate(PKGATE Gate);
+
+VOID
+FASTCALL
+KeSignalGateBoostPriority(PKGATE Gate);
+
+VOID
+FASTCALL
+KeWaitForGate(PKGATE Gate,
+              KWAIT_REASON WaitReason,
+              KPROCESSOR_MODE WaitMode);
 
 /* ipi.c ********************************************************************/
 
@@ -237,7 +279,9 @@ VOID KeContextToTrapFrame(PCONTEXT Context, PKTRAP_FRAME TrapFrame);
 VOID STDCALL KiDeliverApc(KPROCESSOR_MODE PreviousMode,
                   PVOID Reserved,
                   PKTRAP_FRAME TrapFrame);
-
+VOID
+STDCALL
+KiKernelApcDeliveryCheck(VOID);
 LONG 
 STDCALL 
 KiInsertQueue(IN PKQUEUE Queue, 
index 7cbf16f..eee710b 100644 (file)
@@ -97,7 +97,13 @@ typedef struct _KTHREAD
    CHAR              Quantum;             /* 6B */
    KWAIT_BLOCK       WaitBlock[4];        /* 6C */
    PVOID             LegoData;            /* CC */
-   ULONG             KernelApcDisable;    /* D0 */
+   union {
+          struct {
+              USHORT KernelApcDisable;
+              USHORT SpecialApcDisable;
+          };
+          ULONG      CombinedApcDisable;  /* D0 */
+   };
    KAFFINITY         UserAffinity;        /* D4 */
    UCHAR             SystemAffinityActive;/* D8 */
    UCHAR             PowerState;          /* D9 */
index b2f0626..f2fa397 100644 (file)
 
 /* FUNCTIONS *****************************************************************/
 
+/*++
+ * KiKernelApcDeliveryCheck 
+ * @implemented NT 5.2
+ *
+ *     The KiKernelApcDeliveryCheck routine is called whenever APCs have just
+ *     been re-enabled in Kernel Mode, such as after leaving a Critical or
+ *     Guarded Region. It delivers APCs if the environment is right.
+ *
+ * Params:
+ *     None.
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     This routine allows KeLeave/EnterCritical/GuardedRegion to be used as a
+ *     macro from inside WIN32K or other Drivers, which will then only have to
+ *     do an Import API call in the case where APCs are enabled again.
+ *
+ *--*/
+VOID
+STDCALL
+KiKernelApcDeliveryCheck(VOID)
+{
+    /* We should only deliver at passive */
+    if (KeGetCurrentIrql() == PASSIVE_LEVEL)
+    {
+        /* Raise to APC and Deliver APCs, then lower back to Passive */
+        KfRaiseIrql(APC_LEVEL);
+        KiDeliverApc(KernelMode, 0, 0);
+        KfLowerIrql(PASSIVE_LEVEL);
+    }
+    else
+    {
+        /*
+         * If we're not at passive level it means someone raised IRQL
+         * to APC level before the a critical or guarded section was entered
+         * (e.g) by a fast mutex). This implies that the APCs shouldn't
+         * be delivered now, but after the IRQL is lowered to passive
+         * level again.
+         */
+        HalRequestSoftwareInterrupt(APC_LEVEL);
+    }
+}
+
 /*++
  * KeEnterCriticalRegion 
  * @implemented NT4
@@ -37,6 +82,7 @@
  *     Callers of KeEnterCriticalRegion must be running at IRQL <= APC_LEVEL.
  *
  *--*/
+#undef KeEnterCriticalRegion
 VOID 
 STDCALL 
 KeEnterCriticalRegion(VOID)
@@ -46,6 +92,45 @@ KeEnterCriticalRegion(VOID)
     if (Thread) Thread->KernelApcDisable--;
 }
 
+/*++
+ * KeLeaveCriticalRegion 
+ * @implemented NT4
+ *
+ *     The KeLeaveCriticalRegion routine reenables the delivery of normal 
+ *     kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
+ *
+ * Params:
+ *     None.
+ *
+ * Returns:
+ *     None.
+ *
+ * Remarks:
+ *     Highest-level drivers can call this routine while running in the context
+ *     of the thread that requested the current I/O operation. 
+ *
+ *     Callers of KeLeaveCriticalRegion must be running at IRQL <= DISPATCH_LEVEL.
+ *
+ *--*/
+#undef KeLeaveCriticalRegion
+VOID 
+STDCALL 
+KeLeaveCriticalRegion (VOID)
+{
+    PKTHREAD Thread = KeGetCurrentThread(); 
+
+    /* Check if Kernel APCs are now enabled */
+    if((Thread) && (++Thread->KernelApcDisable == 0)) 
+    { 
+        /* Check if we need to request an APC Delivery */
+        if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) 
+        { 
+            /* Check for the right environment */
+            KiKernelApcDeliveryCheck();
+        } 
+    } 
+}
+
 /*++
  * KeInitializeApc 
  * @implemented NT4
@@ -325,45 +410,6 @@ KeInsertQueueApc(PKAPC Apc,
     return Inserted;
 }
 
-/*++
- * KeLeaveCriticalRegion 
- * @implemented NT4
- *
- *     The KeLeaveCriticalRegion routine reenables the delivery of normal 
- *     kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
- *
- * Params:
- *     None.
- *
- * Returns:
- *     None.
- *
- * Remarks:
- *     Highest-level drivers can call this routine while running in the context
- *     of the thread that requested the current I/O operation. 
- *
- *     Callers of KeLeaveCriticalRegion must be running at IRQL <= DISPATCH_LEVEL.
- *
- *--*/
-VOID 
-STDCALL 
-KeLeaveCriticalRegion (VOID)
-{
-    PKTHREAD Thread = KeGetCurrentThread(); 
-
-    /* Check if Kernel APCs are now enabled */
-    if((Thread) && (++Thread->KernelApcDisable == 0)) { 
-        
-        /* Check if we need to request an APC Delivery */
-        if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) { 
-            
-            /* Set APC Pending */
-            Thread->ApcState.KernelApcPending = TRUE; 
-            HalRequestSoftwareInterrupt(APC_LEVEL); 
-        } 
-    } 
-}
-
 /*++
  * KeRemoveQueueApc 
  *
index 35d1d43..62130ce 100644 (file)
@@ -1,11 +1,11 @@
-/* $Id$
- *
+/*
  * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         ReactOS kernel
+ * PROJECT:         ReactOS Kernel
  * FILE:            ntoskrnl/ke/gmutex.c
- * PURPOSE:         Implements guarded mutex (w2k3+/64)
+ * PURPOSE:         Implements Guarded Mutex
  * 
- * PROGRAMMERS:      No programmer listed.
+ * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net) and 
+ *                  Filip Navara (xnavara@volny.cz)
  */
 
 /* INCLUDES *****************************************************************/
 #include <ntoskrnl.h>
 #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 *****************************************************************/
 
-/*
-KeAcquireGuardedMutex
-KeAcquireGuardedMutexUnsafe
-KeEnterGuardedRegion
-KeInitializeGuardedMutex
-KeReleaseGuardedMutexUnsafe
-KeTryToAcquireGuardedMutex
-KeReleaseGuardedMutex
-KeLeaveGuardedRegion
-*/
+/**
+ * @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 */
+            KiKernelApcDeliveryCheck();
+        }
+    }
+}
+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);
+}
+
+VOID
+FASTCALL
+KiAcquireGuardedMutexContented(PKGUARDED_MUTEX GuardedMutex)
+{
+    ULONG BitsToRemove;
+    ULONG BitsToAdd;
+    LONG OldValue;
+    
+    /* Increase the contention count */
+    InterlockedIncrement(&GuardedMutex->Contention);
+    
+    /* 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
+KeAcquireGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
+{
+    /* Disable Special APCs */
+    KeEnterGuardedRegion();
+
+    /* Do the Unsafe Acquire */    
+    KeAcquireGuardedMutexUnsafe(GuardedMutex);
+}
+
+VOID
+FASTCALL
+KeAcquireGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
+{
+    /* Remove the lock */
+    if (!InterlockedClearBit(&GuardedMutex->Count, 0))
+    {
+        /* The Guarded Mutex was already locked, enter contented case */
+        KiAcquireGuardedMutexContented(GuardedMutex);
+    }
+    
+    /* Set the Owner */
+    GuardedMutex->Owner = KeGetCurrentThread();
+}
+
+VOID 
+FASTCALL
+KeReleaseGuardedMutexUnsafe(PKGUARDED_MUTEX GuardedMutex)
+{
+    LONG OldValue;
+    
+    /* 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);
+        }
+    }
+}
+
+VOID 
+FASTCALL
+KeReleaseGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
+{
+    /* Do the actual release */
+    KeReleaseGuardedMutexUnsafe(GuardedMutex);
+    
+    /* Re-enable APCs */
+    KeLeaveGuardedRegion();
+}
+
+BOOL 
+FASTCALL
+KeTryToAcquireGuardedMutex(PKGUARDED_MUTEX GuardedMutex)
+{
+    /* Block APCs */
+    KeEnterGuardedRegion();
+    
+    /* Remove the lock */
+    if (InterlockedClearBit(&GuardedMutex->Count, 0))
+    {
+        /* Re-enable APCs */
+        KeLeaveGuardedRegion();
+        
+        /* Return failure */
+        return FALSE;
+    }
+    
+    /* Set the Owner */
+    GuardedMutex->Owner = KeGetCurrentThread();
+    return TRUE;
+}
 
 /* EOF */
index f7159de..dc939e4 100644 (file)
@@ -649,6 +649,7 @@ KeWaitForSingleObject@20
 ;KiBugCheckData DATA
 KiCoprocessorError@0
 KiDeliverApc@12
+KiKernelApcDeliveryCheck@0
 KiDispatchInterrupt@0
 KiEnableTimerWatchdog
 KiInterruptDispatch2@8
index 952ff73..b5583c7 100644 (file)
@@ -44,10 +44,15 @@ static GENERIC_MAPPING PiThreadMapping = {STANDARD_RIGHTS_READ | THREAD_GET_CONT
 
 /* FUNCTIONS ***************************************************************/
 
+#ifdef KeGetCurrentThread
+#undef KeGetCurrentThread
+#endif
 /*
  * @implemented
  */
-PKTHREAD STDCALL KeGetCurrentThread(VOID)
+PKTHREAD 
+STDCALL 
+KeGetCurrentThread(VOID)
 {
 #ifdef CONFIG_SMP
    ULONG Flags;
index e37c645..5e4af89 100644 (file)
@@ -929,6 +929,21 @@ typedef struct _FAST_MUTEX {
   ULONG  OldIrql;
 } FAST_MUTEX, *PFAST_MUTEX;
 
+typedef struct _KGUARDED_MUTEX
+{
+    LONG Count;
+    struct _KTHREAD* Owner;
+    ULONG Contention;
+    struct _KGATE* Gate;
+    union {
+        struct {
+            SHORT KernelApcDisable;
+            SHORT SpecialApcDisable;
+        };
+        ULONG CombinedApcDisable;
+    };
+} KGUARDED_MUTEX, *PKGUARDED_MUTEX, *RESTRICTED_POINTER PRKGUARDED_MUTEX;
+
 typedef struct _KTIMER {
   DISPATCHER_HEADER  Header;
   ULARGE_INTEGER  DueTime;