Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

380 lines
9.2 KiB

/*
Copyright (c) 1992 Microsoft Corporation
Module Name:
swmr.c
Abstract:
This module contains the single-writer, multi-reader semaphore routines
and the lock-list-count routines.
Author:
Jameel Hyder (microsoft!jameelh)
Revision History:
25 Apr 1992 Initial Version
Notes: Tab stop: 4
--*/
#define FILENUM FILE_SWMR
#include <afp.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfpSwmrInitSwmr)
#endif
/*** AfpSwmrInitSwmr
*
* Initialize the access data structure. Involves initialization of the spin
* lock and the shared and exclusive semaphores. All counts are zeroed.
*/
VOID FASTCALL FASTCALL
AfpSwmrInitSwmr(
IN OUT PSWMR pSwmr
)
{
#if DBG
pSwmr->Signature = SWMR_SIGNATURE;
#endif
pSwmr->swmr_cOwnedExclusive = 0;
pSwmr->swmr_cExclWaiting = 0;
pSwmr->swmr_cSharedOwners = 0;
pSwmr->swmr_cSharedWaiting = 0;
pSwmr->swmr_ExclusiveOwner = NULL;
KeInitializeSemaphore(&pSwmr->swmr_SharedSem, 0, MAXLONG);
KeInitializeSemaphore(&pSwmr->swmr_ExclSem, 0, MAXLONG);
}
/*** AfpSwmrAcquireShared
*
* Take the semaphore for shared access.
*/
VOID FASTCALL
AfpSwmrAcquireShared(
IN PSWMR pSwmr
)
{
NTSTATUS Status;
KIRQL OldIrql;
#ifdef PROFILING
TIME TimeS, TimeE, TimeD;
#endif
ASSERT (VALID_SWMR(pSwmr));
// This should never be called at DISPATCH_LEVEL
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
ACQUIRE_SPIN_LOCK(&AfpSwmrLock, &OldIrql);
if ((pSwmr->swmr_cOwnedExclusive > 0) ||
(pSwmr->swmr_cExclWaiting != 0))
{
pSwmr->swmr_cSharedWaiting++;
RELEASE_SPIN_LOCK(&AfpSwmrLock, OldIrql);
DBGPRINT(DBG_COMP_LOCKS, DBG_LEVEL_INFO,
("AfpSwmrAcquireShared: Blocking for Shared %lx\n", pSwmr));
#ifdef PROFILING
AfpGetPerfCounter(&TimeS);
#endif
do
{
Status = AfpIoWait(&pSwmr->swmr_SharedSem, &FiveSecTimeOut);
if (Status == STATUS_TIMEOUT)
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpSwmrAcquireShared: Timeout Waiting for Shared acess, re-waiting (%lx)\n", pSwmr));
}
} while (Status == STATUS_TIMEOUT);
ASSERT (pSwmr->swmr_cOwnedExclusive == 0);
ASSERT (pSwmr->swmr_cSharedOwners != 0);
#ifdef PROFILING
AfpGetPerfCounter(&TimeE);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_SwmrWaitCount);
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_SwmrWaitTime,
TimeD,
&AfpStatisticsLock);
#endif
}
else // Its either free or shared owners are present with no exclusive waiters
{
pSwmr->swmr_cSharedOwners++;
RELEASE_SPIN_LOCK(&AfpSwmrLock, OldIrql);
}
#ifdef PROFILING
AfpGetPerfCounter(&TimeE);
TimeE.QuadPart = -(TimeE.QuadPart);
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_SwmrLockTimeR,
TimeE,
&AfpStatisticsLock);
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_SwmrLockCountR);
#endif
}
/*** AfpSwmrAcquireExclusive
*
* Take the semaphore for exclusive access.
*/
VOID FASTCALL
AfpSwmrAcquireExclusive(
IN PSWMR pSwmr
)
{
NTSTATUS Status;
KIRQL OldIrql;
#ifdef PROFILING
TIME TimeS, TimeE, TimeD;
#endif
ASSERT (VALID_SWMR(pSwmr));
// This should never be called at DISPATCH_LEVEL
ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
ACQUIRE_SPIN_LOCK(&AfpSwmrLock, &OldIrql);
// If the exclusive access is already granted, Check if it is
// the same thread requesting. If so grant it.
if ((pSwmr->swmr_cOwnedExclusive != 0) &&
(pSwmr->swmr_ExclusiveOwner == PsGetCurrentThread()))
{
pSwmr->swmr_cOwnedExclusive ++;
RELEASE_SPIN_LOCK(&AfpSwmrLock, OldIrql);
}
else if ((pSwmr->swmr_cOwnedExclusive > 0) ||
(pSwmr->swmr_cExclWaiting != 0) ||
(pSwmr->swmr_cSharedOwners != 0))
{
pSwmr->swmr_cExclWaiting++;
RELEASE_SPIN_LOCK(&AfpSwmrLock, OldIrql);
DBGPRINT(DBG_COMP_LOCKS, DBG_LEVEL_INFO,
("AfpSwmrAcquireExclusive: Blocking for exclusive %lx\n", pSwmr));
#ifdef PROFILING
AfpGetPerfCounter(&TimeS);
#endif
do
{
Status = AfpIoWait(&pSwmr->swmr_ExclSem, &FiveSecTimeOut);
if (Status == STATUS_TIMEOUT)
{
DBGPRINT(DBG_COMP_ADMINAPI_SC, DBG_LEVEL_INFO,
("AfpSwmrAcquireExclusive: Timeout Waiting for exclusive acess, re-waiting\n"));
}
} while (Status == STATUS_TIMEOUT);
ASSERT (pSwmr->swmr_cOwnedExclusive == 1);
pSwmr->swmr_ExclusiveOwner = PsGetCurrentThread();
#ifdef PROFILING
AfpGetPerfCounter(&TimeE);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_SwmrWaitCount);
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_SwmrWaitTime,
TimeD,
&AfpStatisticsLock);
#endif
}
else // it is free
{
pSwmr->swmr_cOwnedExclusive ++;
ASSERT(pSwmr->swmr_ExclusiveOwner == NULL);
pSwmr->swmr_ExclusiveOwner = PsGetCurrentThread();
RELEASE_SPIN_LOCK(&AfpSwmrLock, OldIrql);
}
#ifdef PROFILING
AfpGetPerfCounter(&TimeE);
TimeE.QuadPart = -(TimeE.QuadPart);
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_SwmrLockTimeW,
TimeE,
&AfpStatisticsLock);
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_SwmrLockCountW);
#endif
}
/*** AfpSwmrRelease
*
* Release the specified access. It is assumed that the current thread had
* called AfpSwmrAcquirexxxAccess() before this is called. If the SWMR is owned
* exclusively, then there cannot possibly be any shared owners active. When releasing
* the swmr, we first check for exclusive waiters before shared waiters.
*/
VOID FASTCALL
AfpSwmrRelease(
IN PSWMR pSwmr
)
{
KIRQL OldIrql;
#ifdef PROFILING
TIME Time;
BOOLEAN Exclusive = False;
#endif
BOOLEAN fWasShared=FALSE;
ASSERT (VALID_SWMR(pSwmr));
ACQUIRE_SPIN_LOCK(&AfpSwmrLock, &OldIrql);
if (pSwmr->swmr_cOwnedExclusive > 0)
{
ASSERT((pSwmr->swmr_cSharedOwners == 0) &&
(pSwmr->swmr_ExclusiveOwner == PsGetCurrentThread()));
pSwmr->swmr_cOwnedExclusive--;
if (pSwmr->swmr_cOwnedExclusive == 0)
pSwmr->swmr_ExclusiveOwner = NULL;
#ifdef PROFILING
Exclusive = True;
#endif
}
else if (pSwmr->swmr_cSharedOwners != 0)
{
// Was owned for shared access
pSwmr->swmr_cSharedOwners--;
fWasShared = TRUE;
}
else
{
// Releasing w/o acquiring ?
KeBugCheck(0);
}
// If there are shared owners present then we are done. Else check for any
// waiting shared/exclusive waiters
if ((pSwmr->swmr_cOwnedExclusive == 0) && (pSwmr->swmr_cSharedOwners == 0))
{
if ( (pSwmr->swmr_cExclWaiting) &&
(fWasShared || (!pSwmr->swmr_cSharedWaiting)) )
{
ASSERT(pSwmr->swmr_cOwnedExclusive == 0);
pSwmr->swmr_cOwnedExclusive = 1;
pSwmr->swmr_cExclWaiting--;
DBGPRINT(DBG_COMP_LOCKS, DBG_LEVEL_INFO,
("AfpSwmrReleasAccess: Waking exclusive waiter %lx\n", pSwmr));
// Wake up the first exclusive waiter. Everybody else coming in will
// see the access is busy.
KeReleaseSemaphore(&pSwmr->swmr_ExclSem,
SEMAPHORE_INCREMENT,
1,
False);
}
else if (pSwmr->swmr_cSharedWaiting)
{
pSwmr->swmr_cSharedOwners = pSwmr->swmr_cSharedWaiting;
pSwmr->swmr_cSharedWaiting = 0;
DBGPRINT(DBG_COMP_LOCKS, DBG_LEVEL_INFO,
("AfpSwmrReleasAccess: Waking %d shared owner(s) %lx\n",
pSwmr->swmr_cSharedOwners, pSwmr));
KeReleaseSemaphore(&pSwmr->swmr_SharedSem,
SEMAPHORE_INCREMENT,
pSwmr->swmr_cSharedOwners,
False);
}
}
RELEASE_SPIN_LOCK(&AfpSwmrLock, OldIrql);
#ifdef PROFILING
AfpGetPerfCounter(&Time);
INTERLOCKED_ADD_LARGE_INTGR(Exclusive ?
&AfpServerProfile->perf_SwmrLockTimeW :
&AfpServerProfile->perf_SwmrLockTimeR,
Time,
&AfpStatisticsLock);
#endif
}
/*** AfpSwmrUpgradeAccess
*
* The caller currently has shared access. Upgrade him to exclusive, if possible.
*/
BOOLEAN FASTCALL
AfpSwmrUpgradeToExclusive(
IN PSWMR pSwmr
)
{
KIRQL OldIrql;
BOOLEAN RetCode = False; // Assume failed
ASSERT (VALID_SWMR(pSwmr));
ASSERT((pSwmr->swmr_cOwnedExclusive == 0) && (pSwmr->swmr_cSharedOwners != 0));
ACQUIRE_SPIN_LOCK(&AfpSwmrLock, &OldIrql);
if (pSwmr->swmr_cSharedOwners == 1) // Possible if there are no more shared owners
{
pSwmr->swmr_cSharedOwners = 0;
pSwmr->swmr_cOwnedExclusive = 1;
pSwmr->swmr_ExclusiveOwner = PsGetCurrentThread();
RetCode = True;
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_SwmrUpgradeCount);
#endif
}
RELEASE_SPIN_LOCK(&AfpSwmrLock, OldIrql);
return RetCode;
}
/*** AfpSwmrDowngradeAccess
*
* The caller currently has exclusive access. Downgrade him to shared.
*/
VOID FASTCALL
AfpSwmrDowngradeToShared(
IN PSWMR pSwmr
)
{
KIRQL OldIrql;
int cSharedWaiting;
ASSERT (VALID_SWMR(pSwmr));
ASSERT((pSwmr->swmr_cOwnedExclusive == 1) &&
(pSwmr->swmr_ExclusiveOwner == PsGetCurrentThread()) &&
(pSwmr->swmr_cSharedOwners == 0));
ACQUIRE_SPIN_LOCK(&AfpSwmrLock, &OldIrql);
pSwmr->swmr_cOwnedExclusive = 0;
pSwmr->swmr_ExclusiveOwner = NULL;
pSwmr->swmr_cSharedOwners = 1;
if (cSharedWaiting = pSwmr->swmr_cSharedWaiting)
{
pSwmr->swmr_cSharedOwners += (BYTE)cSharedWaiting;
pSwmr->swmr_cSharedWaiting = 0;
DBGPRINT(DBG_COMP_LOCKS, DBG_LEVEL_INFO,
("AfpSwmrDowngradeAccess: Waking %d Reader(s) %lx\n",
cSharedWaiting, pSwmr));
KeReleaseSemaphore(&pSwmr->swmr_SharedSem,
SEMAPHORE_INCREMENT,
cSharedWaiting,
False);
}
RELEASE_SPIN_LOCK(&AfpSwmrLock, OldIrql);
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_SwmrDowngradeCount);
#endif
}