NT4/private/ntos/dll/resource.c
2020-09-30 17:12:29 +02:00

1616 lines
42 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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 <ldrp.h>
#include <ntimage.h>
#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 );
}