From 5ecb728b0e2cd179674e2b12f62ea3647262eeb3 Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Thu, 13 Jul 2006 04:20:18 +0000 Subject: [PATCH] - 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 --- reactos/include/ddk/winddk.h | 65 +++-- reactos/include/ndk/ketypes.h | 4 +- reactos/include/psdk/winnt.h | 4 +- reactos/ntoskrnl/include/internal/ke.h | 2 + reactos/ntoskrnl/include/internal/ke_x.h | 59 ++++ reactos/ntoskrnl/ke/gate.c | 11 +- reactos/ntoskrnl/ke/gmutex.c | 357 ++++++++++++++--------- 7 files changed, 325 insertions(+), 177 deletions(-) create mode 100644 reactos/ntoskrnl/include/internal/ke_x.h diff --git a/reactos/include/ddk/winddk.h b/reactos/include/ddk/winddk.h index 750396dea33..0586737c55a 100644 --- a/reactos/include/ddk/winddk.h +++ b/reactos/include/ddk/winddk.h @@ -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 */ diff --git a/reactos/include/ndk/ketypes.h b/reactos/include/ndk/ketypes.h index a32400e29b4..cff284123d4 100644 --- a/reactos/include/ndk/ketypes.h +++ b/reactos/include/ndk/ketypes.h @@ -695,8 +695,8 @@ typedef struct _KTHREAD { struct { - USHORT KernelApcDisable; - USHORT SpecialApcDisable; + SHORT KernelApcDisable; + SHORT SpecialApcDisable; }; ULONG CombinedApcDisable; }; diff --git a/reactos/include/psdk/winnt.h b/reactos/include/psdk/winnt.h index bdabef7763e..49149f08e3e 100644 --- a/reactos/include/psdk/winnt.h +++ b/reactos/include/psdk/winnt.h @@ -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; diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index b45333f78df..2799f04457c 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -709,4 +709,6 @@ KeV86Exception( ULONG address ); +#include "ke_x.h" + #endif /* __NTOSKRNL_INCLUDE_INTERNAL_KE_H */ diff --git a/reactos/ntoskrnl/include/internal/ke_x.h b/reactos/ntoskrnl/include/internal/ke_x.h new file mode 100644 index 00000000000..ae4c78b9205 --- /dev/null +++ b/reactos/ntoskrnl/include/internal/ke_x.h @@ -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 +// + diff --git a/reactos/ntoskrnl/ke/gate.c b/reactos/ntoskrnl/ke/gate.c index 30bbc580fd0..e691cf7d56b 100644 --- a/reactos/ntoskrnl/ke/gate.c +++ b/reactos/ntoskrnl/ke/gate.c @@ -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; diff --git a/reactos/ntoskrnl/ke/gmutex.c b/reactos/ntoskrnl/ke/gmutex.c index 25f1eb58bd0..19d11cf7dbd 100644 --- a/reactos/ntoskrnl/ke/gmutex.c +++ b/reactos/ntoskrnl/ke/gmutex.c @@ -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 +#define NDEBUG #include -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 */