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.
583 lines
11 KiB
583 lines
11 KiB
/*++
|
|
|
|
Microsoft Windows NT RPC Name Service
|
|
Copyright (C) Microsoft Corporation, 1995 - 1999
|
|
|
|
Module Name:
|
|
|
|
mutex.hxx
|
|
|
|
Abstract:
|
|
|
|
This file contains definitions of classes which provide several variations
|
|
on mutexes, including shared and private mutexes, reader/writer mutexes,
|
|
and semaphores in the NT sense (which for us are mutexes which can be
|
|
acquired and released by different threads).
|
|
|
|
Author:
|
|
|
|
Satish Thatte (SatishT) 09/01/95 Created all the code below except where
|
|
otherwise indicated.
|
|
|
|
--*/
|
|
|
|
#ifndef __MUTEX_HXX__
|
|
#define __MUTEX_HXX__
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Class Definition:
|
|
|
|
CPrivateCriticalSection
|
|
|
|
Abstract:
|
|
|
|
This class implements an intra process critical section which allows
|
|
only one thread in the process at a time to gain access to resources.
|
|
Rather than being the CRITICAL_SECTION itself, the CSharedCriticalSection
|
|
contains a pointer to the CRITICAL_SECTION. This isolates the system
|
|
dependent code at a tiny expense in compilation speed since all the code
|
|
for the methods is inline.
|
|
|
|
It seems strange to talk of a private critical section -- if it is
|
|
really private why use it? The sense in which this class of object is
|
|
meant to be private is that CPrivateCriticalSection objects are expected
|
|
to have a single reference and a single destruction point and therefore
|
|
do not need reference counting.
|
|
|
|
--*/
|
|
|
|
class CPrivateCriticalSection {
|
|
|
|
CRITICAL_SECTION * pNTCS; // Reference to system CRITICAL_SECTION object
|
|
// we are impersonating
|
|
|
|
|
|
public:
|
|
|
|
CPrivateCriticalSection(
|
|
);
|
|
|
|
virtual ~CPrivateCriticalSection(
|
|
);
|
|
|
|
void
|
|
Enter(
|
|
) ;
|
|
|
|
void
|
|
Leave(
|
|
);
|
|
};
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Class Definition:
|
|
|
|
SimpleCriticalSection
|
|
|
|
Abstract:
|
|
|
|
A convenient way to acquire and release a lock on a CPrivateCriticalSection.
|
|
Entry occurs on creation and exit on destruction of this object, and hence
|
|
can be made to coincide with a local scope. Especially convenient when
|
|
there are multiple exit points from the scope (returns, breaks, etc.).
|
|
|
|
|
|
--*/
|
|
|
|
class SimpleCriticalSection {
|
|
|
|
CPrivateCriticalSection& MyPCS;
|
|
|
|
public:
|
|
|
|
SimpleCriticalSection(CPrivateCriticalSection& PCS) : MyPCS(PCS)
|
|
{
|
|
MyPCS.Enter();
|
|
}
|
|
|
|
~SimpleCriticalSection()
|
|
{
|
|
MyPCS.Leave();
|
|
}
|
|
};
|
|
|
|
|
|
/*++
|
|
|
|
Class Definition:
|
|
|
|
CSharedCriticalSection
|
|
|
|
Abstract:
|
|
|
|
This class implements an intra process critical section which allows
|
|
only one thread in the process at a time to gain access to resources.
|
|
|
|
Continuing the remarks in relation to CPrivateCriticalSection, this
|
|
class of object is shared as in "referenced, and released in multiple
|
|
other objects" such as guarded lists and iterators, for instance.
|
|
|
|
Using the primary (impersonated) CRITICAL_SECTION for locking the
|
|
reference count is cute but not viable. If the CRITICAL_SECTION
|
|
is being held for real locking, just acquiring a reference to the
|
|
CSharedCriticalSection object becomes a blocking operation.
|
|
|
|
--*/
|
|
|
|
class CSharedCriticalSection : public CRefCounted {
|
|
|
|
CPrivateCriticalSection PCSprimary; // Reference to system CRITICAL_SECTION object
|
|
// we are impersonating
|
|
|
|
CPrivateCriticalSection PCScount; // CRITICAL_SECTION object used for ref counting
|
|
|
|
|
|
public:
|
|
|
|
void
|
|
Enter(
|
|
) ;
|
|
|
|
void
|
|
Leave(
|
|
);
|
|
|
|
virtual void
|
|
hold(
|
|
);
|
|
|
|
virtual void
|
|
release(
|
|
);
|
|
};
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Class Definition:
|
|
|
|
CPrivateSemaphore
|
|
|
|
Abstract:
|
|
|
|
This class implements an intra process semaphore which allows
|
|
multiple and shared access to resources. The enter and leave
|
|
operators are delinked -- they do not have to be performed
|
|
by the same thread.
|
|
|
|
As in the case of a private critical section, a private semaphore
|
|
is not meant to be shared as an object, and is not reference counted.
|
|
|
|
--*/
|
|
|
|
class CPrivateSemaphore {
|
|
|
|
HANDLE pNTSem; // Reference to system semaphore object
|
|
// we are impersonating
|
|
|
|
|
|
public:
|
|
|
|
CPrivateSemaphore(long lMaxCount = 1);
|
|
|
|
~CPrivateSemaphore();
|
|
|
|
void Enter() ;
|
|
|
|
void Leave(long lIncrement = 1);
|
|
};
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Class Definition:
|
|
|
|
CReadWriteSection
|
|
|
|
Abstract:
|
|
|
|
This class implements a simple solution of the usual readers/writers
|
|
problem. The writer block is a semaphore rather than critical section
|
|
because it is acquired by the first reader thread and released by the
|
|
last one, hence it is not "owned" by any thread at any time.
|
|
|
|
--*/
|
|
|
|
class CReadWriteSection : public CRefCounted {
|
|
|
|
/* PCSReaderBlock: mutex used by writers to block readers and by readers
|
|
to guard access to ulReaderCount and PSemWriterBlock.
|
|
*/
|
|
|
|
CPrivateCriticalSection PCSReaderBlock;
|
|
|
|
/* PSemWriterBlock: semaphore used by readers to block writers. This is a
|
|
semaphore because the first reader blocks it and the last one releases it,
|
|
which doesn't work with critical sections!
|
|
*/
|
|
|
|
CPrivateSemaphore PSemWriterBlock;
|
|
|
|
/* The reason we use an explicit lock instead of InterlockedIncrement etc is
|
|
that we need to lock out access to reader count not only to change its
|
|
value but also to decide on taking or releasing the writer block.
|
|
*/
|
|
|
|
CPrivateCriticalSection PCSCountBlock;
|
|
|
|
long ulReaderCount;
|
|
|
|
public:
|
|
|
|
CReadWriteSection();
|
|
|
|
int
|
|
readerEnter(
|
|
) ;
|
|
|
|
int
|
|
readerLeave(
|
|
);
|
|
|
|
void
|
|
writerEnter(
|
|
) ;
|
|
|
|
void
|
|
writerLeave(
|
|
);
|
|
};
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Class Definition:
|
|
|
|
CriticalWriter
|
|
|
|
Abstract:
|
|
|
|
A convenient way to acquire and release a writer-lock on a CReadWriteSection.
|
|
Entry occurs on creation and exit on destruction of this object, and hence
|
|
can be made to coincide with a local scope. Especially convenient when
|
|
there are multiple exit points from the scope (returns, breaks, etc.).
|
|
|
|
|
|
--*/
|
|
|
|
class CriticalWriter {
|
|
|
|
CReadWriteSection& rwHost;
|
|
|
|
public:
|
|
|
|
CriticalWriter(CReadWriteSection& rw) : rwHost(rw) {
|
|
DBGOUT(SEM1, "Starting Construction of Critical Writer\n\n");
|
|
|
|
rwHost.writerEnter();
|
|
|
|
DBGOUT(SEM1, "Ending Construction of Critical Writer\n\n");
|
|
}
|
|
|
|
~CriticalWriter() {
|
|
DBGOUT(SEM1, "Starting Destruction of Critical Writer\n\n");
|
|
|
|
rwHost.writerLeave();
|
|
|
|
DBGOUT(SEM1, "Ending Destruction of Critical Writer\n\n");
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Class Definition:
|
|
|
|
CriticalReader
|
|
|
|
Abstract:
|
|
|
|
A convenient way to acquire and release a reader-lock on a CReadWriteSection.
|
|
Entry occurs on creation and exit on destruction of this object, and hence
|
|
can be made to coincide with a local scope. Especially convenient when
|
|
there are multiple exit points from the scope (returns, breaks, etc.).
|
|
|
|
|
|
--*/
|
|
|
|
class CriticalReader {
|
|
|
|
CReadWriteSection& rwHost;
|
|
|
|
public:
|
|
|
|
CriticalReader(CReadWriteSection& rw) : rwHost(rw) {
|
|
DBGOUT(SEM1, "Starting Construction of Critical Reader\n\n");
|
|
|
|
rwHost.readerEnter();
|
|
|
|
DBGOUT(SEM1, "Ending Construction of Critical Reader\n\n");
|
|
}
|
|
|
|
~CriticalReader() {
|
|
DBGOUT(SEM1, "Starting Destruction of Critical Reader\n\n");
|
|
|
|
rwHost.readerLeave();
|
|
|
|
DBGOUT(SEM1, "Ending Destruction of Critical Reader\n\n");
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/******** inline methods for CPrivateCriticalSection ********/
|
|
|
|
|
|
inline
|
|
CPrivateCriticalSection::CPrivateCriticalSection (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Rather than being the CRITICAL_SECTION itself, the CPrivateCriticalSection
|
|
contains a pointer to the CRITICAL_SECTION. This isolates the system
|
|
dependent code at a tiny expense in compilation speed since all the code
|
|
for the methods is inline.
|
|
|
|
Results:
|
|
|
|
None unless an exception occurs.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if ((pNTCS = new RTL_CRITICAL_SECTION))
|
|
InitializeCriticalSection(pNTCS);
|
|
|
|
else RaiseException(
|
|
NSI_S_OUT_OF_MEMORY,
|
|
EXCEPTION_NONCONTINUABLE,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
inline
|
|
CPrivateCriticalSection::~CPrivateCriticalSection (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the NT critical section object and the memory it uses.
|
|
|
|
--*/
|
|
{
|
|
DeleteCriticalSection(pNTCS);
|
|
delete pNTCS;
|
|
}
|
|
|
|
|
|
inline void
|
|
CPrivateCriticalSection::Leave (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clear the CPrivateCriticalSection indicating that the current thread is done with it.
|
|
--*/
|
|
|
|
{
|
|
LeaveCriticalSection(pNTCS);
|
|
}
|
|
|
|
inline void
|
|
CPrivateCriticalSection::Enter (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Request exclusive access to the CPrivateCriticalSection. This routine will
|
|
not return until the current thread has exclusive access to the
|
|
CSharedCriticalSection.
|
|
--*/
|
|
{
|
|
EnterCriticalSection(pNTCS);
|
|
}
|
|
|
|
|
|
/******** inline methods for CSharedCriticalSection ********/
|
|
|
|
|
|
inline void
|
|
CSharedCriticalSection::Leave (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clear the CSharedCriticalSection indicating that the current thread is done with it.
|
|
--*/
|
|
|
|
{
|
|
PCSprimary.Leave();
|
|
}
|
|
|
|
inline void
|
|
CSharedCriticalSection::Enter (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Request exclusive access to the CSharedCriticalSection. This routine will
|
|
not return until the current thread has exclusive access to the
|
|
CSharedCriticalSection.
|
|
--*/
|
|
{
|
|
PCSprimary.Enter();
|
|
}
|
|
|
|
|
|
inline void
|
|
CSharedCriticalSection::hold (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We need this new implementation of hold to make sure the reference
|
|
counts are accurate. For instance, suppose two iterators are
|
|
being created for the same list in different threads. Unless we
|
|
use locking, there is a chance that the references will be
|
|
undercounted due to race conditions.
|
|
|
|
--*/
|
|
{
|
|
CRefCounted::hold();
|
|
}
|
|
|
|
|
|
inline void
|
|
CSharedCriticalSection::release (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
See above. Note that we cannot call the parent release method
|
|
because it may cause self-destruct before we can perform Leave
|
|
on PCScount! However, we are using a "cooperative" rather than
|
|
"opportunistic" sharing model here -- we don't deal with the
|
|
situation where an opportunistic thread may grab this object
|
|
and bump up its ulRefCount after it has dropped to zero.
|
|
--*/
|
|
{
|
|
|
|
PCScount.Enter();
|
|
ASSERT(ulRefCount, "Decrementing nonpositive reference count\n");
|
|
ulRefCount--;
|
|
PCScount.Leave();
|
|
if (!ulRefCount)
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******** inline methods for CReadWriteSection ********/
|
|
|
|
|
|
inline
|
|
CReadWriteSection::CReadWriteSection()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Simple constructor for CReadWriteSection.
|
|
|
|
Results:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ulReaderCount = 0;
|
|
}
|
|
|
|
|
|
inline void
|
|
CReadWriteSection::writerEnter()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The writer must block new readers from entering otherwise it
|
|
may starve. Must also wait for current readers to leave.
|
|
Blocking other writers is a simple side-effect of these.
|
|
|
|
Results:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DBGOUT(SEM, "Entering writerEnter\n");
|
|
|
|
PCSReaderBlock.Enter(); // block any further readers
|
|
|
|
DBGOUT(SEM, "Got Past PCSReaderBlock\n");
|
|
|
|
PSemWriterBlock.Enter(); // wait for current readers to leave
|
|
|
|
DBGOUT(SEM, "Leaving writerEnter\n");
|
|
}
|
|
|
|
|
|
inline void
|
|
CReadWriteSection::writerLeave()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Release everything and go away.
|
|
|
|
Results:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DBGOUT(SEM, "Performing writerLeave\n");
|
|
|
|
PSemWriterBlock.Leave(); // this order "levels the playing field"
|
|
PCSReaderBlock.Leave(); // between readers and writers for entry
|
|
}
|
|
|
|
|
|
#endif // __MUTEX_HXX__
|