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.
704 lines
16 KiB
704 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1997-2002 Microsoft Corporation
|
|
|
|
Module Name :
|
|
kLocks.h
|
|
|
|
Abstract:
|
|
A collection of kernel-mode locks for multithreaded access to
|
|
data structures that provide the same abstractions as <locks.h>
|
|
|
|
Author:
|
|
George V. Reilly (GeorgeRe) 24-Oct-2000
|
|
|
|
Environment:
|
|
Win32 - Kernel Mode
|
|
|
|
Project:
|
|
LKRhash
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#ifndef __KLOCKS_H__
|
|
#define __KLOCKS_H__
|
|
|
|
#define LOCKS_KERNEL_MODE
|
|
|
|
#ifdef __LOCKS_H__
|
|
# error Do not #include <locks.h> before <klocks.h>
|
|
#endif
|
|
|
|
#include <locks.h>
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CKSpinLock is a mutex lock based on KSPIN_LOCK
|
|
|
|
class IRTL_DLLEXP CKSpinLock :
|
|
public CLockBase<LOCK_KSPINLOCK, LOCK_MUTEX,
|
|
LOCK_NON_RECURSIVE, LOCK_WAIT_SPIN, LOCK_QUEUE_KERNEL,
|
|
LOCK_CLASS_SPIN
|
|
>
|
|
{
|
|
private:
|
|
// BUGBUG: this data must live in non-paged memory
|
|
mutable KSPIN_LOCK KSpinLock;
|
|
volatile KIRQL m_OldIrql;
|
|
|
|
LOCK_INSTRUMENTATION_DECL();
|
|
|
|
public:
|
|
|
|
#ifndef LOCK_INSTRUMENTATION
|
|
|
|
CKSpinLock()
|
|
{
|
|
KeInitializeSpinLock(&KSpinLock);
|
|
m_OldIrql = PASSIVE_LEVEL;
|
|
}
|
|
|
|
#else // LOCK_INSTRUMENTATION
|
|
|
|
CKSpinLock(
|
|
const TCHAR* ptszName)
|
|
{
|
|
KeInitializeSpinLock(&KSpinLock);
|
|
m_OldIrql = PASSIVE_LEVEL;
|
|
LOCK_INSTRUMENTATION_INIT(ptszName);
|
|
}
|
|
|
|
#endif // LOCK_INSTRUMENTATION
|
|
|
|
#ifdef IRTLDEBUG
|
|
~CKSpinLock() {}
|
|
#endif // IRTLDEBUG
|
|
|
|
// Acquire an exclusive lock for writing.
|
|
// Blocks (if needed) until acquired.
|
|
void WriteLock()
|
|
{
|
|
KIRQL OldIrql;
|
|
KeAcquireSpinLock(&KSpinLock, &OldIrql);
|
|
|
|
// Now that we've acquired the spinlock, copy the stack variable
|
|
// into the member variable. The member variable is only written
|
|
// or read by the owner of the spinlock.
|
|
m_OldIrql = OldIrql;
|
|
}
|
|
|
|
// Acquire a (possibly shared) lock for reading.
|
|
// Blocks (if needed) until acquired.
|
|
void ReadLock()
|
|
{
|
|
WriteLock();
|
|
}
|
|
|
|
// Try to acquire an exclusive lock for writing. Returns true
|
|
// if successful. Non-blocking.
|
|
bool TryWriteLock()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Try to acquire a (possibly shared) lock for reading. Returns true
|
|
// if successful. Non-blocking.
|
|
bool TryReadLock()
|
|
{
|
|
return TryWriteLock();
|
|
}
|
|
|
|
// Unlock the lock after a successful call to {,Try}WriteLock().
|
|
// Assumes caller owned the lock.
|
|
void WriteUnlock()
|
|
{
|
|
KeReleaseSpinLock(&KSpinLock, m_OldIrql);
|
|
}
|
|
|
|
// Unlock the lock after a successful call to {,Try}ReadLock().
|
|
// Assumes caller owned the lock.
|
|
void ReadUnlock()
|
|
{
|
|
WriteUnlock();
|
|
}
|
|
|
|
// Is the lock already locked for writing by this thread?
|
|
bool IsWriteLocked() const
|
|
{
|
|
// A KSPIN_LOCK doesn't keep track of its owning thread/processor.
|
|
// It's either 0 (unlocked) or 1 (locked). We could keep track of
|
|
// the owner in an additional member variable, but currently we don't.
|
|
return false;
|
|
}
|
|
|
|
// Is the lock already locked for reading?
|
|
bool IsReadLocked() const
|
|
{
|
|
return IsWriteLocked();
|
|
}
|
|
|
|
// Is the lock unlocked for writing?
|
|
bool IsWriteUnlocked() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Is the lock unlocked for reading?
|
|
bool IsReadUnlocked() const
|
|
{
|
|
return IsWriteUnlocked();
|
|
}
|
|
|
|
// Convert a reader lock to a writer lock
|
|
void ConvertSharedToExclusive()
|
|
{
|
|
// no-op
|
|
}
|
|
|
|
// Convert a writer lock to a reader lock
|
|
void ConvertExclusiveToShared()
|
|
{
|
|
// no-op
|
|
}
|
|
|
|
#ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
|
|
// Set the spin count for this lock.
|
|
// Returns true if successfully set the per-lock spincount, false otherwise
|
|
bool SetSpinCount(WORD wSpins)
|
|
{
|
|
IRTLASSERT((wSpins == LOCK_DONT_SPIN)
|
|
|| (wSpins == LOCK_USE_DEFAULT_SPINS)
|
|
|| (LOCK_MINIMUM_SPINS <= wSpins
|
|
&& wSpins <= LOCK_MAXIMUM_SPINS));
|
|
|
|
return false;
|
|
}
|
|
|
|
// Return the spin count for this lock.
|
|
WORD GetSpinCount() const
|
|
{
|
|
return sm_wDefaultSpinCount;
|
|
}
|
|
|
|
LOCK_DEFAULT_SPIN_IMPLEMENTATION();
|
|
#endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
|
|
|
|
static const TCHAR* ClassName() {return _TEXT("CKSpinLock");}
|
|
}; // CKSpinLock
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CFastMutex is a mutex lock based on FAST_MUTEX
|
|
|
|
class IRTL_DLLEXP CFastMutex :
|
|
public CLockBase<LOCK_FASTMUTEX, LOCK_MUTEX,
|
|
LOCK_NON_RECURSIVE, LOCK_WAIT_HANDLE, LOCK_QUEUE_KERNEL,
|
|
LOCK_CLASS_SPIN
|
|
>
|
|
{
|
|
private:
|
|
mutable FAST_MUTEX FastMutex;
|
|
|
|
LOCK_INSTRUMENTATION_DECL();
|
|
|
|
public:
|
|
|
|
#ifndef LOCK_INSTRUMENTATION
|
|
|
|
CFastMutex()
|
|
{
|
|
ExInitializeFastMutex(&FastMutex);
|
|
}
|
|
|
|
#else // LOCK_INSTRUMENTATION
|
|
|
|
CFastMutex(
|
|
const TCHAR* ptszName)
|
|
{
|
|
ExInitializeFastMutex(&FastMutex);
|
|
LOCK_INSTRUMENTATION_INIT(ptszName);
|
|
}
|
|
|
|
#endif // LOCK_INSTRUMENTATION
|
|
|
|
#ifdef IRTLDEBUG
|
|
~CFastMutex() {}
|
|
#endif // IRTLDEBUG
|
|
|
|
// Acquire an exclusive lock for writing.
|
|
// Blocks (if needed) until acquired.
|
|
void WriteLock()
|
|
{
|
|
ExAcquireFastMutex(&FastMutex);
|
|
}
|
|
|
|
// Acquire a (possibly shared) lock for reading.
|
|
// Blocks (if needed) until acquired.
|
|
void ReadLock()
|
|
{
|
|
WriteLock();
|
|
}
|
|
|
|
// Try to acquire an exclusive lock for writing. Returns true
|
|
// if successful. Non-blocking.
|
|
bool TryWriteLock()
|
|
{
|
|
return ExTryToAcquireFastMutex(&FastMutex) != FALSE;
|
|
}
|
|
|
|
// Try to acquire a (possibly shared) lock for reading. Returns true
|
|
// if successful. Non-blocking.
|
|
bool TryReadLock()
|
|
{
|
|
return TryWriteLock();
|
|
}
|
|
|
|
// Unlock the lock after a successful call to {,Try}WriteLock().
|
|
// Assumes caller owned the lock.
|
|
void WriteUnlock()
|
|
{
|
|
ExReleaseFastMutex(&FastMutex);
|
|
}
|
|
|
|
// Unlock the lock after a successful call to {,Try}ReadLock().
|
|
// Assumes caller owned the lock.
|
|
void ReadUnlock()
|
|
{
|
|
WriteUnlock();
|
|
}
|
|
|
|
// Is the lock already locked for writing by this thread?
|
|
bool IsWriteLocked() const
|
|
{
|
|
return false; // no way of determining this w/o auxiliary info
|
|
}
|
|
|
|
// Is the lock already locked for reading?
|
|
bool IsReadLocked() const
|
|
{
|
|
return IsWriteLocked();
|
|
}
|
|
|
|
// Is the lock unlocked for writing?
|
|
bool IsWriteUnlocked() const
|
|
{
|
|
return !IsWriteLocked();
|
|
}
|
|
|
|
// Is the lock unlocked for reading?
|
|
bool IsReadUnlocked() const
|
|
{
|
|
return IsWriteUnlocked();
|
|
}
|
|
|
|
// Convert a reader lock to a writer lock
|
|
void ConvertSharedToExclusive()
|
|
{
|
|
// no-op
|
|
}
|
|
|
|
// Convert a writer lock to a reader lock
|
|
void ConvertExclusiveToShared()
|
|
{
|
|
// no-op
|
|
}
|
|
|
|
#ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
|
|
// Set the spin count for this lock.
|
|
// Returns true if successfully set the per-lock spincount, false otherwise
|
|
bool SetSpinCount(WORD wSpins)
|
|
{
|
|
IRTLASSERT((wSpins == LOCK_DONT_SPIN)
|
|
|| (wSpins == LOCK_USE_DEFAULT_SPINS)
|
|
|| (LOCK_MINIMUM_SPINS <= wSpins
|
|
&& wSpins <= LOCK_MAXIMUM_SPINS));
|
|
|
|
return false;
|
|
}
|
|
|
|
// Return the spin count for this lock.
|
|
WORD GetSpinCount() const
|
|
{
|
|
return sm_wDefaultSpinCount;
|
|
}
|
|
|
|
LOCK_DEFAULT_SPIN_IMPLEMENTATION();
|
|
#endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
|
|
|
|
static const TCHAR* ClassName() {return _TEXT("CFastMutex");}
|
|
}; // CFastMutex
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
// CEResource is a multi-reader, single-writer lock based on ERESOURCE
|
|
|
|
class IRTL_DLLEXP CEResource :
|
|
public CLockBase<LOCK_ERESOURCE, LOCK_MRSW,
|
|
LOCK_RECURSIVE, LOCK_WAIT_HANDLE, LOCK_QUEUE_KERNEL,
|
|
LOCK_CLASS_SPIN
|
|
>
|
|
{
|
|
private:
|
|
mutable ERESOURCE Resource;
|
|
|
|
public:
|
|
CEResource()
|
|
{
|
|
ExInitializeResourceLite(&Resource);
|
|
}
|
|
|
|
#ifdef LOCK_INSTRUMENTATION
|
|
CEResource(
|
|
const TCHAR* ptszName)
|
|
{
|
|
ExInitializeResourceLite(&Resource);
|
|
LOCK_INSTRUMENTATION_INIT(ptszName);
|
|
}
|
|
#endif // LOCK_INSTRUMENTATION
|
|
|
|
~CEResource()
|
|
{
|
|
ExDeleteResourceLite(&Resource);
|
|
}
|
|
|
|
inline void
|
|
WriteLock()
|
|
{
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceExclusiveLite(&Resource, TRUE);
|
|
}
|
|
|
|
inline void
|
|
ReadLock()
|
|
{
|
|
KeEnterCriticalRegion();
|
|
ExAcquireResourceSharedLite(&Resource, TRUE);
|
|
}
|
|
|
|
bool ReadOrWriteLock();
|
|
|
|
inline bool
|
|
TryWriteLock()
|
|
{
|
|
KeEnterCriticalRegion();
|
|
BOOLEAN fLocked = ExAcquireResourceExclusiveLite(&Resource, FALSE);
|
|
if (! fLocked)
|
|
KeLeaveCriticalRegion();
|
|
return fLocked != FALSE;
|
|
}
|
|
|
|
inline bool
|
|
TryReadLock()
|
|
{
|
|
KeEnterCriticalRegion();
|
|
BOOLEAN fLocked = ExAcquireResourceSharedLite(&Resource, FALSE);
|
|
if (! fLocked)
|
|
KeLeaveCriticalRegion();
|
|
return fLocked != FALSE;
|
|
}
|
|
|
|
inline void
|
|
WriteUnlock()
|
|
{
|
|
ExReleaseResourceLite(&Resource);
|
|
KeLeaveCriticalRegion();
|
|
}
|
|
|
|
inline void
|
|
ReadUnlock()
|
|
{
|
|
WriteUnlock();
|
|
}
|
|
|
|
void ReadOrWriteUnlock(bool fIsReadLocked);
|
|
|
|
// Does current thread hold a write lock?
|
|
bool
|
|
IsWriteLocked() const
|
|
{
|
|
return ExIsResourceAcquiredExclusiveLite(&Resource) != FALSE;
|
|
}
|
|
|
|
bool
|
|
IsReadLocked() const
|
|
{
|
|
return ExIsResourceAcquiredSharedLite(&Resource) > 0;
|
|
}
|
|
|
|
bool
|
|
IsWriteUnlocked() const
|
|
{ return !IsWriteLocked(); }
|
|
|
|
bool
|
|
IsReadUnlocked() const
|
|
{ return !IsReadLocked(); }
|
|
|
|
void
|
|
ConvertSharedToExclusive()
|
|
{
|
|
ReadUnlock();
|
|
WriteLock();
|
|
}
|
|
|
|
// There is no such window when converting from a writelock to a readlock
|
|
void
|
|
ConvertExclusiveToShared()
|
|
{
|
|
#if 0
|
|
ExConvertExclusiveToShared(&Resource);
|
|
#else
|
|
WriteUnlock();
|
|
ReadLock();
|
|
#endif
|
|
}
|
|
|
|
#ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
|
|
bool
|
|
SetSpinCount(WORD wSpins)
|
|
{return false;}
|
|
|
|
WORD
|
|
GetSpinCount() const
|
|
{return sm_wDefaultSpinCount;}
|
|
|
|
LOCK_DEFAULT_SPIN_IMPLEMENTATION();
|
|
#endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
|
|
|
|
static const TCHAR*
|
|
ClassName()
|
|
{return _TEXT("CEResource");}
|
|
|
|
}; // CEResource
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
//--------------------------------------------------------------------
|
|
// CRtlMrswLock is a multi-reader, single-writer lock from the TCP team
|
|
//
|
|
// The following structure and routines implement a multiple reader,
|
|
// single writer lock. The lock may be used from PASSIVE_LEVEL to
|
|
// DISPATCH_LEVEL.
|
|
//
|
|
// While the lock is held by readers or writer, the IRQL is
|
|
// raised to DISPATCH_LEVEL. As a result, the memory for the
|
|
// CRtlMrswLock structure must reside in non-paged pool. The
|
|
// lock is "fair" in the sense that waiters are granted the lock
|
|
// in the order requested. This is achieved via the use of a
|
|
// queued spinlock internally.
|
|
//
|
|
// The lock can be recursively acquired by readers, but not by writers.
|
|
//
|
|
// There is no support for upgrading (downgrading) a read (write) lock to
|
|
// a write (read) lock.
|
|
|
|
class IRTL_DLLEXP CRtlMrswLock :
|
|
public CLockBase<LOCK_RTL_MRSW_LOCK, LOCK_MRSW,
|
|
LOCK_READ_RECURSIVE, LOCK_WAIT_SPIN, LOCK_QUEUE_KERNEL,
|
|
LOCK_CLASS_SPIN
|
|
>
|
|
{
|
|
private:
|
|
mutable KSPIN_LOCK ExclusiveLock;
|
|
volatile LONG ReaderCount;
|
|
|
|
public:
|
|
CRtlMrswLock()
|
|
{
|
|
KeInitializeSpinLock(&ExclusiveLock);
|
|
ReaderCount = 0;
|
|
}
|
|
|
|
#ifdef LOCK_INSTRUMENTATION
|
|
CRtlMrswLock(
|
|
const TCHAR* ptszName)
|
|
{
|
|
KeInitializeSpinLock(&ExclusiveLock);
|
|
ReaderCount = 0;
|
|
LOCK_INSTRUMENTATION_INIT(ptszName);
|
|
}
|
|
#endif // LOCK_INSTRUMENTATION
|
|
|
|
~CRtlMrswLock()
|
|
{
|
|
IRTLASSERT(ReaderCount == 0);
|
|
// KeUninitializeSpinLock(&ExclusiveLock);
|
|
}
|
|
|
|
inline void
|
|
WriteLockAtDpcLevel(
|
|
OUT PKLOCK_QUEUE_HANDLE LockHandle)
|
|
{
|
|
//
|
|
// Wait for the writer (if there is one) to release.
|
|
//
|
|
KeAcquireInStackQueuedSpinLockAtDpcLevel(&ExclusiveLock, LockHandle);
|
|
|
|
//
|
|
// Now wait for all readers to release.
|
|
//
|
|
while (ReaderCount != 0)
|
|
{}
|
|
}
|
|
|
|
inline void
|
|
ReadLockAtDpcLevel()
|
|
{
|
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|
|
|
//
|
|
// Wait for the writer (if there is one) to release.
|
|
//
|
|
KeAcquireInStackQueuedSpinLockAtDpcLevel(&ExclusiveLock, &LockHandle);
|
|
|
|
//
|
|
// Now that we have the exclusive lock, we know there are no writers.
|
|
// Bump the reader count to cause any new writer to wait.
|
|
// We use an interlock here becasue the decrement path is not done
|
|
// under the exclusive lock.
|
|
//
|
|
InterlockedIncrement(const_cast<LONG*>(&ReaderCount));
|
|
|
|
KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
|
|
}
|
|
|
|
inline void
|
|
WriteLock(
|
|
OUT PKLOCK_QUEUE_HANDLE LockHandle)
|
|
{
|
|
//
|
|
// Wait for the writer (if there is one) to release.
|
|
//
|
|
KeAcquireInStackQueuedSpinLock(&ExclusiveLock, LockHandle);
|
|
|
|
//
|
|
// Now wait for all readers to release.
|
|
//
|
|
while (ReaderCount != 0)
|
|
{}
|
|
}
|
|
|
|
inline void
|
|
ReadLock(
|
|
OUT PKIRQL OldIrql)
|
|
{
|
|
*OldIrql = KeRaiseIrqlToDpcLevel();
|
|
ReadLockAtDpcLevel();
|
|
}
|
|
|
|
inline bool
|
|
TryWriteLock()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
inline bool
|
|
TryReadLock()
|
|
{
|
|
// TODO
|
|
return false;
|
|
}
|
|
|
|
inline void
|
|
WriteUnlockFromDpcLevel(
|
|
IN PKLOCK_QUEUE_HANDLE LockHandle)
|
|
{
|
|
KeReleaseInStackQueuedSpinLockFromDpcLevel(LockHandle);
|
|
}
|
|
|
|
inline void
|
|
ReadUnlockFromDpcLevel()
|
|
{
|
|
//
|
|
// Decrement the reader count. This will cause any waiting writer
|
|
// to wake up and acquire the lock if the reader count becomes zero.
|
|
//
|
|
InterlockedDecrement(const_cast<LONG*>(&ReaderCount));
|
|
}
|
|
|
|
inline void
|
|
WriteUnlock(
|
|
IN PKLOCK_QUEUE_HANDLE LockHandle)
|
|
{
|
|
KeReleaseInStackQueuedSpinLock(LockHandle);
|
|
}
|
|
|
|
inline void
|
|
ReadUnlock(
|
|
IN KIRQL OldIrql)
|
|
{
|
|
ReadUnlockFromDpcLevel();
|
|
KeLowerIrql(OldIrql);
|
|
}
|
|
|
|
void ReadOrWriteUnlock(bool fIsReadLocked);
|
|
|
|
// Does current thread hold a write lock?
|
|
bool
|
|
IsWriteLocked() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
IsReadLocked() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
IsWriteUnlocked() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
IsReadUnlocked() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void
|
|
ConvertSharedToExclusive()
|
|
{
|
|
// ReadUnlock();
|
|
// WriteLock();
|
|
}
|
|
|
|
void
|
|
ConvertExclusiveToShared()
|
|
{
|
|
// WriteUnlock();
|
|
// ReadLock();
|
|
}
|
|
|
|
#ifdef LOCK_DEFAULT_SPIN_IMPLEMENTATION
|
|
bool
|
|
SetSpinCount(WORD)
|
|
{return false;}
|
|
|
|
WORD
|
|
GetSpinCount() const
|
|
{return sm_wDefaultSpinCount;}
|
|
|
|
LOCK_DEFAULT_SPIN_IMPLEMENTATION();
|
|
#endif // LOCK_DEFAULT_SPIN_IMPLEMENTATION
|
|
|
|
static const TCHAR*
|
|
ClassName()
|
|
{return _TEXT("CRtlMrswLock");}
|
|
|
|
}; // CRtlMrswLock
|
|
|
|
#endif
|
|
|
|
#endif // __KLOCKS_H__
|