/*++ Copyright (c) 1989-1993 Microsoft Corporation Module Name: Resource.c Abstract: This module implements the executive functions to acquire and release a shared resource. Author: Mark Lucovsky (markl) 04-Aug-1989 Environment: These routines are statically linked in the caller's executable and are callable in only from user mode. They make use of Nt system services. Revision History: --*/ #include #include #if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_) #define InterlockedDecrement _InterlockedDecrement LONG InterlockedDecrement( PLONG Addend ); #pragma intrinsic(_InterlockedDecrement) #else __inline VOID _fastcall InterlockedDecrement( IN PLONG Addend ) { __asm { mov eax, -1 mov ecx, Addend lock xadd [ecx], eax dec eax } } #endif // // Define the desired access for semaphores. // #define DESIRED_SEMAPHORE_ACCESS \ (SEMAPHORE_QUERY_STATE | SEMAPHORE_MODIFY_STATE | SYNCHRONIZE) VOID RtlDumpResource( IN PRTL_RESOURCE Resource ); extern BOOLEAN LdrpShutdownInProgress; extern HANDLE LdrpShutdownThreadId; VOID RtlpInitDeferedCriticalSection( VOID ); RTL_CRITICAL_SECTION DeferedCriticalSection; #if DBG BOOLEAN ProtectHandle( HANDLE hObject ) { NTSTATUS Status; OBJECT_HANDLE_FLAG_INFORMATION HandleInfo; Status = NtQueryObject( hObject, ObjectHandleFlagInformation, &HandleInfo, sizeof( HandleInfo ), NULL ); if (NT_SUCCESS( Status )) { HandleInfo.ProtectFromClose = TRUE; Status = NtSetInformationObject( hObject, ObjectHandleFlagInformation, &HandleInfo, sizeof( HandleInfo ) ); if (NT_SUCCESS( Status )) { return TRUE; } } return FALSE; } BOOLEAN UnProtectHandle( HANDLE hObject ) { NTSTATUS Status; OBJECT_HANDLE_FLAG_INFORMATION HandleInfo; Status = NtQueryObject( hObject, ObjectHandleFlagInformation, &HandleInfo, sizeof( HandleInfo ), NULL ); if (NT_SUCCESS( Status )) { HandleInfo.ProtectFromClose = FALSE; Status = NtSetInformationObject( hObject, ObjectHandleFlagInformation, &HandleInfo, sizeof( HandleInfo ) ); if (NT_SUCCESS( Status )) { return TRUE; } } return FALSE; } #endif // DBG RTL_CRITICAL_SECTION_DEBUG RtlpStaticDebugInfo[ 16 ]; PRTL_CRITICAL_SECTION_DEBUG RtlpDebugInfoFreeList; BOOLEAN RtlpCritSectInitialized; PRTL_CRITICAL_SECTION_DEBUG RtlpChainDebugInfo( IN PVOID BaseAddress, IN ULONG Size ) { PRTL_CRITICAL_SECTION_DEBUG p, p1; if (Size = Size / sizeof( RTL_CRITICAL_SECTION_DEBUG )) { p = (PRTL_CRITICAL_SECTION_DEBUG)BaseAddress + Size - 1; *(PRTL_CRITICAL_SECTION_DEBUG *)p = NULL; while (--Size) { p1 = p - 1; *(PRTL_CRITICAL_SECTION_DEBUG *)p1 = p; p = p1; } } return p; } PVOID RtlpAllocateDebugInfo( VOID ); VOID RtlpFreeDebugInfo( IN PVOID DebugInfo ); PVOID RtlpAllocateDebugInfo( VOID ) { PRTL_CRITICAL_SECTION_DEBUG p; NTSTATUS Status; ULONG Size; if (RtlpCritSectInitialized) { RtlEnterCriticalSection(&DeferedCriticalSection); } try { p = RtlpDebugInfoFreeList; if (p == NULL) { Size = sizeof( RTL_CRITICAL_SECTION_DEBUG ); Status = NtAllocateVirtualMemory( NtCurrentProcess(), (PVOID *)&p, 0, &Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ); if (!NT_SUCCESS( Status )) { KdPrint(( "NTDLL: Unable to allocate debug information - Status == %x\n", Status )); } else { p = RtlpChainDebugInfo( p, Size ); } } if (p != NULL) { RtlpDebugInfoFreeList = *(PRTL_CRITICAL_SECTION_DEBUG *)p; } } finally { if (RtlpCritSectInitialized) { RtlLeaveCriticalSection(&DeferedCriticalSection); } } return p; } VOID RtlpFreeDebugInfo( IN PVOID DebugInfo ) { RtlEnterCriticalSection(&DeferedCriticalSection); try { RtlZeroMemory( DebugInfo, sizeof( RTL_CRITICAL_SECTION_DEBUG ) ); *(PRTL_CRITICAL_SECTION_DEBUG *)DebugInfo = RtlpDebugInfoFreeList; RtlpDebugInfoFreeList = (PRTL_CRITICAL_SECTION_DEBUG)DebugInfo; } finally { RtlLeaveCriticalSection(&DeferedCriticalSection); } return; } VOID RtlpCreateCriticalSectionSem( IN PRTL_CRITICAL_SECTION CriticalSection ); VOID RtlpInitDeferedCriticalSection( VOID ) { if (sizeof( RTL_CRITICAL_SECTION_DEBUG ) != sizeof( RTL_RESOURCE_DEBUG )) { DbgPrint( "NTDLL: Critical Section & Resource Debug Info length mismatch.\n" ); return; } RtlpDebugInfoFreeList = RtlpChainDebugInfo( RtlpStaticDebugInfo, sizeof( RtlpStaticDebugInfo ) ); RtlInitializeCriticalSection(&DeferedCriticalSection); RtlpCreateCriticalSectionSem(&DeferedCriticalSection); RtlpCritSectInitialized = TRUE; return; } BOOLEAN NtdllOkayToLockRoutine( IN PVOID Lock ) { return TRUE; } VOID RtlInitializeResource( IN PRTL_RESOURCE Resource ) /*++ Routine Description: This routine initializes the input resource variable Arguments: Resource - Supplies the resource variable being initialized Return Value: None --*/ { NTSTATUS Status; PRTL_RESOURCE_DEBUG ResourceDebugInfo; // // Initialize the lock fields, the count indicates how many are waiting // to enter or are in the critical section, LockSemaphore is the object // to wait on when entering the critical section. SpinLock is used // for the add interlock instruction. // Status = RtlInitializeCriticalSection( &Resource->CriticalSection ); if ( !NT_SUCCESS(Status) ){ RtlRaiseStatus(Status); } Resource->CriticalSection.DebugInfo->Type = RTL_RESOURCE_TYPE; ResourceDebugInfo = (PRTL_RESOURCE_DEBUG) RtlpAllocateDebugInfo(); if (ResourceDebugInfo == NULL) { RtlRaiseStatus(STATUS_NO_MEMORY); } ResourceDebugInfo->ContentionCount = 0; Resource->DebugInfo = ResourceDebugInfo; // // Initialize flags so there is a default value. // (Some apps may set RTL_RESOURCE_FLAGS_LONG_TERM to affect timeouts.) // Resource->Flags = 0; // // 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. // Status = NtCreateSemaphore( &Resource->SharedSemaphore, DESIRED_SEMAPHORE_ACCESS, NULL, 0, MAXLONG ); if ( !NT_SUCCESS(Status) ){ RtlRaiseStatus(Status); } Resource->NumberOfWaitingShared = 0; Status = NtCreateSemaphore( &Resource->ExclusiveSemaphore, DESIRED_SEMAPHORE_ACCESS, NULL, 0, MAXLONG ); if ( !NT_SUCCESS(Status) ){ RtlRaiseStatus(Status); } Resource->NumberOfWaitingExclusive = 0; // // Initialize the current state of the resource // Resource->NumberOfActive = 0; Resource->ExclusiveOwnerThread = NULL; return; } BOOLEAN RtlAcquireResourceShared( IN PRTL_RESOURCE Resource, IN BOOLEAN Wait ) /*++ Routine Description: The routine acquires the resource for shared access. Upon return from the procedure the resource is acquired for shared 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 --*/ { NTSTATUS Status; ULONG TimeoutCount = 0; PLARGE_INTEGER TimeoutTime = &RtlpTimeout; // // Enter the critical section // RtlEnterCriticalSection(&Resource->CriticalSection); // // If it is not currently acquired for exclusive use then we can acquire // the resource for shared access. Note that this can potentially // starve an exclusive waiter however, this is necessary given the // ability to recursively acquire the resource shared. Otherwise we // might/will reach a deadlock situation where a thread tries to acquire // the resource recusively shared but is blocked by an exclusive waiter. // // The test to reanable not starving an exclusive waiter is: // // if ((Resource->NumberOfWaitingExclusive == 0) && // (Resource->NumberOfActive >= 0)) { // if (Resource->NumberOfActive >= 0) { // // The resource is ours, so indicate that we have it and // exit the critical section // Resource->NumberOfActive += 1; RtlLeaveCriticalSection(&Resource->CriticalSection); // // Otherwise check to see if this thread is the one currently holding // exclusive access to the resource. And if it is then we change // this shared request to an exclusive recusive request and grant // access to the resource. // } else if (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread) { // // The resource is ours (recusively) so indicate that we have it // and exit the critial section // Resource->NumberOfActive -= 1; RtlLeaveCriticalSection(&Resource->CriticalSection); // // Otherwise we'll have to wait for access. // } else { // // Check if we are allowed to wait or must return immedately, and // indicate that we didn't acquire the resource // if (!Wait) { RtlLeaveCriticalSection(&Resource->CriticalSection); return FALSE; } // // Otherwise we need to wait to acquire the resource. // To wait we will increment the number of waiting shared, // release the lock, and wait on the shared semaphore // Resource->NumberOfWaitingShared += 1; Resource->DebugInfo->ContentionCount++; RtlLeaveCriticalSection(&Resource->CriticalSection); rewait: if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) { TimeoutTime = NULL; } Status = NtWaitForSingleObject( Resource->SharedSemaphore, FALSE, TimeoutTime ); if ( Status == STATUS_TIMEOUT ) { DbgPrint("RTL: Acquire Shared Sem Timeout %d(2 minutes)\n",TimeoutCount); DbgPrint("RTL: Resource at %lx\n",Resource); TimeoutCount++; if ( TimeoutCount > 2 ) { PIMAGE_NT_HEADERS NtHeaders; // // If the image is a Win32 image, then raise an exception and try to get to the // uae popup // NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); if (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { EXCEPTION_RECORD ExceptionRecord; ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK; ExceptionRecord.ExceptionFlags = 0; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.ExceptionAddress = (PVOID)RtlRaiseException; ExceptionRecord.NumberParameters = 1; ExceptionRecord.ExceptionInformation[0] = (ULONG)Resource; RtlRaiseException(&ExceptionRecord); } else { DbgBreakPoint(); } } DbgPrint("RTL: Re-Waiting\n"); goto rewait; } if ( !NT_SUCCESS(Status) ) { RtlRaiseStatus(Status); } } // // Now the resource is ours, for shared access // return TRUE; } BOOLEAN RtlAcquireResourceExclusive( IN PRTL_RESOURCE 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 --*/ { NTSTATUS Status; ULONG TimeoutCount = 0; PLARGE_INTEGER TimeoutTime = &RtlpTimeout; // // Loop until the resource is ours or exit if we cannot wait. // while (TRUE) { // // Enter the critical section // RtlEnterCriticalSection(&Resource->CriticalSection); // // If there are no shared users and it is not currently acquired for // exclusive use then we can acquire the resource for exclusive // access. We also can acquire it if the resource indicates exclusive // access but there isn't currently an owner. // if ((Resource->NumberOfActive == 0) || ((Resource->NumberOfActive == -1) && (Resource->ExclusiveOwnerThread == NULL))) { // // The resource is ours, so indicate that we have it and // exit the critical section // Resource->NumberOfActive = -1; Resource->ExclusiveOwnerThread = NtCurrentTeb()->ClientId.UniqueThread; RtlLeaveCriticalSection(&Resource->CriticalSection); return TRUE; } // // Otherwise check to see if we already have exclusive access to the // resource and can simply recusively acquire it again. // if (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread) { // // The resource is ours (recusively) so indicate that we have it // and exit the critial section // Resource->NumberOfActive -= 1; RtlLeaveCriticalSection(&Resource->CriticalSection); return TRUE; } // // Check if we are allowed to wait or must return immedately, and // indicate that we didn't acquire the resource // if (!Wait) { RtlLeaveCriticalSection(&Resource->CriticalSection); return FALSE; } // // Otherwise we need to wait to acquire the resource. // To wait we will increment the number of waiting exclusive, // release the lock, and wait on the exclusive semaphore // Resource->NumberOfWaitingExclusive += 1; Resource->DebugInfo->ContentionCount++; RtlLeaveCriticalSection(&Resource->CriticalSection); rewait: if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) { TimeoutTime = NULL; } Status = NtWaitForSingleObject( Resource->ExclusiveSemaphore, FALSE, TimeoutTime ); if ( Status == STATUS_TIMEOUT ) { DbgPrint("RTL: Acquire Exclusive Sem Timeout %d (2 minutes)\n",TimeoutCount); DbgPrint("RTL: Resource at %lx\n",Resource); TimeoutCount++; if ( TimeoutCount > 2 ) { PIMAGE_NT_HEADERS NtHeaders; // // If the image is a Win32 image, then raise an exception and try to get to the // uae popup // NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); if (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { EXCEPTION_RECORD ExceptionRecord; ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK; ExceptionRecord.ExceptionFlags = 0; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.ExceptionAddress = (PVOID)RtlRaiseException; ExceptionRecord.NumberParameters = 1; ExceptionRecord.ExceptionInformation[0] = (ULONG)Resource; RtlRaiseException(&ExceptionRecord); } else { DbgBreakPoint(); } } DbgPrint("RTL: Re-Waiting\n"); goto rewait; } if ( !NT_SUCCESS(Status) ) { RtlRaiseStatus(Status); } } } VOID RtlReleaseResource( IN PRTL_RESOURCE Resource ) /*++ Routine Description: This routine release the input resource. The resource can have been acquired for either shared or exclusive access. Arguments: Resource - Supplies the resource to release Return Value: None. --*/ { NTSTATUS Status; LONG PreviousCount; // // Enter the critical section // RtlEnterCriticalSection(&Resource->CriticalSection); // // Test if the resource is acquired for shared or exclusive access // if (Resource->NumberOfActive > 0) { // // Releasing shared access to the resource, so decrement // the number of shared users // Resource->NumberOfActive -= 1; // // If the resource is now available and there is a waiting // exclusive user then give the resource to the waiting thread // if ((Resource->NumberOfActive == 0) && (Resource->NumberOfWaitingExclusive > 0)) { // // Set the resource state to exclusive (but not owned), // decrement the number of waiting exclusive, and release // one exclusive waiter // Resource->NumberOfActive = -1; Resource->ExclusiveOwnerThread = NULL; Resource->NumberOfWaitingExclusive -= 1; Status = NtReleaseSemaphore( Resource->ExclusiveSemaphore, 1, &PreviousCount ); if ( !NT_SUCCESS(Status) ) { RtlRaiseStatus(Status); } } } else if (Resource->NumberOfActive < 0) { // // Releasing exclusive access to the resource, so increment the // number of active by one. And continue testing only // if the resource is now available. // Resource->NumberOfActive += 1; if (Resource->NumberOfActive == 0) { // // The resource is now available. Remove ourselves as the // owner thread // Resource->ExclusiveOwnerThread = NULL; // // If there is another waiting exclusive then give the resource // to it. // if (Resource->NumberOfWaitingExclusive > 0) { // // Set the resource to exclusive, and its owner undefined. // Decrement the number of waiting exclusive and release one // exclusive waiter // Resource->NumberOfActive = -1; Resource->NumberOfWaitingExclusive -= 1; Status = NtReleaseSemaphore( Resource->ExclusiveSemaphore, 1, &PreviousCount ); if ( !NT_SUCCESS(Status) ) { RtlRaiseStatus(Status); } // // Check to see if there are waiting shared, who should now get // the resource // } else if (Resource->NumberOfWaitingShared > 0) { // // Set the new state to indicate that all of the shared // requesters have access and there are no more waiting // shared requesters, and then release all of the shared // requsters // Resource->NumberOfActive = Resource->NumberOfWaitingShared; Resource->NumberOfWaitingShared = 0; Status = NtReleaseSemaphore( Resource->SharedSemaphore, Resource->NumberOfActive, &PreviousCount ); if ( !NT_SUCCESS(Status) ) { RtlRaiseStatus(Status); } } } #if DBG } else { // // The resource isn't current acquired, there is nothing to release // so tell the user the mistake // DbgPrint("NTDLL - Resource released too many times %lx\n", Resource); DbgBreakPoint(); #endif } // // Exit the critical section, and return to the caller // RtlLeaveCriticalSection(&Resource->CriticalSection); return; } VOID RtlConvertSharedToExclusive( IN PRTL_RESOURCE Resource ) /*++ Routine Description: This routine converts a resource acquired for shared access into one acquired for exclusive access. Upon return from the procedure the resource is acquired for exclusive access Arguments: Resource - Supplies the resource to acquire for shared access, it must already be acquired for shared access Return Value: None --*/ { NTSTATUS Status; ULONG TimeoutCount = 0; // // Enter the critical section // RtlEnterCriticalSection(&Resource->CriticalSection); // // If there is only one shared user (it's us) and we can acquire the // resource for exclusive access. // if (Resource->NumberOfActive == 1) { // // The resource is ours, so indicate that we have it and // exit the critical section, and return // Resource->NumberOfActive = -1; Resource->ExclusiveOwnerThread = NtCurrentTeb()->ClientId.UniqueThread; RtlLeaveCriticalSection(&Resource->CriticalSection); return; } // // If the resource is currently acquired exclusive and it's us then // we already have exclusive access // if ((Resource->NumberOfActive < 0) && (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread)) { // // We already have exclusive access to the resource so we'll just // exit the critical section and return // RtlLeaveCriticalSection(&Resource->CriticalSection); return; } // // If the resource is acquired by more than one shared then we need // to wait to get exclusive access to the resource // if (Resource->NumberOfActive > 1) { // // To wait we will decrement the fact that we have the resource for // shared, and then loop waiting on the exclusive lock, and then // testing to see if we can get exclusive access to the resource // Resource->NumberOfActive -= 1; while (TRUE) { // // Increment the number of waiting exclusive, exit and critical // section and wait on the exclusive semaphore // Resource->NumberOfWaitingExclusive += 1; Resource->DebugInfo->ContentionCount++; RtlLeaveCriticalSection(&Resource->CriticalSection); rewait: Status = NtWaitForSingleObject( Resource->ExclusiveSemaphore, FALSE, &RtlpTimeout ); if ( Status == STATUS_TIMEOUT ) { DbgPrint("RTL: Convert Exclusive Sem Timeout %d (2 minutes)\n",TimeoutCount); DbgPrint("RTL: Resource at %lx\n",Resource); TimeoutCount++; if ( TimeoutCount > 2 ) { PIMAGE_NT_HEADERS NtHeaders; // // If the image is a Win32 image, then raise an exception and try to get to the // uae popup // NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); if (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { EXCEPTION_RECORD ExceptionRecord; ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK; ExceptionRecord.ExceptionFlags = 0; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.ExceptionAddress = (PVOID)RtlRaiseException; ExceptionRecord.NumberParameters = 1; ExceptionRecord.ExceptionInformation[0] = (ULONG)Resource; RtlRaiseException(&ExceptionRecord); } else { DbgBreakPoint(); } } DbgPrint("RTL: Re-Waiting\n"); goto rewait; } if ( !NT_SUCCESS(Status) ) { RtlRaiseStatus(Status); } // // Enter the critical section // RtlEnterCriticalSection(&Resource->CriticalSection); // // If there are no shared users and it is not currently acquired // for exclusive use then we can acquire the resource for // exclusive access. We can also acquire it if the resource // indicates exclusive access but there isn't currently an owner // if ((Resource->NumberOfActive == 0) || ((Resource->NumberOfActive == -1) && (Resource->ExclusiveOwnerThread == NULL))) { // // The resource is ours, so indicate that we have it and // exit the critical section and return. // Resource->NumberOfActive = -1; Resource->ExclusiveOwnerThread = NtCurrentTeb()->ClientId.UniqueThread; RtlLeaveCriticalSection(&Resource->CriticalSection); return; } // // Otherwise check to see if we already have exclusive access to // the resource and can simply recusively acquire it again. // if (Resource->ExclusiveOwnerThread == NtCurrentTeb()->ClientId.UniqueThread) { // // The resource is ours (recusively) so indicate that we have // it and exit the critical section and return. // Resource->NumberOfActive -= 1; RtlLeaveCriticalSection(&Resource->CriticalSection); return; } } } // // The resource is not currently acquired for shared so this is a // spurious call // #if DBG DbgPrint("NTDLL: Failed error - SHARED_RESOURCE_CONV_ERROR\n"); DbgBreakPoint(); #endif } VOID RtlConvertExclusiveToShared( IN PRTL_RESOURCE Resource ) /*++ Routine Description: This routine converts a resource acquired for exclusive access into one acquired for shared access. Upon return from the procedure the resource is acquired for shared access Arguments: Resource - Supplies the resource to acquire for shared access, it must already be acquired for exclusive access Return Value: None --*/ { LONG PreviousCount; NTSTATUS Status; // // Enter the critical section // RtlEnterCriticalSection(&Resource->CriticalSection); // // If there is only one shared user (it's us) and we can acquire the // resource for exclusive access. // if (Resource->NumberOfActive == -1) { Resource->ExclusiveOwnerThread = NULL; // // Check to see if there are waiting shared, who should now get the // resource along with us // if (Resource->NumberOfWaitingShared > 0) { // // Set the new state to indicate that all of the shared requesters // have access including us, and there are no more waiting shared // requesters, and then release all of the shared requsters // Resource->NumberOfActive = Resource->NumberOfWaitingShared + 1; Resource->NumberOfWaitingShared = 0; Status = NtReleaseSemaphore( Resource->SharedSemaphore, Resource->NumberOfActive - 1, &PreviousCount ); if ( !NT_SUCCESS(Status) ) { RtlRaiseStatus(Status); } } else { // // There is no one waiting for shared access so it's only ours // Resource->NumberOfActive = 1; } RtlLeaveCriticalSection(&Resource->CriticalSection); return; } // // The resource is not currently acquired for exclusive, or we've // recursively acquired it, so this must be a spurious call // #if DBG DbgPrint("NTDLL: Failed error - SHARED_RESOURCE_CONV_ERROR\n"); DbgBreakPoint(); #endif } VOID RtlDeleteResource ( IN PRTL_RESOURCE Resource ) /*++ Routine Description: This routine deletes (i.e., uninitializes) the input resource variable Arguments: Resource - Supplies the resource variable being deleted Return Value: None --*/ { RtlDeleteCriticalSection( &Resource->CriticalSection ); NtClose(Resource->SharedSemaphore); NtClose(Resource->ExclusiveSemaphore); RtlpFreeDebugInfo( Resource->DebugInfo ); RtlZeroMemory( Resource, sizeof( *Resource ) ); return; } VOID RtlDumpResource( IN PRTL_RESOURCE Resource ) { DbgPrint("Resource @ %lx\n", Resource); DbgPrint(" NumberOfWaitingShared = %lx\n", Resource->NumberOfWaitingShared); DbgPrint(" NumberOfWaitingExclusive = %lx\n", Resource->NumberOfWaitingExclusive); DbgPrint(" NumberOfActive = %lx\n", Resource->NumberOfActive); return; } NTSTATUS RtlInitializeCriticalSection( IN PRTL_CRITICAL_SECTION CriticalSection ) /*++ Routine Description: This routine initializes the input critial section variable Arguments: CriticalSection - Supplies the resource variable being initialized Return Value: TBD - Status of semaphore creation. --*/ { PRTL_CRITICAL_SECTION_DEBUG DebugInfo; // // Initialize the lock fields, the count indicates how many are waiting // to enter or are in the critical section, LockSemaphore is the object // to wait on when entering the critical section. SpinLock is used // for the add interlock instruction. Recursion count is the number of // times the critical section has been recursively entered. // CriticalSection->LockCount = -1; CriticalSection->RecursionCount = 0; CriticalSection->OwningThread = 0; CriticalSection->LockSemaphore = 0; // // Initialize debugging information. // DebugInfo = (PRTL_CRITICAL_SECTION_DEBUG)RtlpAllocateDebugInfo(); if (DebugInfo == NULL) { return STATUS_NO_MEMORY; } DebugInfo->Type = RTL_CRITSECT_TYPE; DebugInfo->ContentionCount = 0; DebugInfo->EntryCount = 0; // // If the critical section lock itself is not being initialized, then // synchronize the insert of the critical section in the process locks // list. Otherwise, insert the critical section with no synchronization. // if ((CriticalSection != &RtlCriticalSectionLock) && (RtlpCritSectInitialized != FALSE)) { RtlEnterCriticalSection(&RtlCriticalSectionLock); InsertTailList(&RtlCriticalSectionList, &DebugInfo->ProcessLocksList); RtlLeaveCriticalSection(&RtlCriticalSectionLock ); } else { InsertTailList(&RtlCriticalSectionList, &DebugInfo->ProcessLocksList); } DebugInfo->CriticalSection = CriticalSection; CriticalSection->DebugInfo = DebugInfo; #if i386 DebugInfo->CreatorBackTraceIndex = (USHORT)RtlLogStackBackTrace(); #endif // i386 return STATUS_SUCCESS; } VOID RtlpCreateCriticalSectionSem( IN PRTL_CRITICAL_SECTION CriticalSection ) { NTSTATUS Status; Status = NtCreateSemaphore( &CriticalSection->LockSemaphore, DESIRED_SEMAPHORE_ACCESS, NULL, 0, MAXLONG ); if ( !NT_SUCCESS(Status) ) { InterlockedDecrement(&CriticalSection->LockCount); KdPrint(( "NTDLL: Warning. Unable to allocate lock semaphore for Cs %x. Undoing lock and raising %x\n", CriticalSection,Status )); RtlRaiseStatus(Status); } #if DBG ProtectHandle(CriticalSection->LockSemaphore); #endif // DBG } VOID RtlpCheckDeferedCriticalSection( IN PRTL_CRITICAL_SECTION CriticalSection ) { RtlEnterCriticalSection(&DeferedCriticalSection); try { if ( !CriticalSection->LockSemaphore ) { RtlpCreateCriticalSectionSem(CriticalSection); } } finally { RtlLeaveCriticalSection(&DeferedCriticalSection); } return; } NTSTATUS RtlDeleteCriticalSection( IN PRTL_CRITICAL_SECTION CriticalSection ) /*++ Routine Description: This routine deletes (i.e., uninitializes) the input critical section variable Arguments: CriticalSection - Supplies the resource variable being deleted Return Value: TBD - Status of semaphore close. --*/ { NTSTATUS Status; PRTL_CRITICAL_SECTION_DEBUG DebugInfo; if ( CriticalSection->LockSemaphore ) { #if DBG UnProtectHandle( CriticalSection->LockSemaphore ); #endif // DBG Status = NtClose( CriticalSection->LockSemaphore ); } else { Status = STATUS_SUCCESS; } // // Remove critical section from the list // RtlEnterCriticalSection( &RtlCriticalSectionLock ); DebugInfo = CriticalSection->DebugInfo; if (DebugInfo != NULL) { RemoveEntryList( &DebugInfo->ProcessLocksList ); RtlZeroMemory( DebugInfo, sizeof( *DebugInfo ) ); } RtlLeaveCriticalSection( &RtlCriticalSectionLock ); if (DebugInfo != NULL) { RtlpFreeDebugInfo( DebugInfo ); } RtlZeroMemory( CriticalSection, sizeof( *CriticalSection ) ); return Status; } // // The following support routines are called from the machine language // implementations of RtlEnterCriticalSection and RtlLeaveCriticalSection // to execute the slow path logic of either waiting for a critical section // or releasing a critical section to a waiting thread. // void RtlpWaitForCriticalSection( IN PRTL_CRITICAL_SECTION CriticalSection ); void RtlpUnWaitCriticalSection( IN PRTL_CRITICAL_SECTION CriticalSection ); void RtlpWaitForCriticalSection( IN PRTL_CRITICAL_SECTION CriticalSection ) { NTSTATUS st; ULONG TimeoutCount = 0; PLARGE_INTEGER TimeoutTime; BOOLEAN CsIsLoaderLock; // // critical sections are disabled during exit process so that // apps that are not carefull during shutdown don't hang // CsIsLoaderLock = (CriticalSection == NtCurrentPeb()->LoaderLock); NtCurrentTeb()->WaitingOnLoaderLock = (ULONG)CsIsLoaderLock; if ( LdrpShutdownInProgress && ((!CsIsLoaderLock) || (CsIsLoaderLock && LdrpShutdownThreadId == NtCurrentTeb()->ClientId.UniqueThread) ) ) { // // slimey reinitialization of the critical section with the count biased by one // this is how the critical section would normally look to the thread coming out // of this function. Note that the semaphore handle is leaked, but since the // app is exiting, it's ok // CriticalSection->LockCount = 0; CriticalSection->RecursionCount = 0; CriticalSection->OwningThread = 0; CriticalSection->LockSemaphore = 0; NtCurrentTeb()->WaitingOnLoaderLock = 0; return; } if (RtlpTimoutDisable) { TimeoutTime = NULL; } else { TimeoutTime = &RtlpTimeout; } if ( !CriticalSection->LockSemaphore ) { RtlpCheckDeferedCriticalSection(CriticalSection); } CriticalSection->DebugInfo->EntryCount++; while( TRUE ) { CriticalSection->DebugInfo->ContentionCount++; #if 0 DbgPrint( "NTDLL: Waiting for CritSect: %X owned by ThreadId: %X Count: %u Level: %u\n", CriticalSection, CriticalSection->OwningThread, CriticalSection->LockCount, CriticalSection->RecursionCount ); #endif st = NtWaitForSingleObject( CriticalSection->LockSemaphore, FALSE, TimeoutTime ); if ( st == STATUS_TIMEOUT ) { DbgPrint( "RTL: Enter Critical Section Timeout (2 minutes) %d\n", TimeoutCount ); DbgPrint( "RTL: Pid.Tid %x.%x, owner tid %x Critical Section %lx - ContentionCount == %lu\n", NtCurrentTeb()->ClientId.UniqueProcess, NtCurrentTeb()->ClientId.UniqueThread, CriticalSection->OwningThread, CriticalSection, CriticalSection->DebugInfo->ContentionCount ); TimeoutCount++; if ( TimeoutCount > 2 && CriticalSection != NtCurrentPeb()->LoaderLock ) { PIMAGE_NT_HEADERS NtHeaders; // // If the image is a Win32 image, then raise an exception and try to get to the // uae popup // NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress); if (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI || NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) { EXCEPTION_RECORD ExceptionRecord; ExceptionRecord.ExceptionCode = STATUS_POSSIBLE_DEADLOCK; ExceptionRecord.ExceptionFlags = 0; ExceptionRecord.ExceptionRecord = NULL; ExceptionRecord.ExceptionAddress = (PVOID)RtlRaiseException; ExceptionRecord.NumberParameters = 1; ExceptionRecord.ExceptionInformation[0] = (ULONG)CriticalSection; RtlRaiseException(&ExceptionRecord); } else { DbgBreakPoint(); } } DbgPrint("RTL: Re-Waiting\n"); } else { if ( NT_SUCCESS(st) ) { if ( CsIsLoaderLock ) { CriticalSection->OwningThread = NtCurrentTeb()->ClientId.UniqueThread; NtCurrentTeb()->WaitingOnLoaderLock = 0; } return; } else { RtlRaiseStatus(st); } } } } void RtlpUnWaitCriticalSection( IN PRTL_CRITICAL_SECTION CriticalSection ) { NTSTATUS st; #if 0 DbgPrint( "NTDLL: Releasing CritSect: %X ThreadId: %X\n", CriticalSection, CriticalSection->OwningThread ); #endif if ( !CriticalSection->LockSemaphore ) { RtlpCheckDeferedCriticalSection(CriticalSection); } st = NtReleaseSemaphore( CriticalSection->LockSemaphore, 1, NULL ); if ( NT_SUCCESS(st) ) { return; } else { RtlRaiseStatus(st); } } void RtlpNotOwnerCriticalSection( IN PRTL_CRITICAL_SECTION CriticalSection ); void RtlpNotOwnerCriticalSection( IN PRTL_CRITICAL_SECTION CriticalSection ) { BOOLEAN CsIsLoaderLock; // // critical sections are disabled during exit process so that // apps that are not carefull during shutdown don't hang // CsIsLoaderLock = (CriticalSection == NtCurrentPeb()->LoaderLock); if ( LdrpShutdownInProgress && ((!CsIsLoaderLock) || (CsIsLoaderLock && LdrpShutdownThreadId == NtCurrentTeb()->ClientId.UniqueThread) ) ) { return; } if (NtCurrentPeb()->BeingDebugged) { DbgPrint( "NTDLL: Calling thread (%X) not owner of CritSect: %X Owner ThreadId: %X\n", NtCurrentTeb()->ClientId.UniqueThread, CriticalSection, CriticalSection->OwningThread ); #if i386 _asm { int 3 } #else DbgBreakPoint(); #endif } RtlRaiseStatus( STATUS_RESOURCE_NOT_OWNED ); }