From 300da88affc7658ffc89cf95484c4bd4f7955999 Mon Sep 17 00:00:00 2001 From: Alex Ionescu Date: Thu, 5 Jan 2006 16:24:32 +0000 Subject: [PATCH] - New ERESOURCE implementation: fixes the return value of some functions (VOID vs NTSTATUS, USHORT vs ULONG), as well as optimized the code loops and general structure of the code. Additionnaly, functions do not simply call other functions with similar names; the exact implementation of each function has now been properly separated (see the DDK for more information on this) to have the most optimized scenarios. - Also, the spinlock is not actually acquired on non-SMP builds; instead, interrupts are blocked and unblocked for acquire/release, this optimizes locking. - Added many asserts and bugcheck scenarios. - Added thread priority boosting. - Added some debugging helpers and deadlock detection. - Added RESOURCE_NOT_OWNED bugcehck message. * Thanks again to Waxdragon (Andrew) for testing this build. svn path=/trunk/; revision=20580 --- reactos/include/ndk/extypes.h | 5 + reactos/ntoskrnl/ex/init.c | 5 +- reactos/ntoskrnl/ex/resource.c | 3150 ++++++++++++++++------- reactos/ntoskrnl/include/internal/ex.h | 15 + reactos/ntoskrnl/include/internal/tag.h | 6 +- reactos/ntoskrnl/ntoskrnl.mc | 26 +- reactos/w32api/include/ddk/winddk.h | 4 +- 7 files changed, 2301 insertions(+), 910 deletions(-) diff --git a/reactos/include/ndk/extypes.h b/reactos/include/ndk/extypes.h index 64557d4817b..bd7c6f17946 100644 --- a/reactos/include/ndk/extypes.h +++ b/reactos/include/ndk/extypes.h @@ -117,6 +117,11 @@ extern ULONG NTSYSAPI NtBuildNumber; #define EX_PUSH_LOCK_FLAGS_EXCLUSIVE 1 #define EX_PUSH_LOCK_FLAGS_WAIT 2 +// +// Resource (ERESOURCE) Flags +// +#define ResourceHasDisabledPriorityBoost 0x08 + // // Shutdown types for NtShutdownSystem // diff --git a/reactos/ntoskrnl/ex/init.c b/reactos/ntoskrnl/ex/init.c index 2847aaf9b56..82df1cf0f6d 100644 --- a/reactos/ntoskrnl/ex/init.c +++ b/reactos/ntoskrnl/ex/init.c @@ -489,7 +489,7 @@ ExpLoadInitialProcess(PHANDLE ProcessHandle, DPRINT("Process created successfully\n"); return STATUS_SUCCESS; } - + VOID INIT_FUNCTION STDCALL @@ -543,6 +543,9 @@ ExpInitializeExecutive(VOID) InitializeListHead(&KiProfileSourceListHead); KeInitializeSpinLock(&KiProfileLock); + /* Initialize resources */ + ExpResourceInitialization(); + /* Load basic Security for other Managers */ if (!SeInit1()) KEBUGCHECK(SECURITY_INITIALIZATION_FAILED); diff --git a/reactos/ntoskrnl/ex/resource.c b/reactos/ntoskrnl/ex/resource.c index 1d673261680..2b87a4b39fa 100644 --- a/reactos/ntoskrnl/ex/resource.c +++ b/reactos/ntoskrnl/ex/resource.c @@ -1,38 +1,20 @@ -/* $Id$ - * - * COPYRIGHT: See COPYING in the top level directory - * PROJECT: ReactOS kernel - * FILE: ntoskrnl/ex/resource.c - * PURPOSE: Resource synchronization construct - * - * PROGRAMMERS: No programmer listed. - */ - - /* - * Usage of ERESOURCE members is not documented. - * From names of members and functionnalities, we can assume : - * - * OwnerTable = list of threads who have shared access(if more than one) - * ActiveCount = number of threads who have access to the resource - * Flag = bits : ResourceOwnedExclusive=0x80 - * ResourceNeverExclusive=0x10 - * ResourceReleaseByOtherThread=0x20 - * ResourceDisableBoost=0x08 - * SharedWaiters = semaphore, used to manage wait list of shared waiters. - * ExclusiveWaiters = event, used to manage wait list of exclusive waiters. - * OwnerThreads[0]= thread who have exclusive access - * OwnerThreads[1]= if only one thread own the resource - * thread who have shared access - * else - * OwnerThread=0 - * and TableSize = number of entries in the owner table - * NumberOfExclusiveWaiters = number of threads waiting for exclusive access. - * NumberOfSharedWaiters = number of threads waiting for exclusive access. - * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Kernel + * FILE: ntoskrnl/ex/resource.c + * PURPOSE: ERESOURCE Implementation + * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) */ -#define ResourceDisableBoost 0x08 +/* WARNING: + * This implementation is the Windows NT 5.x one. + * NT 6.0 beta has optimized the OwnerThread entry array + * and the internals of ExpFindEntryForThread and ExpFindFreeEntry + * need to be modified accordingly in order to support the WDK. + * These changes will not be made here until NT 6.0 reaches RTM status since + * there is a possibility that they will be removed; as such, do NOT + * update ERESOURCE/relevant code to the WDK definition. + */ /* INCLUDES *****************************************************************/ @@ -40,916 +22,2294 @@ #define NDEBUG #include -/* FUNCTIONS *****************************************************************/ - - -BOOLEAN -STDCALL -ExTryToAcquireResourceExclusiveLite ( - PERESOURCE Resource - ) -/* - * FUNCTION: Attempts to require the resource for exclusive access - * ARGUMENTS: - * Resource = Points to the resource of be acquired - * RETURNS: TRUE if the resource was acquired for the caller - * NOTES: Must be acquired at IRQL < DISPATCH_LEVEL - */ -{ - return(ExAcquireResourceExclusiveLite(Resource,FALSE)); -} - -#ifdef ExAcquireResourceExclusive -#undef ExAcquireResourceExclusive +#if defined (ALLOC_PRAGMA) +#pragma alloc_text(INIT, ExpResourceInitialization) #endif -/* - * @implemented - */ -BOOLEAN -STDCALL -ExAcquireResourceExclusive ( - PERESOURCE Resource, - BOOLEAN Wait - ) +/* Macros for reading resource flags */ +#define IsExclusiveWaiting(r) (r->NumberOfExclusiveWaiters) +#define IsSharedWaiting(r) (r->NumberOfSharedWaiters) +#define IsOwnedExclusive(r) (r->Flag & ResourceOwnedExclusive) + +/* DATA***********************************************************************/ + +LARGE_INTEGER ExpTimeout; +ULONG ExpResourceTimeoutCount = 90 * 3600 / 2; +KSPIN_LOCK ExpResourceSpinLock; +LIST_ENTRY ExpSystemResourcesList; +BOOLEAN ExResourceStrict = FALSE; /* FIXME */ + +/* PRIVATE FUNCTIONS *********************************************************/ + +#if DBG +/*++ + * @name ExpVerifyResource + * + * The ExpVerifyResource routine verifies the correctness of an ERESOURCE + * + * @param Resource + * Pointer to the resource being verified. + * + * @return None. + * + * @remarks Only present on DBG builds. + * + *--*/ +VOID +NTAPI +ExpVerifyResource(IN PERESOURCE Resource) { - return(ExAcquireResourceExclusiveLite(Resource,Wait)); + /* Verify the resource data */ + ASSERT((((ULONG_PTR)Resource) & (sizeof(ULONG_PTR) - 1)) == 0); + ASSERT(!Resource->SharedWaiters || + Resource->SharedWaiters->Header.Type == SemaphoreObject); + ASSERT(!Resource->SharedWaiters || + Resource->SharedWaiters->Header.Size == (sizeof(KSEMAPHORE) / sizeof(ULONG))); + ASSERT(!Resource->ExclusiveWaiters || + Resource->ExclusiveWaiters->Header.Type == SynchronizationEvent); + ASSERT(!Resource->ExclusiveWaiters || + Resource->ExclusiveWaiters->Header.Size == (sizeof(KEVENT) / sizeof(ULONG))); } - -/* - * @implemented - */ -BOOLEAN -STDCALL -ExAcquireResourceExclusiveLite ( - PERESOURCE Resource, - BOOLEAN Wait - ) -/* - * FUNCTION: Acquires a resource exclusively for the calling thread - * ARGUMENTS: - * Resource = Points to the resource to acquire - * Wait = Is set to TRUE if the caller should wait to acquire the - * resource if it can't be acquired immediately - * RETURNS: TRUE if the resource was acquired, - * FALSE otherwise - * NOTES: Must be called at IRQL < DISPATCH_LEVEL - */ +/*++ + * @name ExpCheckForApcsDisabled + * + * The ExpCheckForApcsDisabled routine checks if Kernel APCs are still + * enabled when they should be disabled, and optionally breakpoints. + * + * @param BreakIfTrue + * Specifies if we should break if an invalid APC State is detected. + * + * @param Resource + * Pointer to the resource being checked. + * + * @param Thread + * Pointer to the thread being checked. + * + * @return None. + * + * @remarks Only present on DBG builds. Depends on ExResourceStrict value. + * + *--*/ +VOID +NTAPI +ExpCheckForApcsDisabled(IN BOOLEAN BreakIfTrue, + IN PERESOURCE Resource, + IN PETHREAD Thread) { - KIRQL oldIrql; - - DPRINT("ExAcquireResourceExclusiveLite(Resource 0x%p, Wait %d)\n", - Resource, Wait); - - ASSERT_IRQL_LESS(DISPATCH_LEVEL); - -/* undefed for now, since cdfs must be fixed first */ -#if 0 - /* At least regular kmode APC's must be disabled - * Note that this requirement is missing in old DDK's */ - ASSERT(KeGetCurrentThread() == NULL || /* <-Early in the boot process the current thread is obseved to be NULL */ - KeGetCurrentThread()->KernelApcDisable || - KeGetCurrentIrql() == APC_LEVEL); + /* Check if we should care and check if we should break */ + if ((ExResourceStrict) && + (BreakIfTrue) && + !(Thread->SystemThread) && + !(Thread->Tcb.CombinedApcDisable)) + { + /* Bad! */ + DPRINT1("EX: resource: APCs still enabled before resource %p acquire " + "!!!\n", Resource); + DbgBreakPoint(); + } +} #endif - KeAcquireSpinLock(&Resource->SpinLock, &oldIrql); - - /* resource already locked */ - if((Resource->Flag & ResourceOwnedExclusive) - && Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread()) - { - /* it's ok : same lock for same thread */ - Resource->OwnerThreads[0].OwnerCount++; - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n"); - return(TRUE); - } - - if (Resource->ActiveCount && !Wait) - { - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n"); - return(FALSE); - } - - /* - * This is slightly better than it looks because other exclusive - * threads who are waiting won't be woken up but there is a race - * with new threads trying to grab the resource so we must have - * the spinlock, still normally this loop will only be executed - * once - * NOTE: We might want to set a timeout to detect deadlock - * (10 minutes?) - */ - while (Resource->ActiveCount) - { - Resource->NumberOfExclusiveWaiters++; - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - KeWaitForSingleObject(Resource->ExclusiveWaiters, - Executive, - KernelMode, - FALSE, - NULL); - KeAcquireSpinLock(&Resource->SpinLock, &oldIrql); - Resource->NumberOfExclusiveWaiters--; - } - Resource->Flag |= ResourceOwnedExclusive; - Resource->ActiveCount = 1; - Resource->OwnerThreads[0].OwnerThread = ExGetCurrentResourceThread(); - Resource->OwnerThreads[0].OwnerCount = 1; - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n"); - return(TRUE); -} - -static BOOLEAN EiRemoveSharedOwner(PERESOURCE Resource, - ERESOURCE_THREAD ResourceThreadId) -/* - * FUNCTION: Removes the current thread from the shared owners of the resource - * ARGUMENTS: - * Resource = Pointer to the resource for which the thread is to be - * added - * NOTE: Must be called with the resource spinlock held - */ -{ - ULONG i; - - if (Resource->OwnerThreads[1].OwnerThread == ResourceThreadId) - { - Resource->OwnerThreads[1].OwnerCount--; - if (Resource->OwnerThreads[1].OwnerCount == 0) - { - Resource->ActiveCount--; - Resource->OwnerThreads[1].OwnerThread = 0; - } - return(TRUE); - } - - if (Resource->OwnerThreads[1].OwnerThread) - { - /* Oh dear, the caller didn't own the resource after all */ - return(FALSE); - } - - for (i=0; iOwnerThreads[1].TableSize; i++) - { - if (Resource->OwnerTable[i].OwnerThread == ResourceThreadId) - { - Resource->OwnerTable[i].OwnerCount--; - if (Resource->OwnerTable[i].OwnerCount == 0) - { - Resource->ActiveCount--; - Resource->OwnerTable[i].OwnerThread = 0; - } - return TRUE; - } - } - return(FALSE); -} - -static BOOLEAN EiAddSharedOwner(PERESOURCE Resource) -/* - * FUNCTION: Adds the current thread to the shared owners of the resource - * ARGUMENTS: - * Resource = Pointer to the resource for which the thread is to be - * added - * NOTE: Must be called with the resource spinlock held - */ -{ - ERESOURCE_THREAD CurrentThread = ExGetCurrentResourceThread(); - POWNER_ENTRY freeEntry; - ULONG i = 0; - - DPRINT("EiAddSharedOwner(Resource 0x%p)\n", Resource); - - if (Resource->ActiveCount == 0) - { - /* no owner, it's easy */ - Resource->OwnerThreads[1].OwnerThread = ExGetCurrentResourceThread(); - Resource->OwnerThreads[1].OwnerCount = 1; - if (Resource->OwnerTable != NULL) - { - ExFreePool(Resource->OwnerTable); - } - Resource->OwnerTable = NULL; - Resource->ActiveCount = 1; - DPRINT("EiAddSharedOwner() = TRUE\n"); - return(TRUE); - } - - /* - * now, we must search if this thread has already acquired this resource - * then increase ownercount if found, else create new entry or reuse free - * entry - */ - if (Resource->OwnerTable == NULL) - { - DPRINT("Creating owner table\n"); - - /* allocate ownertable,memset to 0, initialize first entry */ - Resource->OwnerTable = - ExAllocatePoolWithTag(NonPagedPool, sizeof(OWNER_ENTRY)*3, - TAG_OWNER_TABLE); - if (Resource->OwnerTable == NULL) - { - KEBUGCHECK(0); - return(FALSE); - } - memset(Resource->OwnerTable,0,sizeof(OWNER_ENTRY)*3); - memcpy(&Resource->OwnerTable[0], &Resource->OwnerThreads[1], - sizeof(OWNER_ENTRY)); - - Resource->OwnerThreads[1].OwnerThread = 0; - Resource->OwnerThreads[1].TableSize = 3; - - Resource->OwnerTable[1].OwnerThread = CurrentThread; - Resource->OwnerTable[1].OwnerCount = 1; - Resource->ActiveCount++; - - return(TRUE); - } - - DPRINT("Search free entries\n"); - - DPRINT("Number of entries %d\n", - Resource->OwnerThreads[1].TableSize); - - freeEntry = NULL; - for (i=0; iOwnerThreads[1].TableSize; i++) - { - if (Resource->OwnerTable[i].OwnerThread == CurrentThread) - { - DPRINT("Thread already owns resource\n"); - Resource->OwnerTable[i].OwnerCount++; - return(TRUE); - } - if (Resource->OwnerTable[i].OwnerThread == 0) - { - freeEntry = &Resource->OwnerTable[i]; - break; - } - } - - DPRINT("Found free entry 0x%p\n", freeEntry); - - if (!freeEntry) - { - DPRINT("Allocating new entry\n"); - - /* reallocate ownertable with one more entry */ - freeEntry = - ExAllocatePoolWithTag(NonPagedPool, - sizeof(OWNER_ENTRY)* - (Resource->OwnerThreads[1].TableSize+1), - TAG_OWNER_TABLE); - if (freeEntry == NULL) - { - KEBUGCHECK(0); - return(FALSE); - } - memcpy(freeEntry,Resource->OwnerTable, - sizeof(OWNER_ENTRY)*(Resource->OwnerThreads[1].TableSize)); - ExFreePool(Resource->OwnerTable); - Resource->OwnerTable=freeEntry; - freeEntry=&Resource->OwnerTable[Resource->OwnerThreads[1].TableSize]; - Resource->OwnerThreads[1].TableSize++; - } - DPRINT("Creating entry\n"); - freeEntry->OwnerThread=ExGetCurrentResourceThread(); - freeEntry->OwnerCount=1; - Resource->ActiveCount++; - return(TRUE); -} - -/* - * @implemented - */ -BOOLEAN -STDCALL -ExAcquireResourceSharedLite ( - PERESOURCE Resource, - BOOLEAN Wait - ) -/* - * FUNCTION: Acquires the given resource for shared access by the calling - * thread - * ARGUMENTS: - * Resource = Points to the resource to acquire - * Wait = Is set to TRUE if the caller should be put into wait state - * until the resource can be acquired if it cannot be acquired - * immediately - * RETURNS: TRUE, if the resource is acquire - * FALSE otherwise - */ -{ - KIRQL oldIrql; - - DPRINT("ExAcquireResourceSharedLite(Resource 0x%p, Wait %d)\n", - Resource, Wait); - - ASSERT_IRQL_LESS(DISPATCH_LEVEL); - -/* undefed for now, since cdfs must be fixed first */ -#if 0 - /* At least regular kmode APC's must be disabled - * Note that this requirement is missing in old DDK's - */ - ASSERT(KeGetCurrentThread() == NULL || /* <-Early in the boot process the current thread is obseved to be NULL */ - KeGetCurrentThread()->KernelApcDisable || - KeGetCurrentIrql() == APC_LEVEL); -#endif - - KeAcquireSpinLock(&Resource->SpinLock, &oldIrql); - - /* first, resolve trivial cases */ - if (Resource->ActiveCount == 0) - { - EiAddSharedOwner(Resource); - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireResourceSharedLite() = TRUE\n"); - return(TRUE); - } - - if ((Resource->Flag & ResourceOwnedExclusive) - && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread()) - { - /* exclusive, but by same thread : it's ok */ - /* - * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared - */ - Resource->OwnerThreads[0].OwnerCount++; - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireResourceSharedLite() = TRUE\n"); - return(TRUE); - } - - if ((Resource->Flag & ResourceOwnedExclusive) - || Resource->NumberOfExclusiveWaiters) - { - /* exclusive by another thread , or thread waiting for exclusive */ - if (!Wait) - { - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireResourceSharedLite() = FALSE\n"); - return(FALSE); - } - else - { - Resource->NumberOfSharedWaiters++; - do - { - /* wait for the semaphore */ - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - KeWaitForSingleObject(Resource->SharedWaiters,0, KernelMode, FALSE, NULL); - KeAcquireSpinLock(&Resource->SpinLock, &oldIrql); - /* the spin lock was released we must check again */ - } - while ((Resource->Flag & ResourceOwnedExclusive) - || Resource->NumberOfExclusiveWaiters); - Resource->NumberOfSharedWaiters--; - } - } - - EiAddSharedOwner(Resource); - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireResourceSharedLite() = TRUE\n"); - return(TRUE); -} - -/* - * @implemented - */ +/*++ + * @name ExpResourceInitialization + * + * The ExpResourceInitialization routine initializes resources for use. + * + * @param None. + * + * @return None. + * + * @remarks This routine should only be called once, during system startup. + * + *--*/ VOID -STDCALL -ExConvertExclusiveToSharedLite ( - PERESOURCE Resource - ) -/* - * FUNCTION: Converts a given resource from acquired for exclusive access - * to acquire for shared access - * ARGUMENTS: - * Resource = Points to the resource for which the access should be - * converted - * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL - */ +NTAPI +INIT_FUNCTION +ExpResourceInitialization(VOID) { - ULONG oldWaiters; - KIRQL oldIrql; - - DPRINT("ExConvertExclusiveToSharedLite(Resource 0x%p)\n", Resource); - - KeAcquireSpinLock(&Resource->SpinLock, &oldIrql); - - oldWaiters = Resource->NumberOfSharedWaiters; - - if (!(Resource->Flag & ResourceOwnedExclusive)) - { - /* Might not be what the caller expects, better bug check */ - KEBUGCHECK(0); - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - return; - } - - //transfer infos from entry 0 to entry 1 and erase entry 0 - Resource->OwnerThreads[1].OwnerThread=Resource->OwnerThreads[0].OwnerThread; - Resource->OwnerThreads[1].OwnerCount=Resource->OwnerThreads[0].OwnerCount; - Resource->OwnerThreads[0].OwnerThread=0; - Resource->OwnerThreads[0].OwnerCount=0; - /* erase exclusive flag */ - Resource->Flag &= (~ResourceOwnedExclusive); - /* if no shared waiters, that's all */ - if (!oldWaiters) - { - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - return; - } - /* else, awake the waiters */ - KeReleaseSemaphore(Resource->SharedWaiters,0,oldWaiters,0); - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExConvertExclusiveToSharedLite() finished\n"); + /* Setup the timeout */ + ExpTimeout.QuadPart = Int32x32To64(4, -10000000); + InitializeListHead(&ExpSystemResourcesList); + KeInitializeSpinLock(&ExpResourceSpinLock); } -/* - * @implemented - */ +/*++ + * @name ExpAllocateExclusiveWaiterEvent + * + * The ExpAllocateExclusiveWaiterEvent routine creates the event that will + * be used by exclusive waiters on the resource. + * + * @param Resource + * Pointer to the resource. + * + * @param OldIrql + * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock. + * + * @return None. + * + * @remarks The pointer to the event must be atomically set. + * + *--*/ VOID -STDCALL -ExDisableResourceBoostLite ( - PERESOURCE Resource - ) +NTAPI +ExpAllocateExclusiveWaiterEvent(IN PERESOURCE Resource, + IN PKIRQL OldIrql) { - Resource->Flag |= ResourceDisableBoost; + PKEVENT Event; + + /* Release the lock */ + ExReleaseResourceLock(&Resource->SpinLock, *OldIrql); + + /* Allocate the event */ + Event = ExAllocatePoolWithTag(NonPagedPool, + sizeof(KEVENT), + TAG_RESOURCE_EVENT); + + /* Initialize it */ + KeInitializeEvent(Event, SynchronizationEvent, FALSE); + + /* Set it */ + if (InterlockedCompareExchangePointer(&Resource->ExclusiveWaiters, + Event, + NULL)) + { + /* Someone already set it, free our event */ + DPRINT1("WARNING: Handling race condition\n"); + ExFreePool(Event); + } + + /* Re-acquire the lock */ + ExAcquireResourceLock(&Resource->SpinLock, OldIrql); } -/* - * @implemented - */ -ULONG -STDCALL -ExGetExclusiveWaiterCount ( - PERESOURCE Resource - ) -{ - return(Resource->NumberOfExclusiveWaiters); -} - -/* - * @implemented - */ -BOOLEAN -STDCALL -ExAcquireSharedStarveExclusive ( - PERESOURCE Resource, - BOOLEAN Wait - ) -/* - * FUNCTION: Acquires a given resource for shared access without waiting - * for any pending attempts to acquire exclusive access to the - * same resource - * ARGUMENTS: - * Resource = Points to the resource to be acquired for shared access - * Wait = Is set to TRUE if the caller will wait until the resource - * becomes available when access can't be granted immediately - * RETURNS: TRUE if the requested access is granted. The routine returns - * FALSE if the input Wait is FALSE and shared access can't be - * granted immediately - */ -{ - KIRQL oldIrql; - - DPRINT("ExAcquireSharedStarveExclusive(Resource 0x%p, Wait %d)\n", - Resource, Wait); - - KeAcquireSpinLock(&Resource->SpinLock, &oldIrql); - - /* no owner, it's easy */ - if (Resource->ActiveCount == 0) - { - Resource->OwnerThreads[1].OwnerThread=ExGetCurrentResourceThread(); - Resource->OwnerThreads[1].OwnerCount=1; - Resource->ActiveCount=1; - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n"); - return(TRUE); - } - - if ((Resource->Flag & ResourceOwnedExclusive) - && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread()) - { - /* exclusive, but by same thread : it's ok */ - Resource->OwnerThreads[0].OwnerCount++; - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n"); - return(TRUE); - } - - if (Resource->Flag & ResourceOwnedExclusive) - { - /* exclusive by another thread */ - if (!Wait) - { - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n"); - return(FALSE); - } - else - { - Resource->NumberOfSharedWaiters++; - /* wait for the semaphore */ - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - KeWaitForSingleObject(Resource->SharedWaiters,0,0,0,0); - KeAcquireSpinLock(&Resource->SpinLock, &oldIrql); - Resource->NumberOfSharedWaiters--; - } - } - EiAddSharedOwner(Resource); - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n"); - return(TRUE); -} - -/* - * @implemented - */ -BOOLEAN -STDCALL -ExAcquireSharedWaitForExclusive ( - PERESOURCE Resource, - BOOLEAN Wait - ) -{ - return(ExAcquireResourceSharedLite(Resource,Wait)); -} - - -#ifdef ExDeleteResource -#undef ExDeleteResource -#endif - - -/* - * @implemented - */ -NTSTATUS -STDCALL -ExDeleteResource ( - PERESOURCE Resource - ) -{ - return(ExDeleteResourceLite(Resource)); -} - -/* - * @implemented - */ -NTSTATUS -STDCALL -ExDeleteResourceLite ( - PERESOURCE Resource - ) -{ - DPRINT("ExDeleteResourceLite(Resource 0x%p)\n", Resource); - if (Resource->OwnerTable) ExFreePool(Resource->OwnerTable); - if (Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters); - if (Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters); - return(STATUS_SUCCESS); -} - -/* - * @implemented - */ -ULONG -STDCALL -ExGetSharedWaiterCount ( - PERESOURCE Resource - ) -{ - return(Resource->NumberOfSharedWaiters); -} - - -#ifdef ExInitializeResource -#undef ExInitializeResource -#endif - -/* - * @implemented - */ -NTSTATUS -STDCALL -ExInitializeResource ( - PERESOURCE Resource - ) -{ - return(ExInitializeResourceLite(Resource)); -} - -/* - * @implemented - */ -NTSTATUS STDCALL -ExInitializeResourceLite (PERESOURCE Resource) -{ - DPRINT("ExInitializeResourceLite(Resource 0x%p)\n", Resource); - memset(Resource,0,sizeof(ERESOURCE)); - Resource->NumberOfSharedWaiters = 0; - Resource->NumberOfExclusiveWaiters = 0; - KeInitializeSpinLock(&Resource->SpinLock); - Resource->Flag = 0; - Resource->ExclusiveWaiters = - ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_EXCLUSIVE_LOCK); - KeInitializeEvent(Resource->ExclusiveWaiters, - SynchronizationEvent, - FALSE); - Resource->SharedWaiters = - ExAllocatePoolWithTag(NonPagedPool ,sizeof(KSEMAPHORE), TAG_SHARED_SEM); - KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff); - Resource->ActiveCount = 0; - return(0); -} - -/* - * @implemented - */ -BOOLEAN -STDCALL -ExIsResourceAcquiredExclusiveLite ( - PERESOURCE Resource - ) -/* - * FUNCTION: Returns whether the current thread has exclusive access to - * a given resource - * ARGUMENTS: - * Resource = Points to the resource to be queried - * RETURNS: TRUE if the caller has exclusive access to the resource, - * FALSE otherwise - */ -{ - return((Resource->Flag & ResourceOwnedExclusive) - && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread()); -} - - - -#ifdef ExIsResourceAcquiredSharedLite -#undef ExIsResourceAcquiredSharedLite -#endif - - -/* - * @implemented - */ - - -//NTOSAPI -//DDKAPI -USHORT STDCALL -ExIsResourceAcquiredSharedLite( - IN PERESOURCE Resource) -/* - * FUNCTION: Returns whether the current thread has shared access to a given - * resource - * ARGUMENTS: - * Resource = Points to the resource to be queried - * RETURNS: The number of times the caller has acquired shared access to the - * given resource - */ -{ - ULONG i; - if (Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread()) - { - return (USHORT)(Resource->OwnerThreads[0].OwnerCount); - } - if (Resource->OwnerThreads[1].OwnerThread == ExGetCurrentResourceThread()) - { - return (USHORT)(Resource->OwnerThreads[1].OwnerCount); - } - if (!Resource->OwnerThreads[1].TableSize) - { - return(0); - } - for (i=0; iOwnerThreads[1].TableSize; i++) - { - if (Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread()) - { - return (USHORT)Resource->OwnerTable[i].OwnerCount; - } - } - return(0); -} - -/* - * @implemented - */ +/*++ + * @name ExpAllocateSharedWaiterSemaphore + * + * The ExpAllocateSharedWaiterSemaphore routine creates the semaphore that + * will be used by shared waiters on the resource. + * + * @param Resource + * Pointer to the resource. + * + * @param OldIrql + * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock. + * + * @return None. + * + * @remarks The pointer to the semaphore must be atomically set. + * + *--*/ VOID -STDCALL -ExReinitializeResourceLite ( - PERESOURCE Resource - ) +NTAPI +ExpAllocateSharedWaiterSemaphore(IN PERESOURCE Resource, + IN PKIRQL OldIrql) { - Resource->NumberOfSharedWaiters = 0; - Resource->NumberOfExclusiveWaiters = 0; - KeInitializeSpinLock(&Resource->SpinLock); - Resource->Flag=0; - KeInitializeEvent(Resource->ExclusiveWaiters,SynchronizationEvent, - FALSE); - KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff); - Resource->ActiveCount = 0; - if (Resource->OwnerTable) - { - ExFreePool(Resource->OwnerTable); - } - Resource->OwnerThreads[0].OwnerThread=0; - Resource->OwnerThreads[0].OwnerCount=0; - Resource->OwnerThreads[1].OwnerThread=0; - Resource->OwnerThreads[1].OwnerCount=0; + PKSEMAPHORE Semaphore; + + /* Release the lock */ + ExReleaseResourceLock(&Resource->SpinLock, *OldIrql); + + /* Allocate the semaphore */ + Semaphore = ExAllocatePoolWithTag(NonPagedPool, + sizeof(KSEMAPHORE), + TAG_RESOURCE_SEMAPHORE); + + /* Initialize it */ + KeInitializeSemaphore(Semaphore, 0, MAXLONG); + + /* Set it */ + if (InterlockedCompareExchangePointer(&Resource->SharedWaiters, + Semaphore, + NULL)) + { + /* Someone already set it, free our semaphore */ + DPRINT1("WARNING: Handling race condition\n"); + ExFreePool(Semaphore); + } + + /* Re-acquire the lock */ + ExAcquireResourceLock(&Resource->SpinLock, OldIrql); } -/* - * @implemented - */ +/*++ + * @name ExpExpandResourceOwnerTable + * + * The ExpExpandResourceOwnerTable routine expands the owner table of the + * specified resource. + * + * @param Resource + * Pointer to the resource. + * + * @param OldIrql + * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock. + * + * @return None. + * + * @remarks None. + * + *--*/ VOID -FASTCALL -ExReleaseResourceLite ( - PERESOURCE Resource - ) +NTAPI +ExpExpandResourceOwnerTable(IN PERESOURCE Resource, + IN PKIRQL OldIrql) { - ExReleaseResourceForThreadLite(Resource, - ExGetCurrentResourceThread()); -} + POWNER_ENTRY Owner, Table; + ULONG NewSize, OldSize; + DPRINT("ExpExpandResourceOwnerTable: %p\n", Resource); + /* Get the owner table */ + Owner = Resource->OwnerTable; + if (!Owner) + { + /* Start with the default size of 3 */ + OldSize = 0; + NewSize = 3; + } + else + { + /* Add 4 more entries */ + OldSize = Owner->TableSize; + NewSize = OldSize + 4; + } + /* Release the lock */ + ExReleaseResourceLock(&Resource->SpinLock, *OldIrql); -#ifdef ExReleaseResourceForThread -#undef ExReleaseResourceForThread -#endif + /* Allocate memory for the table */ + Table = ExAllocatePoolWithTag(NonPagedPool, + NewSize * sizeof(OWNER_ENTRY), + TAG_RESOURCE_TABLE); - -/* - * @implemented - */ -VOID -STDCALL -ExReleaseResourceForThread ( - PERESOURCE Resource, - ERESOURCE_THREAD ResourceThreadId - ) -{ - ExReleaseResourceForThreadLite(Resource,ResourceThreadId); -} - - -/* - * @implemented - */ -VOID -STDCALL -ExReleaseResourceForThreadLite ( - PERESOURCE Resource, - ERESOURCE_THREAD ResourceThreadId - ) -/* - * FUNCTION: Releases a resource for the given thread - * ARGUMENTS: - * Resource = Points to the release to release - * ResourceThreadId = Identifies the thread that originally acquired - * the resource - * NOTES: Must be running at IRQL < DISPATCH_LEVEL - * BUG: We don't support starving exclusive waiters - */ -{ - KIRQL oldIrql; - - DPRINT("ExReleaseResourceForThreadLite(Resource 0x%p, ResourceThreadId 0x%p)\n", - Resource, ResourceThreadId); - - ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - - KeAcquireSpinLock(&Resource->SpinLock, &oldIrql); - - if (Resource->Flag & ResourceOwnedExclusive) - { - DPRINT("Releasing from exclusive access\n"); - - Resource->OwnerThreads[0].OwnerCount--; - if (Resource->OwnerThreads[0].OwnerCount > 0) - { - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExReleaseResourceForThreadLite() finished\n"); - return; - } - - Resource->OwnerThreads[0].OwnerThread = 0; - Resource->ActiveCount--; - Resource->Flag &=(~ResourceOwnedExclusive); - ASSERT(Resource->ActiveCount == 0); - DPRINT("Resource->NumberOfExclusiveWaiters %d\n", - Resource->NumberOfExclusiveWaiters); - if (Resource->NumberOfExclusiveWaiters) - { - /* get resource to first exclusive waiter */ - KeSetEvent(Resource->ExclusiveWaiters, - IO_NO_INCREMENT, - FALSE); - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExReleaseResourceForThreadLite() finished\n"); - return; - } - DPRINT("Resource->NumberOfSharedWaiters %d\n", - Resource->NumberOfSharedWaiters); - if (Resource->NumberOfSharedWaiters) - { - DPRINT("Releasing semaphore\n"); - KeReleaseSemaphore(Resource->SharedWaiters, - IO_NO_INCREMENT, - Resource->NumberOfSharedWaiters, - FALSE); - } - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExReleaseResourceForThreadLite() finished\n"); - return; - } - - EiRemoveSharedOwner(Resource, ResourceThreadId); - - if (Resource->ActiveCount == 0) - { - if (Resource->NumberOfExclusiveWaiters) - { - /* get resource to first exclusive waiter */ - KeSetEvent(Resource->ExclusiveWaiters, - IO_NO_INCREMENT, - FALSE); - } - } - - KeReleaseSpinLock(&Resource->SpinLock, oldIrql); - DPRINT("ExReleaseResourceForThreadLite() finished\n"); -} - - -/* - * @implemented - */ -VOID -STDCALL -ExSetResourceOwnerPointer ( - IN PERESOURCE Resource, - IN PVOID OwnerPointer - ) -{ - PKTHREAD CurrentThread; - KIRQL OldIrql; - POWNER_ENTRY OwnerEntry; - - CurrentThread = KeGetCurrentThread(); + /* Zero the table */ + RtlZeroMemory((PVOID)(Table + OldSize), + (NewSize - OldSize) * sizeof(OWNER_ENTRY)); /* Lock the resource */ - KeAcquireSpinLock(&Resource->SpinLock, &OldIrql); + ExAcquireResourceLock(&Resource->SpinLock, OldIrql); - /* Check if it's exclusive */ - if (Resource->Flag & ResourceOwnedExclusive) { + /* Make sure nothing has changed */ + if ((Owner != Resource->OwnerTable) || + ((Owner) && (OldSize != Resource->OwnerTable->TableSize))) + { + /* Resource changed while we weren't holding the lock; bail out */ + ExReleaseResourceLock(&Resource->SpinLock, *OldIrql); + ExFreePool(Table); + } + else + { + /* Copy the table */ + RtlCopyMemory((PVOID)Table, + Owner, + OldSize * sizeof(OWNER_ENTRY)); - /* If it's exclusive, set the first entry no matter what */ - Resource->OwnerThreads[0].OwnerThread = (ULONG_PTR)OwnerPointer; + /* Acquire dispatcher lock to prevent thread boosting */ + //KeAcquireDispatcherDatabaseLockAtDpcLevel(); - } else { + /* Set the new table data */ + Table->TableSize = NewSize; + Resource->OwnerTable = Table; - /* Check both entries and see which one matches the current thread */ - if (Resource->OwnerThreads[0].OwnerThread == (ULONG_PTR)CurrentThread) { + /* Sanity check */ + ExpVerifyResource(Resource); - Resource->OwnerThreads[0].OwnerThread = (ULONG_PTR)OwnerPointer; + /* Release locks */ + //KeReleaseDispatcherDatabaseLockFromDpcLevel(); + ExReleaseResourceLock(&Resource->SpinLock, *OldIrql); - } else if (Resource->OwnerThreads[1].OwnerThread == (ULONG_PTR)CurrentThread) { + /* Free the old table */ + if (Owner) ExFreePool(Owner); - Resource->OwnerThreads[1].OwnerThread = (ULONG_PTR)OwnerPointer; + /* Set the resource index */ + if (!OldSize) OldSize = 1; + } - } else { /* None of the entries match, so we need to do a lookup */ + /* Set the resource index */ + KeGetCurrentThread()->ResourceIndex = (UCHAR)OldSize; - /* Get the first Entry */ - OwnerEntry = Resource->OwnerTable; + /* Lock the resource again */ + ExAcquireResourceLock(&Resource->SpinLock, OldIrql); +} - /* Check if the Current Thread is in the Resource Table Entry */ - if ((CurrentThread->ResourceIndex >= OwnerEntry->TableSize) || - (OwnerEntry[CurrentThread->ResourceIndex].OwnerThread != (ULONG_PTR)CurrentThread)) { +/*++ + * @name ExpFindFreeEntry + * + * The ExpFindFreeEntry routine locates an empty owner entry in the + * specified resource. If none was found, then the owner table is + * expanded. + * + * @param Resource + * Pointer to the resource. + * + * @param OldIrql + * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock. + * + * @return Pointer to an empty OWNER_ENTRY structure. + * + * @remarks None. + * + *--*/ +POWNER_ENTRY +FASTCALL +ExpFindFreeEntry(IN PERESOURCE Resource, + IN PKIRQL OldIrql) +{ + POWNER_ENTRY Owner, Limit; + ULONG Size; + POWNER_ENTRY FreeEntry = NULL; + DPRINT("ExpFindFreeEntry: %p\n", Resource); - /* Loop until we find the current thread in an entry */ - for (;OwnerEntry->OwnerThread == (ULONG_PTR)CurrentThread;OwnerEntry++); + /* Sanity check */ + ASSERT(OldIrql != 0); + ASSERT(Resource->OwnerThreads[0].OwnerThread != 0); - } else { + /* Check if the next built-in entry is free */ + if (!Resource->OwnerThreads[1].OwnerThread) + { + /* Return it */ + FreeEntry = &Resource->OwnerThreads[1]; + } + else + { + /* Get the current table pointer */ + Owner = Resource->OwnerTable; + if (Owner) + { + /* Loop every entry */ + Size = Owner->TableSize; + Limit = &Owner[Size]; - /* It's in the current RTE, so set it */ - OwnerEntry = &OwnerEntry[CurrentThread->ResourceIndex]; + /* Go to the next entry and loop */ + Owner++; + do + { + /* Check for a free entry */ + if (!Owner->OwnerThread) + { + /* Found one, return it!*/ + FreeEntry = Owner; + break; + } + + /* Move on */ + Owner++; + } while (Owner != Limit); + + /* If we found a free entry by now, return it */ + if (FreeEntry) + { + /* Set the resource index */ + KeGetCurrentThread()->ResourceIndex = + (UCHAR)(Owner - Resource->OwnerTable); + return FreeEntry; + } + } + + /* No free entry, expand the table */ + ExpExpandResourceOwnerTable(Resource, OldIrql); + FreeEntry = NULL; + } + + /* Return the entry found */ + return FreeEntry; +} + +/*++ + * @name ExpFindEntryForThread + * + * The ExpFindEntryForThread routine locates the owner entry associated with + * the specified thread in the given resource. If none was found, then the + * owner table is expanded. + * + * @param Resource + * Pointer to the resource. + * + * @param Thread + * Pointer to the thread to find. + * + * @param OldIrql + * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock. + * + * @return Pointer to an empty OWNER_ENTRY structure. + * + * @remarks None. + * + *--*/ +POWNER_ENTRY +FASTCALL +ExpFindEntryForThread(IN PERESOURCE Resource, + IN ERESOURCE_THREAD Thread, + IN PKIRQL OldIrql) +{ + POWNER_ENTRY FreeEntry, Owner, Limit; + ULONG Size; + DPRINT("ExpFindEntryForThread: %p\n", Resource); + + /* Start by looking in the static array */ + if (Resource->OwnerThreads[0].OwnerThread == Thread) + { + /* Found it, return it! */ + return &Resource->OwnerThreads[0]; + } + else if (Resource->OwnerThreads[1].OwnerThread == Thread) + { + /* Return it */ + return &Resource->OwnerThreads[1]; + } + else + { + /* Check if the first array is empty for our use */ + FreeEntry = NULL; + if (!Resource->OwnerThreads[1].OwnerThread) + { + /* Use this as the first free entry */ + FreeEntry = &Resource->OwnerThreads[1]; + } + + /* Get the current table pointer */ + Owner = Resource->OwnerTable; + if (!Owner) + { + /* The current table is empty, so no size */ + Size = 0; + } + else + { + /* We have a table, get it's size and limit */ + Size = Owner->TableSize; + Limit = &Owner[Size]; + + /* Go to the next entry and loop */ + Owner++; + do + { + /* Check for a match */ + if (Owner->OwnerThread == Thread) + { + /* Match found! Set the resource index */ + KeGetCurrentThread()->ResourceIndex = + (UCHAR)(Owner - Resource->OwnerTable); + return Owner; + } + + /* If we don't have a free entry yet, make this one free */ + if (!(FreeEntry) && !(Owner->OwnerThread)) FreeEntry = Owner; + + /* Move on */ + Owner++; + } while (Owner != Limit); + } + } + + /* Check if it's OK to do an expansion */ + if (!OldIrql) return NULL; + + /* If we found a free entry by now, return it */ + if (FreeEntry) + { + /* Set the resource index */ + KeGetCurrentThread()->ResourceIndex = (UCHAR) + (Owner - Resource->OwnerTable); + return FreeEntry; + } + + /* No free entry, expand the table */ + ExpExpandResourceOwnerTable(Resource, OldIrql); + return NULL; +} + +/*++ + * @name ExpBoostOwnerThread + * + * The ExpBoostOwnerThread routine increases the priority of a waiting + * thread in an attempt to fight a possible deadlock. + * + * @param Thread + * Pointer to the current thread. + * + * @param OwnerThread + * Pointer to thread that owns the resource. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +FASTCALL +ExpBoostOwnerThread(IN PKTHREAD Thread, + IN PKTHREAD OwnerThread) +{ + BOOLEAN Released = FALSE; + DPRINT("ExpBoostOwnerThread: %p\n", Thread); + + /* Make sure the owner thread is a pointer, not an ID */ + if (!((ULONG_PTR)OwnerThread & 0x3)) + { + /* Check if we can actually boost it */ + if ((OwnerThread->Priority < Thread->Priority) && + (OwnerThread->Priority < 14)) + { + /* Make sure we're at dispatch */ + ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); + + /* Set the new priority */ + OwnerThread->PriorityDecrement += 14 - OwnerThread->Priority; + + /* Update quantum */ + OwnerThread->Quantum = OwnerThread->QuantumReset; + + /* Update the kernel state */ + KiSetPriorityThread(OwnerThread, 14, &Released); + + /* Release Lock if needed */ + if (!Released) KeReleaseDispatcherDatabaseLockFromDpcLevel(); + + /* Make sure we're still at dispatch */ + ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL); + } + } +} + +/*++ + * @name ExpWaitForResource + * + * The ExpWaitForResource routine performs a wait on the specified resource. + * + * @param Resource + * Pointer to the resource to wait on. + * + * @param OwnerThread + * Pointer to object (exclusive event or shared semaphore) to wait on. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +FASTCALL +ExpWaitForResource(IN PERESOURCE Resource, + IN PVOID Object) +{ + ULONG i; + ULONG Size, WaitCount = 0; + KIRQL OldIrql; + POWNER_ENTRY Owner; + PKTHREAD OwnerThread, Thread; + NTSTATUS Status; + LARGE_INTEGER Timeout; + + /* Increase contention count and use a 5 second timeout */ + Resource->ContentionCount++; + Timeout.QuadPart = 500 * -10000; + for(;;) + { + /* Wait for ownership */ + Status = KeWaitForSingleObject(Object, + WrResource, + KernelMode, + FALSE, + &Timeout); + if (Status != STATUS_TIMEOUT) break; + + /* Increase wait count */ + WaitCount++; + Timeout = ExpTimeout; + + /* Check if we've exceeded the limit */ + if (WaitCount > ExpResourceTimeoutCount) + { + /* Reset wait count */ + WaitCount = 0; +#if DBG + /* Lock the resource */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* Dump debug information */ + DPRINT1("Resource @ %lx\n", Resource); + DPRINT1(" ActiveCount = %04lx Flags = %s%s%s\n", + Resource->ActiveCount, + IsOwnedExclusive(Resource) ? "IsOwnedExclusive " : "", + IsSharedWaiting(Resource) ? "SharedWaiter " : "", + IsExclusiveWaiting(Resource) ? "ExclusiveWaiter " : ""); + DPRINT1(" NumberOfExclusiveWaiters = %04lx\n", + Resource->NumberOfExclusiveWaiters); + DPRINT1(" Thread = %08lx, Count = %02x\n", + Resource->OwnerThreads[0].OwnerThread, + Resource->OwnerThreads[0].OwnerCount); + DPRINT1(" Thread = %08lx, Count = %02x\n", + Resource->OwnerThreads[1].OwnerThread, + Resource->OwnerThreads[1].OwnerCount); + + /* Dump out the table too */ + Owner = Resource->OwnerTable; + if (Owner) + { + /* Loop every entry */ + Size = Owner->TableSize; + for (i = 1; i < Size; i++) + { + /* Print the data */ + Owner++; + DPRINT1(" Thread = %08lx, Count = %02x\n", + Owner->OwnerThread, + Owner->OwnerCount); + } } - /* Now that we went to the right entry, set the Owner Pointer */ - OwnerEntry->OwnerThread = (ULONG_PTR)OwnerPointer; + /* Break */ + DbgBreakPoint(); + DPRINT1("EX - Rewaiting\n"); + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); +#endif + } + + /* Check if we can boost */ + if (!(Resource->Flag & ResourceHasDisabledPriorityBoost)) + { + /* Get the current kernel thread and lock the dispatcher */ + Thread = KeGetCurrentThread(); + Thread->WaitIrql = KeAcquireDispatcherDatabaseLock(); + Thread->WaitNext = TRUE; + + /* Get the owner thread and boost it */ + OwnerThread = (PKTHREAD)Resource->OwnerThreads[0].OwnerThread; + if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread); + + /* If it's a shared resource */ + if (!IsOwnedExclusive(Resource)) + { + /* Boost the other owner thread too */ + OwnerThread = (PKTHREAD)Resource->OwnerThreads[1].OwnerThread; + if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread); + + /* Get the table */ + Owner = Resource->OwnerTable; + if (Owner) + { + /* Loop every entry */ + Size = Owner->TableSize; + for (i = 1; i < Size; i++) + { + /* Boot every thread in the table */ + OwnerThread = (PKTHREAD) + Resource->OwnerThreads[1].OwnerThread; + if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread); + } + } + } + } + } +} + +/* FUNCTIONS *****************************************************************/ + +/*++ + * @name ExAcquireResourceExclusiveLite + * @implemented NT4 + * + * The ExAcquireResourceExclusiveLite routine acquires the given resource + * for exclusive access by the calling thread. + * + * @param Resource + * Pointer to the resource to acquire. + * + * @param Wait + * Specifies the routine's behavior whenever the resource cannot be + * acquired immediately. + * + * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE + * and exclusive access cannot be granted immediately. + * + * @remarks The caller can release the resource by calling either + * ExReleaseResourceLite or ExReleaseResourceForThreadLite. + * + * Normal kernel APC delivery must be disabled before calling this + * routine. Disable normal kernel APC delivery by calling + * KeEnterCriticalRegion. Delivery must remain disabled until the + * resource is released, at which point it can be reenabled by calling + * KeLeaveCriticalRegion. + * + * For better performance, call ExTryToAcquireResourceExclusiveLite, + * rather than calling ExAcquireResourceExclusiveLite with Wait set + * to FALSE. + * + * Callers of ExAcquireResourceExclusiveLite must be running at IRQL < + * DISPATCH_LEVEL. + * + *--*/ +BOOLEAN +NTAPI +ExAcquireResourceExclusiveLite(PERESOURCE Resource, + BOOLEAN Wait) +{ + KIRQL OldIrql = PASSIVE_LEVEL; + ERESOURCE_THREAD Thread; + BOOLEAN Success; + + /* Sanity check */ + ASSERT((Resource->Flag & ResourceNeverExclusive) == 0); + + /* Get the thread */ + Thread = ExGetCurrentResourceThread(); + + /* Sanity check and validation */ + ASSERT(KeIsExecutingDpc() == FALSE); + ExpVerifyResource(Resource); + + /* Acquire the lock */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + ExpCheckForApcsDisabled(TRUE, Resource, (PETHREAD)Thread); + + /* Check if there is a shared owner or exclusive owner */ +TryAcquire: + DPRINT("ExAcquireResourceExclusiveLite(Resource 0x%p, Wait %d)\n", + Resource, Wait); + if (Resource->ActiveCount) + { + /* Check if it's exclusively owned, and we own it */ + if ((IsOwnedExclusive(Resource)) && + (Resource->OwnerThreads[0].OwnerThread == Thread)) + { + /* Increase the owning count */ + Resource->OwnerThreads[0].OwnerCount++; + Success = TRUE; + } + else + { + /* + * If the caller doesn't want us to wait, we can't acquire the + * resource because someone else then us owns it. If we can wait, + * then we'll wait. + */ + if (!Wait) + { + Success = FALSE; + } + else + { + /* Check if it has exclusive waiters */ + if (!Resource->ExclusiveWaiters) + { + /* It doesn't, allocate the event and try acquiring again */ + ExpAllocateExclusiveWaiterEvent(Resource, &OldIrql); + goto TryAcquire; + } + else + { + /* Has exclusive waiters, wait on it */ + Resource->NumberOfExclusiveWaiters++; + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + ExpWaitForResource(Resource, Resource->ExclusiveWaiters); + + /* Set owner and return success */ + Resource->OwnerThreads[0].OwnerThread = Thread; + return TRUE; + } + } + } + } + else + { + /* Nobody owns it, so let's! */ + Resource->Flag |= ResourceOwnedExclusive; + Resource->ActiveCount = 1; + Resource->OwnerThreads[0].OwnerThread = Thread; + Resource->OwnerThreads[0].OwnerCount = 1; + Success = TRUE; + } + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return Success; +} + +/*++ + * @name ExAcquireResourceSharedLite + * @implemented NT4 + * + * The ExAcquireResourceSharedLite routine acquires the given resource + * for shared access by the calling thread. + * + * @param Resource + * Pointer to the resource to acquire. + * + * @param Wait + * Specifies the routine's behavior whenever the resource cannot be + * acquired immediately. + * + * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE + * and exclusive access cannot be granted immediately. + * + * @remarks The caller can release the resource by calling either + * ExReleaseResourceLite or ExReleaseResourceForThreadLite. + * + * Normal kernel APC delivery must be disabled before calling this + * routine. Disable normal kernel APC delivery by calling + * KeEnterCriticalRegion. Delivery must remain disabled until the + * resource is released, at which point it can be reenabled by calling + * KeLeaveCriticalRegion. + * + * Callers of ExAcquireResourceExclusiveLite must be running at IRQL < + * DISPATCH_LEVEL. + * + *--*/ +BOOLEAN +NTAPI +ExAcquireResourceSharedLite(PERESOURCE Resource, + BOOLEAN Wait) +{ + KIRQL OldIrql; + ERESOURCE_THREAD Thread; + POWNER_ENTRY Owner; + + /* Get the thread */ + Thread = ExGetCurrentResourceThread(); + + /* Sanity check and validation */ + ASSERT(KeIsExecutingDpc() == FALSE); + ExpVerifyResource(Resource); + + /* Acquire the lock */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + ExpCheckForApcsDisabled(TRUE, Resource, (PETHREAD)Thread); + + /* See if nobody owns us */ +TryAcquire: + DPRINT("ExAcquireResourceSharedLite(Resource 0x%p, Wait %d)\n", + Resource, Wait); + if (!Resource->ActiveCount) + { + /* Nobody owns it, so let's take control */ + Resource->ActiveCount = 1; + Resource->OwnerThreads[1].OwnerThread = Thread; + Resource->OwnerThreads[1].OwnerCount = 1; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Check if it's exclusively owned */ + if (IsOwnedExclusive(Resource)) + { + /* Check if we own it */ + if (Resource->OwnerThreads[0].OwnerThread == Thread) + { + /* Increase the owning count */ + Resource->OwnerThreads[0].OwnerCount++; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Find a free entry */ + Owner = ExpFindFreeEntry(Resource, &OldIrql); + if (!Owner) goto TryAcquire; + } + else + { + /* Resource is shared, find who owns it */ + Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql); + if (!Owner) goto TryAcquire; + + /* Is it us? */ + if (Owner->OwnerThread == Thread) + { + /* Increase acquire count and return */ + Owner->OwnerCount++; + ASSERT(Owner->OwnerCount != 0); + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Try to find if there are exclusive waiters */ + if (!IsExclusiveWaiting(Resource)) + { + /* There are none, so acquire it */ + Owner->OwnerThread = Thread; + Owner->OwnerCount = 1; + Resource->ActiveCount++; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + } + + /* If we got here, then we need to wait. Are we allowed? */ + if (!Wait) + { + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Check if we have a shared waiters semaphore */ + if (!Resource->SharedWaiters) + { + /* Allocate it and try another acquire */ + ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql); + goto TryAcquire; + } + + /* Now wait for the resource */ + Owner->OwnerThread = Thread; + Owner->OwnerCount = 1; + Resource->NumberOfSharedWaiters++; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + ExpWaitForResource(Resource, Resource->SharedWaiters); + return TRUE; +} + +/*++ + * @name ExAcquireSharedStarveExclusive + * @implemented NT4 + * + * The ExAcquireSharedStarveExclusive routine acquires the given resource + * shared access without waiting for any pending attempts to acquire + * exclusive access to the same resource. + * + * @param Resource + * Pointer to the resource to acquire. + * + * @param Wait + * Specifies the routine's behavior whenever the resource cannot be + * acquired immediately. + * + * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE + * and exclusive access cannot be granted immediately. + * + * @remarks The caller can release the resource by calling either + * ExReleaseResourceLite or ExReleaseResourceForThreadLite. + * + * Normal kernel APC delivery must be disabled before calling this + * routine. Disable normal kernel APC delivery by calling + * KeEnterCriticalRegion. Delivery must remain disabled until the + * resource is released, at which point it can be reenabled by calling + * KeLeaveCriticalRegion. + * + * Callers of ExAcquireSharedStarveExclusive usually need quick access + * to a shared resource in order to save an exclusive accessor from + * doing redundant work. For example, a file system might call this + * routine to modify a cached resource, such as a BCB pinned in the + * cache, before the Cache Manager can acquire exclusive access to the + * resource and write the cache out to disk. + * + * Callers of ExAcquireResourceExclusiveLite must be running at IRQL < + * DISPATCH_LEVEL. + * + *--*/ +BOOLEAN +NTAPI +ExAcquireSharedStarveExclusive(PERESOURCE Resource, + BOOLEAN Wait) +{ + KIRQL OldIrql; + ERESOURCE_THREAD Thread; + POWNER_ENTRY Owner; + + /* Get the thread */ + Thread = ExGetCurrentResourceThread(); + + /* Sanity check and validation */ + ASSERT(KeIsExecutingDpc() == FALSE); + ExpVerifyResource(Resource); + + /* Acquire the lock */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* See if nobody owns us */ +TryAcquire: + DPRINT("ExAcquireSharedStarveExclusive(Resource 0x%p, Wait %d)\n", + Resource, Wait); + if (!Resource->ActiveCount) + { + /* Nobody owns it, so let's take control */ + Resource->ActiveCount = 1; + Resource->OwnerThreads[1].OwnerThread = Thread; + Resource->OwnerThreads[1].OwnerCount = 1; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Check if it's exclusively owned */ + if (IsOwnedExclusive(Resource)) + { + /* Check if we own it */ + if (Resource->OwnerThreads[0].OwnerThread == Thread) + { + /* Increase the owning count */ + Resource->OwnerThreads[0].OwnerCount++; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Find a free entry */ + Owner = ExpFindFreeEntry(Resource, &OldIrql); + if (!Owner) goto TryAcquire; + + /* If we got here, then we need to wait. Are we allowed? */ + if (!Wait) + { + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Check if we have a shared waiters semaphore */ + if (!Resource->SharedWaiters) + { + /* Allocate one and try again */ + ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql); + goto TryAcquire; + } + } + else + { + /* Resource is shared, find who owns it */ + Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql); + if (!Owner) goto TryAcquire; + + /* Is it us? */ + if (Owner->OwnerThread == Thread) + { + /* Increase acquire count and return */ + Owner->OwnerCount++; + ASSERT(Owner->OwnerCount != 0); + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Acquire it */ + Owner->OwnerThread = Thread; + Owner->OwnerCount = 1; + Resource->ActiveCount++; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* If we got here, then we need to wait. Are we allowed? */ + if (!Wait) + { + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Now wait for the resource */ + Owner->OwnerThread = Thread; + Owner->OwnerCount = 1; + Resource->NumberOfSharedWaiters++; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + ExpWaitForResource(Resource, Resource->SharedWaiters); + return TRUE; +} + +/*++ + * @name ExAcquireSharedWaitForExclusive + * @implemented NT4 + * + * The ExAcquireSharedWaitForExclusive routine acquires the given resource + * for shared access if shared access can be granted and there are no + * exclusive waiters. + * + * @param Resource + * Pointer to the resource to acquire. + * + * @param Wait + * Specifies the routine's behavior whenever the resource cannot be + * acquired immediately. + * + * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE + * and exclusive access cannot be granted immediately. + * + * @remarks The caller can release the resource by calling either + * ExReleaseResourceLite or ExReleaseResourceForThreadLite. + * + * Normal kernel APC delivery must be disabled before calling this + * routine. Disable normal kernel APC delivery by calling + * KeEnterCriticalRegion. Delivery must remain disabled until the + * resource is released, at which point it can be reenabled by calling + * KeLeaveCriticalRegion. + * + * Callers of ExAcquireResourceExclusiveLite must be running at IRQL < + * DISPATCH_LEVEL. + * + *--*/ +BOOLEAN +NTAPI +ExAcquireSharedWaitForExclusive(PERESOURCE Resource, + BOOLEAN Wait) +{ + KIRQL OldIrql; + ERESOURCE_THREAD Thread; + POWNER_ENTRY Owner; + + /* Get the thread */ + Thread = ExGetCurrentResourceThread(); + + /* Sanity check and validation */ + ASSERT(KeIsExecutingDpc() == FALSE); + ExpVerifyResource(Resource); + + /* Acquire the lock */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* See if nobody owns us */ +TryAcquire: + DPRINT("ExAcquireSharedWaitForExclusive(Resource 0x%p, Wait %d)\n", + Resource, Wait); + if (!Resource->ActiveCount) + { + /* Nobody owns it, so let's take control */ + Resource->ActiveCount = 1; + Resource->OwnerThreads[1].OwnerThread = Thread; + Resource->OwnerThreads[1].OwnerCount = 1; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Check if it's exclusively owned */ + if (IsOwnedExclusive(Resource)) + { + /* Check if we own it */ + if (Resource->OwnerThreads[0].OwnerThread == Thread) + { + /* Increase the owning count */ + Resource->OwnerThreads[0].OwnerCount++; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + else + { + /* Find a free entry */ + Owner = ExpFindFreeEntry(Resource, &OldIrql); + if (!Owner) goto TryAcquire; + + /* If we got here, then we need to wait. Are we allowed? */ + if (!Wait) + { + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Check if we have a shared waiters semaphore */ + if (!Resource->SharedWaiters) + { + /* Allocate one and try again */ + ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql); + goto TryAcquire; + } + + /* Now take control of the resource */ + Owner->OwnerThread = Thread; + Owner->OwnerCount = 1; + Resource->NumberOfSharedWaiters++; + + /* Release the lock and wait for it to be ours */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + ExpWaitForResource(Resource, Resource->SharedWaiters); + return TRUE; + } + } + else + { + /* Try to find if there are exclusive waiters */ + if (IsExclusiveWaiting(Resource)) + { + /* We have to wait for the exclusive waiter to be done */ + if (!Wait) + { + /* So bail out if we're not allowed */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* Check if we have a shared waiters semaphore */ + if (!Resource->SharedWaiters) + { + /* Allocate one and try again */ + ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql); + goto TryAcquire; + } + + /* Now wait for the resource */ + Resource->NumberOfSharedWaiters++; + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + ExpWaitForResource(Resource, Resource->SharedWaiters); + + /* Get the lock back */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* Find who owns it now */ + Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql); + + /* Sanity checks */ + ASSERT(IsOwnedExclusive(Resource) == FALSE); + ASSERT(Resource->ActiveCount > 0); + ASSERT(Owner->OwnerThread != Thread); + + /* Take control */ + Owner->OwnerThread = Thread; + Owner->OwnerCount = 1; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + else + { + /* Resource is shared, find who owns it */ + Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql); + if (!Owner) goto TryAcquire; + + /* Is it us? */ + if (Owner->OwnerThread == Thread) + { + /* Increase acquire count and return */ + Owner->OwnerCount++; + ASSERT(Owner->OwnerCount != 0); + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + + /* No exclusive waiters, so acquire it */ + Owner->OwnerThread = Thread; + Owner->OwnerCount = 1; + Resource->ActiveCount++; + + /* Release the lock and return */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return TRUE; + } + } +} + +/*++ + * @name ExConvertExclusiveToSharedLite + * @implemented NT4 + * + * The ExConvertExclusiveToSharedLite routine converts an exclusively + * acquired resource into a resource that can be acquired shared. + * + * @param Resource + * Pointer to the resource to convert. + * + * @return None. + * + * @remarks Callers of ExConvertExclusiveToSharedLite must be running at IRQL < + * DISPATCH_LEVEL. + * + *--*/ +VOID +NTAPI +ExConvertExclusiveToSharedLite(PERESOURCE Resource) +{ + ULONG OldWaiters; + KIRQL OldIrql; + DPRINT("ExConvertExclusiveToSharedLite(Resource 0x%p)\n", Resource); + + /* Lock the resource */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* Sanity checks */ + ASSERT(KeIsExecutingDpc() == FALSE); + ExpVerifyResource(Resource); + ASSERT(IsOwnedExclusive(Resource)); + ASSERT(Resource->OwnerThreads[0].OwnerThread == (ERESOURCE_THREAD)PsGetCurrentThread()); + + /* Erase the exclusive flag */ + Resource->Flag &= ~ResourceOwnedExclusive; + + /* Check if we have shared waiters */ + OldWaiters = Resource->NumberOfSharedWaiters; + if (OldWaiters) + { + /* Make the waiters active owners */ + Resource->ActiveCount = Resource->ActiveCount + (USHORT)OldWaiters; + Resource->NumberOfSharedWaiters = 0; + + /* Release lock and wake the waiters */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + KeReleaseSemaphore(Resource->SharedWaiters, 0, OldWaiters, FALSE); + } + else + { + /* Release lock */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + } +} + +/*++ + * @name ExDeleteResourceLite + * @implemented NT4 + * + * The ExConvertExclusiveToSharedLite routine deletes a given resource + * from the system’s resource list. + * + * @param Resource + * Pointer to the resource to delete. + * + * @return STATUS_SUCCESS if the resource was deleted. + * + * @remarks Callers of ExDeleteResourceLite must be running at IRQL < + * DISPATCH_LEVEL. + * + *--*/ +NTSTATUS +NTAPI +ExDeleteResourceLite(PERESOURCE Resource) +{ + KIRQL OldIrql; + DPRINT("ExDeleteResourceLite(Resource 0x%p)\n", Resource); + + /* Sanity checks */ + ASSERT(IsSharedWaiting(Resource) == FALSE); + ASSERT(IsExclusiveWaiting(Resource) == FALSE); + ASSERT(KeIsExecutingDpc() == FALSE); + ExpVerifyResource(Resource); + + /* Lock the resource */ + KeAcquireSpinLock(&ExpResourceSpinLock, &OldIrql); + + /* Remove the resource */ + RemoveEntryList(&Resource->SystemResourcesList); + + /* Release the lock */ + KeReleaseSpinLock(&ExpResourceSpinLock, OldIrql); + + /* Free every structure */ + if (Resource->OwnerTable) ExFreePool(Resource->OwnerTable); + if (Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters); + if (Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters); + + /* Return success */ + return STATUS_SUCCESS; +} + +/*++ + * @name ExDisableResourceBoostLite + * @implemented NT4 + * + * The ExDisableResourceBoostLite routine disables thread boosting for + * the given resource. + * + * @param Resource + * Pointer to the resource whose thread boosting will be disabled. + * + * @return None. + * + * @remarks None. + * + *--*/ +VOID +NTAPI +ExDisableResourceBoostLite(PERESOURCE Resource) +{ + KIRQL OldIrql; + + /* Sanity check */ + ExpVerifyResource(Resource); + + /* Lock the resource */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* Remove the flag */ + Resource->Flag |= ResourceHasDisabledPriorityBoost; + + /* Release the lock */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); +} + +/*++ + * @name ExGetExclusiveWaiterCount + * @implemented NT4 + * + * The ExGetExclusiveWaiterCount routine returns the number of exclusive + * waiters for the given resource. + * + * @param Resource + * Pointer to the resource to check. + * + * @return The number of exclusive waiters. + * + * @remarks None. + * + *--*/ +ULONG +NTAPI +ExGetExclusiveWaiterCount(PERESOURCE Resource) +{ + /* Return the count */ + return Resource->NumberOfExclusiveWaiters; +} + +/*++ + * @name ExGetSharedWaiterCount + * @implemented NT4 + * + * The ExGetSharedWaiterCount routine returns the number of shared + * waiters for the given resource. + * + * @param Resource + * Pointer to the resource to check. + * + * @return The number of shared waiters. + * + * @remarks None. + * + *--*/ +ULONG +NTAPI +ExGetSharedWaiterCount(PERESOURCE Resource) +{ + /* Return the count */ + return Resource->NumberOfSharedWaiters; +} + +/*++ + * @name ExInitializeResourceLite + * @implemented NT4 + * + * The ExInitializeResourceLite routine initializes a resource variable. + * + * @param Resource + * Pointer to the resource to check. + * + * @return STATUS_SUCCESS. + * + * @remarks The storage for ERESOURCE must not be allocated from paged pool. + * + * The storage must be 8-byte aligned. + * + *--*/ +NTSTATUS +NTAPI +ExInitializeResourceLite(PERESOURCE Resource) +{ + DPRINT("ExInitializeResourceLite(Resource 0x%p)\n", Resource); + + /* Clear the structure */ + RtlZeroMemory(Resource, sizeof(ERESOURCE)); + + /* Initialize the lock */ + KeInitializeSpinLock(&Resource->SpinLock); + + /* Add it into the system list */ + ExInterlockedInsertTailList(&ExpSystemResourcesList, + &Resource->SystemResourcesList, + &ExpResourceSpinLock); + + /* Return success */ + return STATUS_SUCCESS; +} + +/*++ + * @name ExIsResourceAcquiredExclusiveLite + * @implemented NT4 + * + * The ExIsResourceAcquiredExclusiveLite routine returns whether the + * current thread has exclusive access to a given resource. + * + * @param Resource + * Pointer to the resource to check. + * + * @return TRUE if the caller already has exclusive access to the given resource. + * + * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at + * IRQL <= DISPATCH_LEVEL. + * + *--*/ +BOOLEAN +NTAPI +ExIsResourceAcquiredExclusiveLite(PERESOURCE Resource) +{ + ERESOURCE_THREAD Thread = ExGetCurrentResourceThread(); + BOOLEAN IsAcquired = FALSE; + + /* Sanity check */ + ExpVerifyResource(Resource); + + /* Check if it's exclusively acquired */ + if ((IsOwnedExclusive(Resource)) && + (Resource->OwnerThreads[0].OwnerThread == Thread)) + { + /* It is acquired */ + IsAcquired = TRUE; + } + + /* Return if it's acquired */ + return IsAcquired; +} + +/*++ + * @name ExIsResourceAcquiredSharedLite + * @implemented NT4 + * + * The ExIsResourceAcquiredSharedLite routine returns whether the + * current thread has has access (either shared or exclusive) to a + * given resource. + * + * @param Resource + * Pointer to the resource to check. + * + * @return Number of times the caller has acquired the given resource for + * shared or exclusive access. + * + * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at + * IRQL <= DISPATCH_LEVEL. + * + *--*/ +ULONG +NTAPI +ExIsResourceAcquiredSharedLite(IN PERESOURCE Resource) +{ + ERESOURCE_THREAD Thread; + ULONG i, Size; + ULONG Count = 0; + KIRQL OldIrql; + POWNER_ENTRY Owner; + + /* Sanity check */ + ExpVerifyResource(Resource); + + /* Get the thread */ + Thread = ExGetCurrentResourceThread(); + + /* Check if we are in the thread list */ + if (Resource->OwnerThreads[0].OwnerThread == Thread) + { + /* Found it, return count */ + Count = Resource->OwnerThreads[0].OwnerCount; + } + else if (Resource->OwnerThreads[1].OwnerThread == Thread) + { + /* Found it on the second list, return count */ + Count = Resource->OwnerThreads[1].OwnerCount; + } + else + { + /* Not in the list, do a full table look up */ + Owner = Resource->OwnerTable; + if (Owner) + { + /* Lock the resource */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* Get the resource index */ + i = ((PKTHREAD)Thread)->ResourceIndex; + Size = Owner->TableSize; + + /* Check if the index is valid and check if we don't match */ + if ((i >= Size) || (Owner[i].OwnerThread != Thread)) + { + /* Sh*t! We need to do a full search */ + for (i = 1; i < Size; i++) + { + /* Move to next owner */ + Owner++; + + /* Try to find a match */ + if (Owner->OwnerThread == Thread) + { + /* Finally! */ + Count = Owner->OwnerCount; + break; + } + } + } + else + { + /* We found the match directlry */ + Count = Owner[i].OwnerCount; + } + + /* Release the lock */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + } + } + + /* Return count */ + return Count; +} + +/*++ + * @name ExReinitializeResourceLite + * @implemented NT4 + * + * The ExReinitializeResourceLite routine routine reinitializes + * an existing resource variable. + * + * @param Resource + * Pointer to the resource to be reinitialized. + * + * @return STATUS_SUCCESS. + * + * @remarks With a single call to ExReinitializeResource, a driver writer can + * replace three calls: one to ExDeleteResourceLite, another to + * ExAllocatePool, and a third to ExInitializeResourceLite. As + * contention for a resource variable increases, memory is dynamically + * allocated and attached to the resource in order to track this + * contention. As an optimization, ExReinitializeResourceLite retains + * and zeroes this previously allocated memory. + * + * Callers of ExReinitializeResourceLite must be running at + * IRQL <= DISPATCH_LEVEL. + * + *--*/ +NTSTATUS +NTAPI +ExReinitializeResourceLite(PERESOURCE Resource) +{ + PKEVENT Event; + PKSEMAPHORE Semaphore; + ULONG i, Size; + POWNER_ENTRY Owner; + + /* Get the owner table */ + Owner = Resource->OwnerTable; + if (Owner) + { + /* Get the size and loop it */ + Size = Owner->TableSize; + for (i = 0; i < Size; i++) + { + /* Zero the table */ + Owner[i].OwnerThread = 0; + Owner[i].OwnerCount = 0; + } + } + + /* Zero the flags and count */ + Resource->Flag = 0; + Resource->ActiveCount = 0; + + /* Reset the semaphore */ + Semaphore = Resource->SharedWaiters; + if (Semaphore) KeInitializeSemaphore(Semaphore, 0, MAXLONG); + + /* Reset the event */ + Event = Resource->ExclusiveWaiters; + if (Event) KeInitializeEvent(Event, SynchronizationEvent, FALSE); + + /* Clear the resource data */ + Resource->OwnerThreads[0].OwnerThread = 0; + Resource->OwnerThreads[1].OwnerThread = 0; + Resource->OwnerThreads[0].OwnerCount = 0; + Resource->OwnerThreads[1].OwnerCount = 0; + Resource->ContentionCount = 0; + Resource->NumberOfSharedWaiters = 0; + Resource->NumberOfExclusiveWaiters = 0; + + /* Reset the spinlock */ + KeInitializeSpinLock(&Resource->SpinLock); + return STATUS_SUCCESS; +} + +/*++ + * @name ExReleaseResourceLite + * @implemented NT4 + * + * The ExReleaseResourceLite routine routine releases + * a specified executive resource owned by the current thread. + * + * @param Resource + * Pointer to the resource to be released. + * + * @return None. + * + * @remarks Callers of ExReleaseResourceLite must be running at + * IRQL <= DISPATCH_LEVEL. + * + *--*/ +VOID +FASTCALL +ExReleaseResourceLite(PERESOURCE Resource) +{ + ERESOURCE_THREAD Thread; + ULONG Count, i; + KIRQL OldIrql; + POWNER_ENTRY Owner, Limit; + DPRINT("ExReleaseResourceLite: %p\n", Resource); + + /* Sanity check */ + ExpVerifyResource(Resource); + + /* Get the thread and lock the resource */ + Thread = ExGetCurrentResourceThread(); + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + ExpCheckForApcsDisabled(TRUE, Resource, (PETHREAD)Thread); + + /* Check if it's exclusively owned */ + if (IsOwnedExclusive(Resource)) + { + /* Decrement owner count and check if we're done */ + ASSERT(Resource->OwnerThreads[0].OwnerCount > 0); + if (--Resource->OwnerThreads[0].OwnerCount) + { + /* Done, release lock! */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return; + } + + /* Clear the owner */ + Resource->OwnerThreads[0].OwnerThread = 0; + + /* See if the resource isn't being owned anymore */ + ASSERT(Resource->ActiveCount > 0); + if (!(--Resource->ActiveCount)) + { + /* Check if there are shared waiters */ + if (IsSharedWaiting(Resource)) + { + /* Remove the exclusive flag */ + Resource->Flag &= ~ResourceOwnedExclusive; + + /* Give ownage to another thread */ + Count = Resource->NumberOfSharedWaiters; + Resource->ActiveCount = (USHORT)Count; + Resource->NumberOfSharedWaiters = 0; + + /* Release lock and let someone else have it */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + KeReleaseSemaphore(Resource->SharedWaiters, 0, Count, FALSE); + return; + } + else if (IsExclusiveWaiting(Resource)) + { + /* Give exclusive access */ + Resource->OwnerThreads[0].OwnerThread = 1; + Resource->OwnerThreads[0].OwnerCount = 1; + Resource->ActiveCount = 1; + Resource->NumberOfExclusiveWaiters--; + + /* Release the lock and give it away */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + KeSetEventBoostPriority(Resource->ExclusiveWaiters, + (PKTHREAD*) + &Resource->OwnerThreads[0].OwnerThread); + return; + } + + /* Remove the exclusive flag */ + Resource->Flag &= ~ResourceOwnedExclusive; + } + } + else + { + /* Check if we are in the thread list */ + if (Resource->OwnerThreads[1].OwnerThread == Thread) + { + /* Found it, get owner */ + Owner = &Resource->OwnerThreads[1]; + } + else if (Resource->OwnerThreads[0].OwnerThread == Thread) + { + /* Found it on the second list, get owner */ + Owner = &Resource->OwnerThreads[0]; + } + else + { + /* Not in the list, do a full table look up */ + i = ((PKTHREAD)Thread)->ResourceIndex; + Owner = Resource->OwnerTable; + if (!Owner) + { + /* Nobody owns us, bugcheck! */ + KEBUGCHECKEX(RESOURCE_NOT_OWNED, + (ULONG_PTR)Resource, + Thread, + (ULONG_PTR)Resource->OwnerTable, + (ULONG_PTR)2); + } + + /* Check if we're out of the size and don't match */ + if ((i >= Owner->TableSize) || (Owner[i].OwnerThread != Thread)) + { + /* Get the last entry */ + Limit = &Owner[Owner->TableSize]; + for (;;) + { + /* Move to the next entry */ + Owner++; + + /* Check if we don't match */ + if (Owner >= Limit) + { + /* Nobody owns us, bugcheck! */ + KEBUGCHECKEX(RESOURCE_NOT_OWNED, + (ULONG_PTR)Resource, + Thread, + (ULONG_PTR)Resource->OwnerTable, + 2); + } + + /* Check for a match */ + if (Owner->OwnerThread == Thread) break; + } + } + else + { + /* Get the entry directly */ + Owner = &Owner[i]; + } + } + + /* Sanity checks */ + ASSERT(Owner->OwnerThread == Thread); + ASSERT(Owner->OwnerCount > 0); + + /* Check if we are the last owner */ + if (--Owner->OwnerCount) + { + /* Release lock */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return; + } + + /* Clear owner */ + Owner->OwnerThread = 0; + + /* See if the resource isn't being owned anymore */ + ASSERT(Resource->ActiveCount > 0); + if (!(--Resource->ActiveCount)) + { + /* Check if there's an exclusive waiter */ + if (IsExclusiveWaiting(Resource)) + { + /* Give exclusive access */ + Resource->Flag |= ResourceOwnedExclusive; + Resource->OwnerThreads[0].OwnerThread = 1; + Resource->OwnerThreads[0].OwnerCount = 1; + Resource->ActiveCount = 1; + Resource->NumberOfExclusiveWaiters--; + + /* Release the lock and give it away */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + KeSetEventBoostPriority(Resource->ExclusiveWaiters, + (PKTHREAD*) + &Resource->OwnerThreads[0].OwnerThread); + return; + } + } + } + + /* Release lock */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return; +} + +/*++ + * @name ExReleaseResourceForThreadLite + * @implemented NT4 + * + * The ExReleaseResourceForThreadLite routine routine releases + * the input resource of the indicated thread. + * + * @param Resource + * Pointer to the resource to be released. + * + * @param Thread + * Identifies the thread that originally acquired the resource. + * + * @return None. + * + * @remarks Callers of ExReleaseResourceForThreadLite must be running at + * IRQL <= DISPATCH_LEVEL. + * + *--*/ +VOID +NTAPI +ExReleaseResourceForThreadLite(PERESOURCE Resource, + ERESOURCE_THREAD Thread) +{ + ULONG i; + ULONG Count; + KIRQL OldIrql; + POWNER_ENTRY Owner; + ASSERT(Thread != 0); + DPRINT("ExReleaseResourceForThreadLite: %p\n", Resource); + + /* Get the thread and lock the resource */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* Sanity check */ + ExpVerifyResource(Resource); + + /* Check if it's exclusively owned */ + if (IsOwnedExclusive(Resource)) + { + /* Decrement owner count and check if we're done */ + ASSERT(Resource->OwnerThreads[0].OwnerThread == Thread); + ASSERT(Resource->OwnerThreads[0].OwnerCount > 0); + if (--Resource->OwnerThreads[0].OwnerCount) + { + /* Done, release lock! */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return; + } + + /* Clear the owner */ + Resource->OwnerThreads[0].OwnerThread = 0; + + /* See if the resource isn't being owned anymore */ + ASSERT(Resource->ActiveCount > 0); + if (!(--Resource->ActiveCount)) + { + /* Check if there are shared waiters */ + if (IsSharedWaiting(Resource)) + { + /* Remove the exclusive flag */ + Resource->Flag &= ~ResourceOwnedExclusive; + + /* Give ownage to another thread */ + Count = Resource->NumberOfSharedWaiters; + Resource->ActiveCount = (USHORT)Count; + Resource->NumberOfSharedWaiters = 0; + + /* Release lock and let someone else have it */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + KeReleaseSemaphore(Resource->SharedWaiters, 0, Count, FALSE); + return; + } + else if (IsExclusiveWaiting(Resource)) + { + /* Give exclusive access */ + Resource->OwnerThreads[0].OwnerThread = 1; + Resource->OwnerThreads[0].OwnerCount = 1; + Resource->ActiveCount = 1; + Resource->NumberOfExclusiveWaiters--; + + /* Release the lock and give it away */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + KeSetEventBoostPriority(Resource->ExclusiveWaiters, + (PKTHREAD*) + &Resource->OwnerThreads[0].OwnerThread); + return; + } + + /* Remove the exclusive flag */ + Resource->Flag &= ~ResourceOwnedExclusive; + } + } + else + { + /* Check if we are in the thread list */ + if (Resource->OwnerThreads[0].OwnerThread == Thread) + { + /* Found it, get owner */ + Owner = &Resource->OwnerThreads[0]; + } + else if (Resource->OwnerThreads[1].OwnerThread == Thread) + { + /* Found it on the second list, get owner */ + Owner = &Resource->OwnerThreads[1]; + } + else + { + /* Assume no valid index */ + i = 1; + + /* If we got a valid pointer, try to get the resource index */ + if (!((ULONG)Thread & 3)) i = ((PKTHREAD)Thread)->ResourceIndex; + + /* Do a table lookup */ + Owner = Resource->OwnerTable; + ASSERT(Owner != NULL); + + /* Check if we're out of the size and don't match */ + if ((i >= Owner->TableSize) || (Owner[i].OwnerThread != Thread)) + { + /* Get the last entry */ + for (;;) + { + /* Move to the next entry */ + Owner++; + + /* Check for a match */ + if (Owner->OwnerThread == Thread) break; + } + } + else + { + /* Get the entry directly */ + Owner = &Owner[i]; + } + } + + /* Sanity checks */ + ASSERT(Owner->OwnerThread == Thread); + ASSERT(Owner->OwnerCount > 0); + + /* Check if we are the last owner */ + if (!(--Owner->OwnerCount)) + { + /* Release lock */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return; + } + + /* Clear owner */ + Owner->OwnerThread = 0; + + /* See if the resource isn't being owned anymore */ + ASSERT(Resource->ActiveCount > 0); + if (!(--Resource->ActiveCount)) + { + /* Check if there's an exclusive waiter */ + if (IsExclusiveWaiting(Resource)) + { + /* Give exclusive access */ + Resource->OwnerThreads[0].OwnerThread = 1; + Resource->OwnerThreads[0].OwnerCount = 1; + Resource->ActiveCount = 1; + Resource->NumberOfExclusiveWaiters--; + + /* Release the lock and give it away */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + KeSetEventBoostPriority(Resource->ExclusiveWaiters, + (PKTHREAD*) + &Resource->OwnerThreads[0].OwnerThread); + return; + } + } + } + + /* Release lock */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return; +} + +/*++ + * @name ExSetResourceOwnerPointer + * @implemented NT4 + * + * The ExSetResourceOwnerPointer routine routine sets the owner thread + * thread pointer for an executive resource. + * + * @param Resource + * Pointer to the resource whose owner to change. + * + * @param OwnerPointer + * Pointer to an owner thread pointer of type ERESOURCE_THREAD. + * + * @return None. + * + * @remarks ExSetResourceOwnerPointer, used in conjunction with + * ExReleaseResourceForThreadLite, provides a means for one thread + * (acting as an resource manager thread) to acquire and release + * resources for use by another thread (acting as a resource user + * thread). + * + * After calling ExSetResourceOwnerPointer for a specific resource, + * the only other routine that can be called for that resource is + * ExReleaseResourceForThreadLite. + * + * Callers of ExSetResourceOwnerPointer must be running at + * IRQL <= DISPATCH_LEVEL. + * + *--*/ +VOID +NTAPI +ExSetResourceOwnerPointer(IN PERESOURCE Resource, + IN PVOID OwnerPointer) +{ + ERESOURCE_THREAD Thread; + KIRQL OldIrql; + POWNER_ENTRY Owner, ThisOwner; + + /* Sanity check */ + ASSERT((OwnerPointer != 0) && (((ULONG_PTR)OwnerPointer & 3) == 3)); + + /* Get the thread */ + Thread = ExGetCurrentResourceThread(); + + /* Sanity check */ + ExpVerifyResource(Resource); + + /* Lock the resource */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* Check if it's exclusive */ + if (IsOwnedExclusive(Resource)) + { + /* If it's exclusive, set the first entry no matter what */ + ASSERT(Resource->OwnerThreads[0].OwnerThread == Thread); + ASSERT(Resource->OwnerThreads[0].OwnerCount > 0); + Resource->OwnerThreads[0].OwnerThread = (ULONG_PTR)OwnerPointer; + } + else + { + /* Set the thread in both entries */ + ThisOwner = ExpFindEntryForThread(Resource, + (ERESOURCE_THREAD)OwnerPointer, + 0); + Owner = ExpFindEntryForThread(Resource, Thread, 0); + if (!Owner) + { + /* Nobody owns it, crash */ + KEBUGCHECKEX(RESOURCE_NOT_OWNED, + (ULONG_PTR)Resource, + Thread, + (ULONG_PTR)Resource->OwnerTable, + 3); + } + + /* Set if we are the owner */ + if (ThisOwner) + { + /* Update data */ + ThisOwner->OwnerCount += Owner->OwnerCount; + Owner->OwnerCount = 0; + Owner->OwnerThread = 0; + ASSERT(Resource->ActiveCount >= 2); + Resource->ActiveCount--; } } /* Release the resource */ - KeReleaseSpinLock(&Resource->SpinLock, OldIrql); + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); +} + +/*++ + * @name ExTryToAcquireResourceExclusiveLite + * @implemented NT4 + * + * The ExTryToAcquireResourceExclusiveLite routine routine attemps to + * acquire the given resource for exclusive access. + * + * @param Resource + * Pointer to the resource to be acquired. + * + * @return TRUE if the given resource has been acquired for the caller. + * + * @remarks Callers of ExTryToAcquireResourceExclusiveLite must be running at + * IRQL < DISPATCH_LEVEL. + * + *--*/ +BOOLEAN +NTAPI +ExTryToAcquireResourceExclusiveLite(PERESOURCE Resource) +{ + ERESOURCE_THREAD Thread; + KIRQL OldIrql; + BOOLEAN Acquired = FALSE; + DPRINT("ExTryToAcquireResourceExclusiveLite: %p\n", Resource); + + /* Sanity check */ + ASSERT((Resource->Flag & ResourceNeverExclusive) == 0); + + /* Get the thread */ + Thread = ExGetCurrentResourceThread(); + + /* Sanity check and validation */ + ASSERT(KeIsExecutingDpc() == FALSE); + ExpVerifyResource(Resource); + + /* Acquire the lock */ + ExAcquireResourceLock(&Resource->SpinLock, &OldIrql); + + /* Check if there is an owner */ + if (!Resource->ActiveCount) + { + /* No owner, give exclusive access */ + Resource->Flag |= ResourceOwnedExclusive; + Resource->OwnerThreads[0].OwnerThread = Thread; + Resource->OwnerThreads[0].OwnerCount = 1; + Resource->ActiveCount = 1; + Acquired = TRUE; + } + else if ((IsOwnedExclusive(Resource)) && + (Resource->OwnerThreads[0].OwnerThread == Thread)) + { + /* Do a recursive acquire */ + Resource->OwnerThreads[0].OwnerCount++; + Acquired = TRUE; + } + + /* Release the resource */ + ExReleaseResourceLock(&Resource->SpinLock, OldIrql); + return Acquired; +} + +/*++ + * @name ExEnterCriticalRegionAndAcquireResourceExclusive + * @implemented NT5.1 + * + * The ExEnterCriticalRegionAndAcquireResourceExclusive enters a critical + * region and then exclusively acquires a resource. + * + * @param Resource + * Pointer to the resource to acquire. + * + * @return Pointer to the Win32K thread pointer of the current thread. + * + * @remarks See ExAcquireResourceExclusiveLite. + * + *--*/ +PVOID +NTAPI +ExEnterCriticalRegionAndAcquireResourceExclusive(PERESOURCE Resource) +{ + /* Enter critical region */ + KeEnterCriticalRegion(); + + /* Acquire the resource */ + ExAcquireResourceExclusiveLite(Resource, TRUE); + + /* Return the Win32 Thread */ + return KeGetCurrentThread()->Win32Thread; +} + +/*++ + * @name ExReleaseResourceAndLeaveCriticalRegion + * @implemented NT5.1 + * + * The ExReleaseResourceAndLeaveCriticalRegion release a resource and + * then leaves a critical region. + * + * @param Resource + * Pointer to the resource to release. + * + * @return None + * + * @remarks See ExReleaseResourceLite. + * + *--*/ +VOID +FASTCALL +ExReleaseResourceAndLeaveCriticalRegion(PERESOURCE Resource) +{ + /* Release the resource */ + ExReleaseResourceLite(Resource); + + /* Leave critical region */ + KeLeaveCriticalRegion(); } /* EOF */ + diff --git a/reactos/ntoskrnl/include/internal/ex.h b/reactos/ntoskrnl/include/internal/ex.h index 075fbbc6773..714b97c7ca8 100644 --- a/reactos/ntoskrnl/include/internal/ex.h +++ b/reactos/ntoskrnl/include/internal/ex.h @@ -15,6 +15,17 @@ extern POBJECT_TYPE ExEventPairObjectType; ~(EX_HANDLE_ENTRY_PROTECTFROMCLOSE | EX_HANDLE_ENTRY_INHERITABLE | \ EX_HANDLE_ENTRY_AUDITONCLOSE))) +/* Note: we only use a spinlock on SMP. On UP, we cli/sti intead */ +#ifndef CONFIG_SMP +#define ExAcquireResourceLock(l, i) \ + UNREFERENCED_PARAMETER(*i); \ + Ke386DisableInterrupts(); +#define ExReleaseResourceLock(l, i) Ke386EnableInterrupts(); +#else +#define ExAcquireResourceLock(l, i) KeAcquireSpinLock(l, i); +#define ExReleaseResourceLock(l, i) KeReleaseSpinLock(l, i); +#endif + /* INITIALIZATION FUNCTIONS *************************************************/ VOID @@ -77,6 +88,10 @@ VOID STDCALL ExpInitializeProfileImplementation(VOID); +VOID +NTAPI +ExpResourceInitialization(VOID); + /* Rundown Functions ********************************************************/ VOID diff --git a/reactos/ntoskrnl/include/internal/tag.h b/reactos/ntoskrnl/include/internal/tag.h index 4d36fc9d4ac..a7d96d472f7 100644 --- a/reactos/ntoskrnl/include/internal/tag.h +++ b/reactos/ntoskrnl/include/internal/tag.h @@ -10,9 +10,9 @@ #define CALLBACK_TAG TAG('C','L','B','K') /* formerly located in ex/resource.c */ -#define TAG_OWNER_TABLE TAG('R', 'O', 'W', 'N') -#define TAG_EXCLUSIVE_LOCK TAG('E', 'R', 'E', 'L') -#define TAG_SHARED_SEM TAG('E', 'R', 'S', 'S') +#define TAG_RESOURCE_TABLE TAG('R', 'e', 'T', 'a') +#define TAG_RESOURCE_EVENT TAG('R', 'e', 'T', 'a') +#define TAG_RESOURCE_SEMAPHORE TAG('R', 'e', 'T', 'a') /* formerly located in fs/notify.c */ #define FSRTL_NOTIFY_TAG TAG('N','O','T','I') diff --git a/reactos/ntoskrnl/ntoskrnl.mc b/reactos/ntoskrnl/ntoskrnl.mc index e3cde5f3d6e..12987c73379 100644 --- a/reactos/ntoskrnl/ntoskrnl.mc +++ b/reactos/ntoskrnl/ntoskrnl.mc @@ -1003,14 +1003,6 @@ Language=English WORKER_THREAD_RETURNED_AT_BAD_IRQL . -MessageId=0xE4 -Severity=Success -Facility=System -SymbolicName=WORKER_INVALID -Language=English -WORKER_INVALID -. - MessageId=0xE2 Severity=Success Facility=System @@ -1019,6 +1011,22 @@ Language=English MANUALLY_INITIATED_CRASH . +MessageId=0xE3 +Severity=Success +Facility=System +SymbolicName=RESOURCE_NOT_OWNED +Language=English +RESOURCE_NOT_OWNED +. + +MessageId=0xE4 +Severity=Success +Facility=System +SymbolicName=WORKER_INVALID +Language=English +WORKER_INVALID +. + MessageId=0xFA Severity=Success Facility=System @@ -1085,4 +1093,4 @@ Language=English ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY . -; EOF \ No newline at end of file +; EOF diff --git a/reactos/w32api/include/ddk/winddk.h b/reactos/w32api/include/ddk/winddk.h index 8f5f16f553f..6cc7c8d7f7d 100644 --- a/reactos/w32api/include/ddk/winddk.h +++ b/reactos/w32api/include/ddk/winddk.h @@ -6601,7 +6601,7 @@ ExIsResourceAcquiredLite( IN PERESOURCE Resource); NTOSAPI -USHORT +ULONG DDKAPI ExIsResourceAcquiredSharedLite( IN PERESOURCE Resource); @@ -6648,7 +6648,7 @@ ExRegisterCallback( IN PVOID CallbackContext); NTOSAPI -VOID +NTSTATUS DDKAPI ExReinitializeResourceLite( IN PERESOURCE Resource);