OS-version independent implementation of ReadWriteLock API for Windows NT3.51/NT4/2000/XP/2003 kernel mode
by DeathSoft
Here you can find implementaions of RW-locks, compatible with NDIS RWLock. This implementaion is compatible with entire NT-family.
RW-lock is a synchronization primitive, similar to ERESOURCE. In contrast to ERESOURCE, RW-lock can be used on
IRQLs up to DISPATCH_LEVEL.
Is used for managing parallel read-only vs read/write access (shared/exlusive) to common data structures.
Since RW-lock acquisition raises
IRQL, it operates like SpinLock on uniprocessor machines. On multiprocessor systems RW-lock can give performance
benefit since you can implement parallel read access to control structures from different CPUs.
Usage
-
Initialize NDIS_RW_LOCK structure with InitializeReadWriteLock().
This structure must be allocated from NonPagedPool.
VOID
InitializeReadWriteLock(
IN PNDIS_RW_LOCK Lock
);
|
-
Use the following functions to acquire/release RW_LOCK:
VOID
AcquireReadWriteLock(
IN PNDIS_RW_LOCK Lock,
IN BOOLEAN fWrite,
IN PLOCK_STATE LockState
);
|
|
VOID
ReleaseReadWriteLock(
IN PNDIS_RW_LOCK Lock,
IN PLOCK_STATE LockState
);
|
fWrite can be either RWLOCK_FOR_WRITE or RWLOCK_FOR_READ. LockState structure must be allocated from
NonPagedPool. This structure contains context, which is required for releasing RW_LOCK.
Attention: it is permitted to make multiple (recursive) acquisitions of RW_LOCK in a single thread,
but(!) you must allocate separate LOCK_STATE structure for each
AcquireReadWriteLock()/ReleaseReadWriteLock() pair.
Also, you must keep proper nesting:
AcquireReadWriteLock(pLock, RWLOCK_FOR_READ, &LockState1);
...
AcquireReadWriteLock(pLock, RWLOCK_FOR_WRITE, &LockState2);
ReleaseReadWriteLock(pLock, &LockState2);
...
ReleaseReadWriteLock(pLock, &LockState1);
Sources
rwlock.h
#ifndef __CROSS_NT_RWLOCK__H__
#define __CROSS_NT_RWLOCK__H__
#ifndef MAXIMUM_PROCESSORS
#define MAXIMUM_PROCESSORS 32
#endif
typedef union _NDIS_RW_LOCK_REFCOUNT {
unsigned int RefCount;
UCHAR cacheLine[16]; // One refCount per cache line
} NDIS_RW_LOCK_REFCOUNT;
typedef struct _NDIS_RW_LOCK {
union {
struct {
KSPIN_LOCK SpinLock;
PVOID Context;
};
UCHAR Reserved[16];
};
NDIS_RW_LOCK_REFCOUNT RefCount[MAXIMUM_PROCESSORS];
} NDIS_RW_LOCK, *PNDIS_RW_LOCK;
typedef struct _LOCK_STATE {
USHORT LockState;
KIRQL OldIrql;
} LOCK_STATE, *PLOCK_STATE;
#define RWLOCK_STATE_FREE 0
#define RWLOCK_STATE_READ_ACQUIRED 1
#define RWLOCK_STATE_WRITE_ACQUIRED 2
#define RWLOCK_STATE_RECURSIVE 3
#define RWLOCK_STATE_RELEASED 0xffff
#define RWLOCK_FOR_WRITE TRUE
#define RWLOCK_FOR_READ FALSE
#endif __CROSS_NT_RWLOCK__H__
rwlock.cpp
VOID
InitializeReadWriteLock(
IN PNDIS_RW_LOCK Lock
)
{
RtlZeroMemory(Lock, sizeof(*Lock));
} // end InitializeReadWriteLock()
#define RWLOCK_STATE_FREE 0
#define RWLOCK_STATE_READ_ACQUIRED 1
#define RWLOCK_STATE_WRITE_ACQUIRED 2
#define RWLOCK_STATE_RECURSIVE 3
#define RWLOCK_STATE_RELEASED 0xffff
#define RWLOCK_FOR_WRITE TRUE
#define RWLOCK_FOR_READ FALSE
VOID
AcquireReadWriteLock(
IN PNDIS_RW_LOCK Lock,
IN BOOLEAN fWrite,
IN PLOCK_STATE LockState
)
{
ULONG n, m, i, Ref;
/*
// will raise assert if LockState is not initialized with zeros,
// however, this is useful check for debugging.
ASSERT(LockState->LockState == RWLOCK_STATE_FREE ||
LockState->LockState == RWLOCK_STATE_RELEASED);
*/
if(fWrite)
{
if(Lock->Context==KeGetCurrentThread())
{
// recursive acquisition from same thread (LockState must be different!)
LockState->LockState=RWLOCK_STATE_RECURSIVE;
return;
}
KeAcquireSpinLock(&Lock->SpinLock, &LockState->OldIrql);
n=KeGetCurrentProcessorNumber();
Ref=Lock->RefCount[n].RefCount; // save reference count for current CPU
Lock->RefCount[n].RefCount=0;
m=*KeNumberProcessors; // number of system processors
i=0;
do
{
while(Lock->RefCount[i].RefCount==0)
{
i++;
m--;
if(m==0) // release lock (all CPUs have RefCount==0)
{
Lock->RefCount[n].RefCount=Ref; // restore reference count for current CPU
Lock->Context=KeGetCurrentThread();
LockState->LockState=RWLOCK_STATE_WRITE_ACQUIRED;
return;
}
}
for(volatile ULONG j=0;j<50;j++); // do nothing 50 times
}while(TRUE);
// never get here
}
else // read
{
LockState->OldIrql=KeGetCurrentIrql();
if(LockState->OldIrqlOldIrql=KeRaiseIrqlToDpcLevel();
}
n=KeGetCurrentProcessorNumber();
Ref=InterlockedIncrement((PLONG)&Lock->RefCount[n].RefCount);
if(Lock->SpinLock && Ref==1 && Lock->Context!=KeGetCurrentThread())
{
InterlockedDecrement((PLONG)&Lock->RefCount[n].RefCount);
KeAcquireSpinLockAtDpcLevel(&Lock->SpinLock);
InterlockedIncrement((PLONG)&Lock->RefCount[n].RefCount);
KeReleaseSpinLockFromDpcLevel(&Lock->SpinLock);
}
LockState->LockState=RWLOCK_STATE_READ_ACQUIRED;
return;
}
} // end AcquireReadWriteLock()
VOID
ReleaseReadWriteLock(
IN PNDIS_RW_LOCK Lock,
IN PLOCK_STATE LockState
)
{
switch(LockState->LockState) {
case RWLOCK_STATE_READ_ACQUIRED:
InterlockedDecrement((PLONG)&Lock->RefCount[KeGetCurrentProcessorNumber()].RefCount);
LockState->LockState=RWLOCK_STATE_RELEASED;
if(LockState->OldIrqlOldIrql);
}
return;
case RWLOCK_STATE_WRITE_ACQUIRED:
LockState->LockState=RWLOCK_STATE_RELEASED;
Lock->Context=0;
KeReleaseSpinLock(&Lock->SpinLock, LockState->OldIrql);
case RWLOCK_STATE_RECURSIVE:
// do nothing
return;
default:
ASSERT(FALSE);
return;
} // end switch(LockState->LockState)
} // end ReleaseReadWriteLock()
2007.01.27, last update 2007.02.18
See also:
|