1018 lines
25 KiB
C++
1018 lines
25 KiB
C++
/*++
|
||
|
||
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;
|
||
}
|