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;
|
|||
|
}
|