windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/iisrtl/resource.cxx
2020-09-26 16:20:57 +08:00

1018 lines
25 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-1999 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 "precomp.hxx"
#include "dbgutil.h"
#include <tsres.hxx>
#include <isplat.h>
//
// The semaphore wait time before retrying the wait
//
#define INET_RES_TIMEOUT (2 * 60 * 1000)
#define TIMEOUT_BREAK_COUNT 15
#if DBG
LONG g_InetResourcesCreated = 0;
LONG g_InetResourcesDeleted = 0;
#endif
BOOL
InetInitializeResource(
IN PRTL_RESOURCE Resource
)
/*++
Routine Description:
This routine initializes the input resource variable
Arguments:
Resource - Supplies the resource variable being initialized
Return Value:
None
--*/
{
PLATFORM_TYPE platformType;
//
// 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.
//
INITIALIZE_CRITICAL_SECTION( &Resource->CriticalSection );
//
// The critical section's DebugInfo field is only valid under NT.
// If we're running under NT, then set the critical section type
// to mark this as a resource. This is useful when debugging resource
// leaks.
//
platformType = IISGetPlatformType();
if( platformType == PtNtServer ||
platformType == PtNtWorkstation ) {
Resource->CriticalSection.DebugInfo->Type = RTL_RESOURCE_TYPE;
}
Resource->DebugInfo = NULL;
//
// 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.
//
Resource->SharedSemaphore = IIS_CREATE_SEMAPHORE(
"RTL_RESOURCE::SharedSemaphore",
Resource,
0,
MAXLONG
);
if ( !Resource->SharedSemaphore ) {
return FALSE;
}
Resource->NumberOfWaitingShared = 0;
Resource->ExclusiveSemaphore = IIS_CREATE_SEMAPHORE(
"RTL_RESOURCE::ExclusiveSemaphore",
Resource,
0,
MAXLONG
);
if ( !Resource->ExclusiveSemaphore ){
CloseHandle( Resource->SharedSemaphore );
return FALSE;
}
Resource->NumberOfWaitingExclusive = 0;
//
// Initialize the current state of the resource
//
Resource->NumberOfActive = 0;
Resource->ExclusiveOwnerThread = NULL;
#if DBG
InterlockedIncrement( &g_InetResourcesCreated );
#endif
return TRUE;
}
BOOL
InetAcquireResourceShared(
IN PRTL_RESOURCE Resource,
IN BOOL 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:
BOOL - TRUE if the resource is acquired and FALSE otherwise
--*/
{
DWORD ret;
ULONG TimeoutCount = 0;
DWORD TimeoutTime = INET_RES_TIMEOUT;
//
// Enter the critical section
//
EnterCriticalSection(&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;
LeaveCriticalSection(&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;
LeaveCriticalSection(&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) {
LeaveCriticalSection(&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;
LeaveCriticalSection(&Resource->CriticalSection);
rewait:
if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) {
TimeoutTime = INFINITE;
}
ret = WaitForSingleObject(
Resource->SharedSemaphore,
TimeoutTime
);
if ( ret == WAIT_TIMEOUT ) {
IF_DEBUG(RESOURCE) {
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetAcquireResourceShared] Sem timeout\n",
Resource));
}
TimeoutCount++;
if ( TimeoutCount == TIMEOUT_BREAK_COUNT ) {
#if DBG && TIMEOUT_BREAK_COUNT > 0
DebugBreak();
#endif
}
IF_DEBUG(RESOURCE) {
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetAcquireResourceShared] Re-Waiting\n",
Resource));
}
goto rewait;
} else if ( ret != WAIT_OBJECT_0 ) {
IF_DEBUG(RESOURCE) {
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetAcquireResourceShared] "
"WaitForSingleObject Failed\n",
Resource));
}
}
}
//
// Now the resource is ours, for shared access
//
return TRUE;
}
BOOL
InetAcquireResourceExclusive(
IN PRTL_RESOURCE Resource,
IN BOOL 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:
BOOL - TRUE if the resource is acquired and FALSE otherwise
--*/
{
ULONG TimeoutCount = 0;
DWORD TimeoutTime = INET_RES_TIMEOUT;
DWORD ret;
//
// Loop until the resource is ours or exit if we cannot wait.
//
while (TRUE) {
//
// Enter the critical section
//
EnterCriticalSection(&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;
LeaveCriticalSection(&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;
LeaveCriticalSection(&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) {
LeaveCriticalSection(&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;
LeaveCriticalSection(&Resource->CriticalSection);
rewait:
if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) {
TimeoutTime = INFINITE;
}
ret = WaitForSingleObject(
Resource->ExclusiveSemaphore,
TimeoutTime
);
if ( ret == WAIT_TIMEOUT ) {
IF_DEBUG(RESOURCE) {
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetAcquireResourceExclusive] "
"Sem Timeout\n",
Resource));
}
TimeoutCount++;
if ( TimeoutCount == TIMEOUT_BREAK_COUNT ) {
#if DBG && TIMEOUT_BREAK_COUNT > 0
DebugBreak();
#endif
}
IF_DEBUG(RESOURCE) {
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetAcquireResourceExclusive] "
"Re-Waiting\n",
Resource));
}
goto rewait;
} else if ( ret != WAIT_OBJECT_0 ) {
IF_DEBUG(RESOURCE) {
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetAcquireResourceExclusive] "
"WaitForSingleObject Failed\n",
Resource));
}
}
}
return TRUE;
}
BOOL
InetReleaseResource(
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.
--*/
{
LONG PreviousCount;
BOOL fResult = FALSE;
//
// Enter the critical section
//
EnterCriticalSection(&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;
if ( !ReleaseSemaphore(
Resource->ExclusiveSemaphore,
1,
&PreviousCount
)) {
goto cleanup;
}
}
} 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;
if ( !ReleaseSemaphore(
Resource->ExclusiveSemaphore,
1,
&PreviousCount
)) {
goto cleanup;
}
//
// 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;
if ( !ReleaseSemaphore(
Resource->SharedSemaphore,
Resource->NumberOfActive,
&PreviousCount
)) {
goto cleanup;
}
}
}
#if DBG
} else {
//
// The resource isn't current acquired, there is nothing to release
// so tell the user the mistake
//
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetReleaseResource] "
"Resource released too many times!\n",
Resource));
DebugBreak();
#endif
}
//
// If we reached here, we successfully released the resource
//
fResult = TRUE;
cleanup:
//
// Exit the critical section, and return to the caller
//
LeaveCriticalSection(&Resource->CriticalSection);
return fResult;
}
BOOL
InetConvertSharedToExclusive(
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
--*/
{
DWORD ret;
DWORD TimeoutTime = INET_RES_TIMEOUT;
ULONG TimeoutCount = 0;
//
// Enter the critical section
//
EnterCriticalSection(&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;
LeaveCriticalSection(&Resource->CriticalSection);
return TRUE;
}
//
// 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
//
LeaveCriticalSection(&Resource->CriticalSection);
return TRUE;
}
//
// 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;
LeaveCriticalSection(&Resource->CriticalSection);
rewait:
if ( Resource->Flags & RTL_RESOURCE_FLAG_LONG_TERM ) {
TimeoutTime = INFINITE;
}
ret = WaitForSingleObject(
Resource->ExclusiveSemaphore,
TimeoutTime
);
if ( ret == WAIT_TIMEOUT ) {
IF_DEBUG(RESOURCE) {
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetConvertSharedToExclusive] Sem timeout\n",
Resource));
}
TimeoutCount++;
if ( TimeoutCount == TIMEOUT_BREAK_COUNT ) {
#if DBG && TIMEOUT_BREAK_COUNT > 0
DebugBreak();
#endif
}
IF_DEBUG(RESOURCE) {
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetConvertSharedToExclusive] Re-Waiting\n",
Resource));
}
goto rewait;
} else if ( ret != WAIT_OBJECT_0 ) {
IF_DEBUG(RESOURCE) {
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetConvertSharedToExclusive] "
"WaitForSingleObject Failed\n",
Resource));
}
return FALSE;
}
//
// Enter the critical section
//
EnterCriticalSection(&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;
LeaveCriticalSection(&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 critical section and return.
//
Resource->NumberOfActive -= 1;
LeaveCriticalSection(&Resource->CriticalSection);
return TRUE;
}
}
}
//
// The resource is not currently acquired for shared so this is a
// spurious call
//
#if DBG
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetConvertSharedToExclusive] "
"Failed error - SHARED_RESOURCE_CONV_ERROR\n",
Resource));
DebugBreak();
#endif
return FALSE;
}
BOOL
InetConvertExclusiveToShared(
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;
//
// Enter the critical section
//
EnterCriticalSection(&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;
if ( !ReleaseSemaphore(
Resource->SharedSemaphore,
Resource->NumberOfActive - 1,
&PreviousCount
)) {
LeaveCriticalSection(&Resource->CriticalSection);
return FALSE;
}
} else {
//
// There is no one waiting for shared access so it's only ours
//
Resource->NumberOfActive = 1;
}
LeaveCriticalSection(&Resource->CriticalSection);
return TRUE;
}
//
// The resource is not currently acquired for exclusive, or we've
// recursively acquired it, so this must be a spurious call
//
#if DBG
DBGPRINTF(( DBG_CONTEXT,
"%08p::[InetConvertExclusiveToShared] "
"Failed error - SHARED_RESOURCE_CONV_ERROR\n",
Resource));
DebugBreak();
#endif
return FALSE;
}
VOID
InetDeleteResource (
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
--*/
{
DeleteCriticalSection( &Resource->CriticalSection );
CloseHandle(Resource->SharedSemaphore);
CloseHandle(Resource->ExclusiveSemaphore);
ZeroMemory( Resource, sizeof( *Resource ) );
#if DBG
InterlockedIncrement( &g_InetResourcesDeleted );
#endif
return;
}
VOID
InetDumpResource(
IN PRTL_RESOURCE Resource
)
{
DBGPRINTF(( DBG_CONTEXT, "Resource @ %p\n",
Resource));
DBGPRINTF(( DBG_CONTEXT, " NumberOfWaitingShared = %lx\n",
Resource->NumberOfWaitingShared));
DBGPRINTF(( DBG_CONTEXT, " NumberOfWaitingExclusive = %lx\n",
Resource->NumberOfWaitingExclusive));
DBGPRINTF(( DBG_CONTEXT, " NumberOfActive = %lx\n",
Resource->NumberOfActive));
return;
}