| OS-version independent implementation of ReadWriteLock APIfor Windows NT3.51/NT4/2000/XP/2003 kernel mode
by DeathSoft
    Ниже представлены функции работы с RW-locks, совместимые с NDIS RWLock и работающие во всех версиях NT.
    RW-locks - это синхронизационный ресурс, похожий на ERESOURCE, но способный работать на IRQL до DISPATCH_LEVEL включительно.
    Применяется для разграничения read-only и read/write (shared/exlusive) доступа к данным в многопроцессорных системах. Т.к. захват
    RW-lock'а сопровождается повышением IRQL, на однопроцессорных машинах это эквивалентно работе со SpinLock'ами.
    А вот на многопроцессорках можно получить выигрыш за счет параллельного доступа на чтение с разных процессоров.
 Использование
    Инициализировать NDIS_RW_LOCK с помощью InitializeReadWriteLock().
    Структура NDIS_RW_LOCK должна находиться в NonPagedPool.
| 
VOID
InitializeReadWriteLock(
    IN PNDIS_RW_LOCK Lock
    );
 | 
    Для захвата и освобождения RW_LOCK'а используются
fWrite может принимать значения RWLOCK_FOR_WRITE и RWLOCK_FOR_READ. Структура LockState должна быть выделена из
    NonPagedPool. Эта структура содержит контекст, необходимый для освобождения 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
    );
 | 
 Внимание: рекурсивный захват RW_LOCK'а одним потоком допускается, но(!) на каждую пару
    AcquireReadWriteLock()/ReleaseReadWriteLock() должена быть отдельная контекстная структура LOCK_STATE.
    При этом должна соблюдаться вложеность. Т.е.
 
AcquireReadWriteLock(pLock, RWLOCK_FOR_READ, &LockState1);
...
  AcquireReadWriteLock(pLock, RWLOCK_FOR_WRITE, &LockState2);
  ReleaseReadWriteLock(pLock, &LockState2);
...
ReleaseReadWriteLock(pLock, &LockState1);
 Исходники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
 
 
См. также
     |