Alter.Org.UA
 << Back Home EN en   Donate Donate

OS-version independent implementation of ReadWriteLock API
for 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'а используются
    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 может принимать значения RWLOCK_FOR_WRITE и RWLOCK_FOR_READ. Структура LockState должна быть выделена из NonPagedPool. Эта структура содержит контекст, необходимый для освобождения RW_LOCK'а.
    Внимание: рекурсивный захват 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

См. также

<< Back Автор: Alter (Александр А. Телятников) Сервер: Apache+PHP под FBSD © 2002-2024