/*++ Copyright (c) 1989 Microsoft Corporation Module Name: Resource.c Abstract: This module implements the executive functions to acquire and release a shared resource, that was unfortunately export to ntddk in release 1. Author: Gary D. Kimura [GaryKi] 25-Jun-1989 Environment: Kernel mode only. Revision History: --*/ #include "exp.h" #pragma hdrstop #include // // The following variable, marcos, and procedure are only for debug purposes. // extern LARGE_INTEGER ExpTimeout; // // thirty days worth // extern ULONG ExpResourceTimeoutCount; // // Avoid the aliasing done in ex.h // #undef ExInitializeResource #undef ExAcquireResourceExclusive #undef ExReleaseResourceForThread #undef ExDeleteResource #if !DBG && NT_UP && defined(i386) #define ExReleaseResourceForThread ExpReleaseResourceForThread VOID ExReleaseResourceForThread( IN PNTDDK_ERESOURCE Resource, IN ERESOURCE_THREAD OurThread ); #endif #if DBG VOID ExpAssertResourceDdk ( IN PNTDDK_ERESOURCE Resource ); #define ASSERT_RESOURCE(_Resource) ExpAssertResourceDdk(_Resource) #else #define ASSERT_RESOURCE(_Resource) #endif // // bit value for ERESOURCE.Flag // #define ExclusiveWaiter 0x01 // ** Also in i386\resoura.asm ** #define SharedWaiter 0x02 // ** Also in i386\resoura.asm ** // CounterShiftBit 0x04 - see below #define DisablePriorityBoost 0x08 // ResourceOwnedExclusive 0x80 - from ex.h #if NT_UP #define CounterShiftBit 0x00 #else #define CounterShiftBit 0x04 // Must be 0x04! #endif #define IsExclusiveWaiting(a) (((a)->Flag & ExclusiveWaiter) != 0) #define IsOwnedExclusive(a) (((a)->Flag & ResourceOwnedExclusive) != 0) #define IsOwnedExclusive(a) (((a)->Flag & ResourceOwnedExclusive) != 0) #define IsBoostAllowed(a) (((a)->Flag & DisablePriorityBoost) == 0) // // The following static data and two macors are used to control entering and // exiting the resource critical section. // static KSPIN_LOCK ExpResourceSpinLock; //++ // // Macros: // AcquireResourceLock - Obtains the Resource's spinlock // ReleaseResourceLock - Releases the Resource's spinlock // // WaitForResourceExclusive(Resource, OldIrql) // Block on resource until WakeSharedWaiters // // WakeExclusiveWaiters // Wakes threads on resource which are WaitForResourceShared // //-- #define AcquireResourceLock(_Resource,_Irql) { \ ExAcquireSpinLock( &_Resource->SpinLock, _Irql ); \ ASSERT_RESOURCE( _Resource ); \ } #define ReleaseResourceLock(_Resource,_Irql) { \ ExReleaseSpinLock( &_Resource->SpinLock, _Irql ); \ } #define INITIAL_TABLE_SIZE 4 #define WaitForResourceExclusive(_Resource, _OldIrql) { \ _Resource->Flag |= ExclusiveWaiter; \ _Resource->NumberOfExclusiveWaiters += 1; \ ReleaseResourceLock ( _Resource, _OldIrql ); \ ExpWaitForResourceDdk ( _Resource, &_Resource->ExclusiveWaiters ); \ AcquireResourceLock ( _Resource, &_OldIrql); \ if (--_Resource->NumberOfExclusiveWaiters != 0) { \ _Resource->Flag |= ExclusiveWaiter; \ } \ } #define WakeExclusiveWaiters(_Resource) { \ _Resource->Flag &= ~ExclusiveWaiter; \ KeSetEvent(&_Resource->ExclusiveWaiters, 0, FALSE); \ } // // Local procedure prototypes // VOID ExpWaitForResourceDdk ( IN PNTDDK_ERESOURCE Resource, IN PVOID Object ); // // The following list head points to a list of all currently // initialized Executive Resources in the system. // extern LIST_ENTRY ExpSystemResourcesList; #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,ExpResourceInitialization) #pragma alloc_text(PAGELK,ExQuerySystemLockInformation) #endif // // // // NTSTATUS ExInitializeResource ( IN PNTDDK_ERESOURCE Resource ) /*++ Routine Description: This routine initializes the input resource variable Arguments: Resource - Supplies the resource variable being initialized Return Value: Status of the operation. --*/ { ULONG i; ASSERTMSG("A resource cannot be in paged pool ", MmDeterminePoolType(Resource) != PagedPool); // // Initialize the shared and exclusive waiting counters and semaphore. // The counters indicate how many are waiting for access to the resource // and the semaphores are used to wait on the resource. Note that // the semaphores can also indicate the number waiting for a resource // however there is a race condition in the alogrithm on the acquire // side if count if not updated before the critical section is exited. // So we need to have an outside counter. // Resource->NumberOfSharedWaiters = 0; Resource->NumberOfExclusiveWaiters = 0; KeInitializeSemaphore ( &Resource->SharedWaiters, 0, MAXLONG ); KeInitializeEvent ( &Resource->ExclusiveWaiters, SynchronizationEvent, FALSE ); KeInitializeSpinLock ( &Resource->SpinLock ); Resource->OwnerThreads = Resource->InitialOwnerThreads; Resource->OwnerCounts = Resource->InitialOwnerCounts; Resource->TableSize = INITIAL_TABLE_SIZE; Resource->ActiveCount = 0; Resource->TableRover = 1; Resource->Flag = 0; for(i=0; i < INITIAL_TABLE_SIZE; i++) { Resource->OwnerThreads[i] = 0; Resource->OwnerCounts[i] = 0; } Resource->ContentionCount = 0; InitializeListHead( &Resource->SystemResourcesList ); #if i386 && !FPO if (NtGlobalFlag & FLG_KERNEL_STACK_TRACE_DB) { Resource->CreatorBackTraceIndex = RtlLogStackBackTrace(); } else { Resource->CreatorBackTraceIndex = 0; } #endif // i386 && !FPO if (Resource >= (PNTDDK_ERESOURCE)MM_USER_PROBE_ADDRESS) { ExInterlockedInsertTailList ( &ExpSystemResourcesList, &Resource->SystemResourcesList, &ExpResourceSpinLock ); } return STATUS_SUCCESS; } BOOLEAN ExAcquireResourceExclusive( IN PNTDDK_ERESOURCE Resource, IN BOOLEAN Wait ) /*++ Routine Description: The routine acquires the resource for exclusive access. Upon return from the procedure the resource is acquired for exclusive access. Arguments: Resource - Supplies the resource to acquire Wait - Indicates if the call is allowed to wait for the resource to become available for must return immediately Return Value: BOOLEAN - TRUE if the resource is acquired and FALSE otherwise --*/ { KIRQL OldIrql; ERESOURCE_THREAD OurThread; ASSERTMSG("Routine cannot be called at DPC ", !KeIsExecutingDpc() ); ASSERT((Resource->Flag & ResourceNeverExclusive) == 0); OurThread = (ULONG)ExGetCurrentResourceThread(); // // Get exclusive access to this resource structure // AcquireResourceLock( Resource, &OldIrql ); // // Loop until the resource is ours or exit if we cannot wait. // while (TRUE) { if (Resource->ActiveCount == 0) { // // Resource is un-ownded, obtain it exclusive // Resource->InitialOwnerThreads[0] = OurThread; Resource->OwnerThreads[0] = OurThread; Resource->OwnerCounts[0] = 1; Resource->ActiveCount = 1; Resource->Flag |= ResourceOwnedExclusive; ReleaseResourceLock ( Resource, OldIrql ); return TRUE; } if (IsOwnedExclusive(Resource) && Resource->OwnerThreads[0] == OurThread) { // // Our thread is already the exclusive resource owner // Resource->OwnerCounts[0] += 1; ReleaseResourceLock( Resource, OldIrql ); return TRUE; } // // Check if we are allowed to wait or must return immedately, and // indicate that we didn't acquire the resource // if (!Wait) { ReleaseResourceLock( Resource, OldIrql ); return FALSE; } // // Otherwise we need to wait to acquire the resource. // WaitForResourceExclusive ( Resource, OldIrql ); } } VOID ExReleaseResourceForThread( IN PNTDDK_ERESOURCE Resource, IN ERESOURCE_THREAD OurThread ) /*++ Routine Description: This routine release the input resource the indcated thread. The resource can have been acquired for either shared or exclusive access. Arguments: Resource - Supplies the resource to release Thread - Supplies the thread that originally acquired the resource Return Value: None. --*/ { KIRQL OldIrql; ASSERT( OurThread != 0 ); // // Get exclusive access to this resource structure // AcquireResourceLock( Resource, &OldIrql ); ASSERT( Resource->OwnerThreads[0] == OurThread ); // // OwnerThread[0] is optimized for resources which get // single users. We check it first, plus we clear the // threadid if the counts goes to zero // if (--Resource->OwnerCounts[0] != 0) { ReleaseResourceLock( Resource, OldIrql ); return; } Resource->OwnerThreads[0] = 0; Resource->InitialOwnerThreads[0] = 0; // // Thread's count went to zero, lower the resource's active count. // Resource->ActiveCount -= 1; ASSERT( Resource->ActiveCount == 0 ); // // Resource's activecount went to zero - clear possible exclusive // owner bit, and wake any waiters // if (IsExclusiveWaiting(Resource)) { WakeExclusiveWaiters ( Resource ); } // // No longer owned exclusive // Resource->Flag &= ~ResourceOwnedExclusive; ReleaseResourceLock( Resource, OldIrql ); return; } NTSTATUS ExDeleteResource ( IN PNTDDK_ERESOURCE Resource ) /*++ Routine Description: This routine deletes (i.e., uninitializes) the input resource variable Arguments: Resource - Supplies the resource variable being deleted Return Value: None --*/ { ASSERTMSG("Routine cannot be called at DPC ", !KeIsExecutingDpc() ); ASSERT_RESOURCE( Resource ); ASSERT( !IsExclusiveWaiting(Resource) ); if (Resource >= (PNTDDK_ERESOURCE)MM_USER_PROBE_ADDRESS) { KIRQL OldIrql; ExAcquireSpinLock( &ExpResourceSpinLock, &OldIrql ); RemoveEntryList( &Resource->SystemResourcesList ); ExReleaseSpinLock( &ExpResourceSpinLock, OldIrql ); } return STATUS_SUCCESS; } VOID ExpWaitForResourceDdk ( IN PNTDDK_ERESOURCE Resource, IN PVOID Object ) /*++ Routine Description: The routine waits on the Resource's object to be set. If the wait is too long the current owners of the resoruce are boosted in priority. Arguments: Resource - Supplies the resource Object - Event or Semaphore to wait on Return Value: None --*/ { KIRQL OldIrql; NTSTATUS Status; ULONG i; Resource->ContentionCount += 1; i = 0; for (; ;) { Status = KeWaitForSingleObject ( Object, Executive, KernelMode, FALSE, &ExpTimeout ); if (Status != STATUS_TIMEOUT) { break; } if (i++ >= ExpResourceTimeoutCount) { i = 0; DbgPrint("Resource @ %lx\n", Resource); DbgPrint(" ActiveCount = %04lx Flags = %s%s%s\n", Resource->ActiveCount, IsOwnedExclusive(Resource) ? "IsOwnedExclusive " : "", "", IsExclusiveWaiting(Resource) ? "ExclusiveWaiter " : "" ); DbgPrint(" NumberOfExclusiveWaiters = %04lx\n", Resource->NumberOfExclusiveWaiters); DbgPrint(" Thread = %08lx, Count = %02x\n", Resource->OwnerThreads[0], Resource->OwnerCounts[0] ); DbgBreakPoint(); DbgPrint("EX - Rewaiting\n"); } // // Give owning thread(s) a priority boost // AcquireResourceLock( Resource, &OldIrql ); if (IsBoostAllowed(Resource) && IsOwnedExclusive(Resource)) { // // Only one thread, boost it // KeBoostPriorityThread((PKTHREAD) Resource->OwnerThreads[0], ERESOURCE_INCREMENT ); } ReleaseResourceLock( Resource, OldIrql ); // // Loop and wait some more // } // // Wait was statisfied // ASSERT(NT_SUCCESS(Status)); return ; } #if DBG VOID ExpAssertResourceDdk ( IN PNTDDK_ERESOURCE Resource ) { USHORT Sum; // // Assert internal structures headers are correct // ASSERT(Resource->SharedWaiters.Header.Type == SemaphoreObject); ASSERT(Resource->SharedWaiters.Header.Size == sizeof(KSEMAPHORE)); ASSERT(Resource->ExclusiveWaiters.Header.Type == SynchronizationEvent); ASSERT(Resource->ExclusiveWaiters.Header.Size == sizeof(KEVENT)); // // Count number of currently owned threads // Sum = Resource->OwnerCounts[0]; // // Verify sum is consistent with what's in the resource // ASSERT(Resource->ActiveCount == Sum); if (Sum == 0) { ASSERT(!IsOwnedExclusive(Resource)); ASSERT(Resource->OwnerThreads[0] == 0); ASSERT(Resource->InitialOwnerThreads[0] == 0); } if (Sum > 1) { ASSERT(!IsOwnedExclusive(Resource)); } // // Verify resource flags appear correct // if (IsOwnedExclusive(Resource)) { ASSERT (Sum == 1); ASSERT (Resource->OwnerCounts[0] != 0); ASSERT (Resource->OwnerThreads[0] == Resource->InitialOwnerThreads[0]); } } #endif // dbg