Alter.Org.UA
 << Back Home UK uk   Donate Donate

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:

<< Back designed by Alter aka Alexander A. Telyatnikov powered by Apache+PHP under FBSD © 2002-2024