Implement Slim Reader/Writer (SRW) locks:

- Implement AcquireSRWLockExclusive(), AcquireSRWLockShared(), InitializeSRWLock(), ReleaseSRWLockExclusive(), ReleaseSRWLockShared()
- NOTE: Some versions of GCC have a code generation bug with specially aligned structures on the stack. If compiled with such a compiler, the code might trigger special assertions. Pushlocks are also affected by this compiler bug.
- NOTE: The algorithms are most likely not the same as in Windows. Applications are supposed to treat the lock variables as opaque data, therefore it shouldn't matter.

svn path=/trunk/; revision=23806
This commit is contained in:
Thomas Bluemel 2006-08-30 19:02:42 +00:00
parent 538082297d
commit 900bf1a753
6 changed files with 873 additions and 0 deletions

View file

@ -295,6 +295,8 @@ RtlAbsoluteToSelfRelativeSD@12
RtlAcquirePebLock@0
RtlAcquireResourceExclusive@8
RtlAcquireResourceShared@8
RtlAcquireSRWLockExclusive@4
RtlAcquireSRWLockShared@4
RtlAddAccessAllowedAce@16
RtlAddAccessAllowedAceEx@20
RtlAddAccessAllowedObjectAce@28
@ -513,6 +515,7 @@ RtlInitializeRangeList@4
RtlInitializeResource@4
;RtlInitializeRXact
RtlInitializeSid@12
RtlInitializeSRWLock@4
RtlInsertElementGenericTable@16
RtlInsertElementGenericTableAvl@16
RtlInsertElementGenericTableFull@24
@ -612,6 +615,8 @@ RtlRealSuccessor@4
RtlReleasePebLock@0
RtlReleaseRelativeName@4
RtlReleaseResource@4
RtlReleaseSRWLockExclusive@4
RtlReleaseSRWLockShared@4
;RtlRemoteCall
RtlRemoveVectoredExceptionHandler@4
RtlResetRtlTranslations@4

View file

@ -30,6 +30,8 @@
;
LIBRARY KERNEL32.DLL
EXPORTS
AcquireSRWLockExclusive@4=NTDLL.RtlAcquireSRWLockExclusive
AcquireSRWLockShared@4=NTDLL.RtlAcquireSRWLockShared
ActivateActCtx@8
AddAtomA@4
AddAtomW@4
@ -587,6 +589,7 @@ InitAtomTable@4
InitializeCriticalSection@4
InitializeCriticalSectionAndSpinCount@8
InitializeSListHead@4=NTDLL.RtlInitializeSListHead
InitializeSRWLock@4=NTDLL.RtlInitializeSRWLock
InterlockedCompareExchange@12
InterlockedDecrement@4
InterlockedExchange@8
@ -749,6 +752,8 @@ RegisterWowExec@4
ReleaseActCtx@4
ReleaseMutex@4
ReleaseSemaphore@12
ReleaseSRWLockExclusive@4=NTDLL.RtlReleaseSRWLockExclusive
ReleaseSRWLockShared@4=NTDLL.RtlReleaseSRWLockShared
RemoveDirectoryA@4
RemoveDirectoryW@4
;RemoveLocalAlternateComputerNameA

View file

@ -540,6 +540,9 @@ extern "C" {
#define CREATE_EVENT_INITIAL_SET 0x2
#define CREATE_MUTEX_INITIAL_OWNER 0x1
#define CREATE_WAITABLE_TIMER_MANUAL_RESET 0x1
#define SRWLOCK_INIT RTL_SRWLOCK_INIT
#define CONDITION_VARIABLE_INIT RTL_CONDITION_VARIABLE_INIT
#define CONDITION_VARIABLE_LOCKMODE_SHARED RTL_CONDITION_VARIABLE_LOCKMODE_SHARED
#endif
#ifndef RC_INVOKED
@ -1053,6 +1056,9 @@ typedef struct _JOB_SET_ARRAY {
DWORD MemberLevel;
DWORD Flags;
} JOB_SET_ARRAY, *PJOB_SET_ARRAY;
#if (_WIN32_WINNT >= 0x0600)
typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK;
#endif
typedef DWORD(WINAPI *LPPROGRESS_ROUTINE)(LARGE_INTEGER,LARGE_INTEGER,LARGE_INTEGER,LARGE_INTEGER,DWORD,DWORD,HANDLE,HANDLE,LPVOID);
typedef void(WINAPI *LPFIBER_START_ROUTINE)(PVOID);
typedef VOID (WINAPI *PFLS_CALLBACK_FUNCTION)(PVOID);
@ -1093,6 +1099,10 @@ UINT WINAPI _lwrite(HFILE,LPCSTR,UINT);
BOOL WINAPI AccessCheck(PSECURITY_DESCRIPTOR,HANDLE,DWORD,PGENERIC_MAPPING,PPRIVILEGE_SET,PDWORD,PDWORD,PBOOL);
BOOL WINAPI AccessCheckAndAuditAlarmA(LPCSTR,LPVOID,LPSTR,LPSTR,PSECURITY_DESCRIPTOR,DWORD,PGENERIC_MAPPING,BOOL,PDWORD,PBOOL,PBOOL);
BOOL WINAPI AccessCheckAndAuditAlarmW(LPCWSTR,LPVOID,LPWSTR,LPWSTR,PSECURITY_DESCRIPTOR,DWORD,PGENERIC_MAPPING,BOOL,PDWORD,PBOOL,PBOOL);
#if (_WIN32_WINNT >= 0x0600)
VOID WINAPI AcquireSRWLockExclusive(PSRWLOCK);
VOID WINAPI AcquireSRWLockShared(PSRWLOCK);
#endif
#if (_WIN32_WINNT >= 0x0501)
BOOL WINAPI ActivateActCtx(HANDLE,ULONG_PTR*);
#endif
@ -1660,6 +1670,9 @@ BOOL WINAPI InitializeSid (PSID,PSID_IDENTIFIER_AUTHORITY,BYTE);
#if !defined(__WINDDK_H) && _WIN32_WINNT >= 0x0501
VOID WINAPI InitializeSListHead(PSLIST_HEADER);
#endif
#if (_WIN32_WINNT >= 0x0600)
VOID WINAPI InitializeSRWLock(PSRWLOCK);
#endif
#ifndef __INTERLOCKED_DECLARED
#define __INTERLOCKED_DECLARED
LONG WINAPI InterlockedCompareExchange(LPLONG,LONG,LONG);
@ -1842,6 +1855,10 @@ void WINAPI ReleaseActCtx(HANDLE);
#endif
BOOL WINAPI ReleaseMutex(HANDLE);
BOOL WINAPI ReleaseSemaphore(HANDLE,LONG,LPLONG);
#if (_WIN32_WINNT >= 0x0600)
VOID WINAPI ReleaseSRWLockExclusive(PSRWLOCK);
VOID WINAPI ReleaseSRWLockShared(PSRWLOCK);
#endif
BOOL WINAPI RemoveDirectoryA(LPCSTR);
BOOL WINAPI RemoveDirectoryW(LPCWSTR);
#if (_WIN32_WINNT >= 0x0500)

View file

@ -2626,6 +2626,19 @@ typedef struct _RTL_CRITICAL_SECTION {
} RTL_CRITICAL_SECTION,*PRTL_CRITICAL_SECTION;
#endif
#define RTL_SRWLOCK_INIT {0}
typedef struct _RTL_SRWLOCK
{
PVOID Ptr;
} RTL_SRWLOCK, *PRTL_SRWLOCK;
#define RTL_CONDITION_VARIABLE_INIT {0}
#define RTL_CONDITION_VARIABLE_LOCKMODE_SHARED 0x1
typedef struct _RTL_CONDITION_VARIABLE
{
PVOID Ptr;
} RTL_CONDITION_VARIABLE, *PRTL_CONDITION_VARIABLE;
typedef LONG
(NTAPI *PVECTORED_EXCEPTION_HANDLER)(
struct _EXCEPTION_POINTERS *ExceptionInfo

View file

@ -84,6 +84,7 @@
<file>security.c</file>
<file>sid.c</file>
<file>sprintf.c</file>
<file>srw.c</file>
<file>swprintf.c</file>
<file>splaytree.c</file>
<file>thread.c</file>

832
reactos/lib/rtl/srw.c Normal file
View file

@ -0,0 +1,832 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* PURPOSE: Slim Reader/Writer (SRW) Routines
* PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com>
*
* NOTES: The algorithms used in this implementation
* may be different from Vista's implementation.
* Since applications should treat the RTL_SRWLOCK
* structure as opaque data, it should not matter.
* The algorithms are probably not as optimized.
*/
/* INCLUDES *****************************************************************/
#include <rtl.h>
#define NDEBUG
#include <debug.h>
/* FIXME *********************************************************************/
/* FIXME: Interlocked functions that need to be made into a public header */
FORCEINLINE
LONG
InterlockedAnd(IN OUT volatile LONG *Target,
IN LONG Set)
{
LONG i;
LONG j;
j = *Target;
do {
i = j;
j = InterlockedCompareExchange((PLONG)Target,
i & Set,
i);
} while (i != j);
return j;
}
FORCEINLINE
LONG
InterlockedOr(IN OUT volatile LONG *Target,
IN LONG Set)
{
LONG i;
LONG j;
j = *Target;
do {
i = j;
j = InterlockedCompareExchange((PLONG)Target,
i | Set,
i);
} while (i != j);
return j;
}
/* FUNCTIONS *****************************************************************/
#ifdef _WIN64
#define InterlockedBitTestAndSetPointer(ptr,val) InterlockedBitTestAndSet64((PLONGLONG)ptr,(LONGLONG)val)
#define InterlockedAddPointer(ptr,val) InterlockedAdd64((PLONGLONG)ptr,(LONGLONG)val)
#define InterlockedAndPointer(ptr,val) InterlockedAnd64((PLONGLONG)ptr,(LONGLONG)val)
#define InterlockedOrPointer(ptr,val) InterlockedOr64((PLONGLONG)ptr,(LONGLONG)val)
#else
#define InterlockedBitTestAndSetPointer(ptr,val) InterlockedBitTestAndSet((PLONG)ptr,(LONG)val)
#define InterlockedAddPointer(ptr,val) InterlockedAdd((PLONG)ptr,(LONG)val)
#define InterlockedAndPointer(ptr,val) InterlockedAnd((PLONG)ptr,(LONG)val)
#define InterlockedOrPointer(ptr,val) InterlockedOr((PLONG)ptr,(LONG)val)
#endif
#define RTL_SRWLOCK_OWNED_BIT 0
#define RTL_SRWLOCK_CONTENDED_BIT 1
#define RTL_SRWLOCK_SHARED_BIT 2
#define RTL_SRWLOCK_CONTENTION_LOCK_BIT 3
#define RTL_SRWLOCK_OWNED (1 << RTL_SRWLOCK_OWNED_BIT)
#define RTL_SRWLOCK_CONTENDED (1 << RTL_SRWLOCK_CONTENDED_BIT)
#define RTL_SRWLOCK_SHARED (1 << RTL_SRWLOCK_SHARED_BIT)
#define RTL_SRWLOCK_CONTENTION_LOCK (1 << RTL_SRWLOCK_CONTENTION_LOCK_BIT)
#define RTL_SRWLOCK_MASK (RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED | \
RTL_SRWLOCK_SHARED | RTL_SRWLOCK_CONTENTION_LOCK)
#define RTL_SRWLOCK_BITS 4
#if defined(__GNUC__)
/* This macro will cause the code to assert if compiled with a buggy
version of GCC that doesn't align the wait blocks properly on the stack! */
#define ASSERT_SRW_WAITBLOCK(ptr) \
ASSERT(((ULONG_PTR)ptr & ((1 << RTL_SRWLOCK_BITS) - 1)) == 0)
#else
#define ASSERT_SRW_WAITBLOCK(ptr)
#endif
typedef struct _RTLP_SRWLOCK_SHARED_WAKE
{
LONG Wake;
volatile struct _RTLP_SRWLOCK_SHARED_WAKE *Next;
} volatile RTLP_SRWLOCK_SHARED_WAKE, *PRTLP_SRWLOCK_SHARED_WAKE;
typedef struct _RTLP_SRWLOCK_WAITBLOCK
{
/* SharedCount is the number of shared acquirers. */
LONG SharedCount;
/* Last points to the last wait block in the chain. The value
is only valid when read from the first wait block. */
volatile struct _RTLP_SRWLOCK_WAITBLOCK *Last;
/* Next points to the next wait block in the chain. */
volatile struct _RTLP_SRWLOCK_WAITBLOCK *Next;
union
{
/* Wake is only valid for exclusive wait blocks */
LONG Wake;
/* The wake chain is only valid for shared wait blocks */
struct
{
PRTLP_SRWLOCK_SHARED_WAKE SharedWakeChain;
PRTLP_SRWLOCK_SHARED_WAKE LastSharedWake;
};
};
BOOLEAN Exclusive;
} volatile RTLP_SRWLOCK_WAITBLOCK, *PRTLP_SRWLOCK_WAITBLOCK;
static VOID
NTAPI
RtlpReleaseWaitBlockLockExclusive(IN OUT PRTL_SRWLOCK SRWLock,
IN PRTLP_SRWLOCK_WAITBLOCK FirstWaitBlock)
{
PRTLP_SRWLOCK_WAITBLOCK Next;
LONG_PTR NewValue;
/* NOTE: We're currently in an exclusive lock in contended mode. */
Next = FirstWaitBlock->Next;
if (Next != NULL)
{
/* There's more blocks chained, we need to update the pointers
in the next wait block and update the wait block pointer. */
NewValue = (LONG_PTR)Next | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
if (!FirstWaitBlock->Exclusive)
{
/* The next wait block has to be an exclusive lock! */
ASSERT(Next->Exclusive);
/* Save the shared count */
Next->SharedCount = FirstWaitBlock->SharedCount;
NewValue |= RTL_SRWLOCK_SHARED;
}
Next->Last = FirstWaitBlock->Last;
}
else
{
/* Convert the lock to a simple lock. */
if (FirstWaitBlock->Exclusive)
NewValue = RTL_SRWLOCK_OWNED;
else
{
ASSERT(FirstWaitBlock->SharedCount > 0);
NewValue = ((LONG_PTR)FirstWaitBlock->SharedCount << RTL_SRWLOCK_BITS) |
RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED;
}
}
(void)InterlockedExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue);
if (FirstWaitBlock->Exclusive)
{
(void)InterlockedOr(&FirstWaitBlock->Wake,
TRUE);
}
else
{
PRTLP_SRWLOCK_SHARED_WAKE WakeChain, Next;
/* If we were the first one to acquire the shared
lock, we now need to wake all others... */
WakeChain = FirstWaitBlock->SharedWakeChain;
do
{
Next = WakeChain->Next;
(void)InterlockedOr((PLONG)&WakeChain->Wake,
TRUE);
WakeChain = Next;
} while (WakeChain != NULL);
}
}
static VOID
NTAPI
RtlpReleaseWaitBlockLockLastShared(IN OUT PRTL_SRWLOCK SRWLock,
IN PRTLP_SRWLOCK_WAITBLOCK FirstWaitBlock)
{
PRTLP_SRWLOCK_WAITBLOCK Next;
LONG_PTR NewValue;
/* NOTE: We're currently in a shared lock in contended mode. */
/* The next acquirer to be unwaited *must* be an exclusive lock! */
ASSERT(FirstWaitBlock->Exclusive);
Next = FirstWaitBlock->Next;
if (Next != NULL)
{
/* There's more blocks chained, we need to update the pointers
in the next wait block and update the wait block pointer. */
NewValue = (LONG_PTR)Next | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
Next->Last = FirstWaitBlock->Last;
}
else
{
/* Convert the lock to a simple exclusive lock. */
NewValue = RTL_SRWLOCK_OWNED;
}
(void)InterlockedExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue);
(void)InterlockedOr(&FirstWaitBlock->Wake,
TRUE);
}
static VOID
NTAPI
RtlpReleaseWaitBlockLock(IN OUT PRTL_SRWLOCK SRWLock)
{
InterlockedAndPointer(&SRWLock->Ptr,
~RTL_SRWLOCK_CONTENTION_LOCK);
}
static PRTLP_SRWLOCK_WAITBLOCK
NTAPI
RtlpAcquireWaitBlockLock(IN OUT PRTL_SRWLOCK SRWLock)
{
LONG_PTR PrevValue;
PRTLP_SRWLOCK_WAITBLOCK WaitBlock;
while (1)
{
PrevValue = InterlockedOrPointer(&SRWLock->Ptr,
RTL_SRWLOCK_CONTENTION_LOCK);
if (!(PrevValue & RTL_SRWLOCK_CONTENTION_LOCK))
break;
YieldProcessor();
}
if (!(PrevValue & RTL_SRWLOCK_CONTENDED) ||
(PrevValue & ~RTL_SRWLOCK_MASK) == 0)
{
/* Too bad, looks like the wait block was removed in the
meanwhile, unlock again */
RtlpReleaseWaitBlockLock(SRWLock);
return NULL;
}
WaitBlock = (PRTLP_SRWLOCK_WAITBLOCK)(PrevValue & ~RTL_SRWLOCK_MASK);
ASSERT_SRW_WAITBLOCK(WaitBlock);
return WaitBlock;
}
static VOID
NTAPI
RtlpAcquireSRWLockExclusiveWait(IN OUT PRTL_SRWLOCK SRWLock,
IN PRTLP_SRWLOCK_WAITBLOCK WaitBlock)
{
LONG_PTR CurrentValue;
while (1)
{
CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
if (!(CurrentValue & RTL_SRWLOCK_SHARED))
{
if (CurrentValue & RTL_SRWLOCK_CONTENDED)
{
if (WaitBlock->Wake != 0)
{
/* Our wait block became the first one
in the chain, we own the lock now! */
break;
}
}
else
{
/* The last wait block was removed and/or we're
finally a simple exclusive lock. This means we
don't need to wait anymore, we acquired the lock! */
break;
}
}
YieldProcessor();
}
}
static VOID
NTAPI
RtlpAcquireSRWLockSharedWait(IN OUT PRTL_SRWLOCK SRWLock,
IN OUT PRTLP_SRWLOCK_WAITBLOCK FirstWait OPTIONAL,
IN OUT PRTLP_SRWLOCK_SHARED_WAKE WakeChain)
{
if (FirstWait != NULL)
{
while (WakeChain->Wake == 0)
{
YieldProcessor();
}
}
else
{
LONG_PTR CurrentValue;
while (1)
{
CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
if (CurrentValue & RTL_SRWLOCK_SHARED)
{
/* The RTL_SRWLOCK_OWNED bit always needs to be set when
RTL_SRWLOCK_SHARED is set! */
ASSERT(CurrentValue & RTL_SRWLOCK_OWNED);
if (CurrentValue & RTL_SRWLOCK_CONTENDED)
{
if (WakeChain->Wake != 0)
{
/* Our wait block became the first one
in the chain, we own the lock now! */
break;
}
}
else
{
/* The last wait block was removed and/or we're
finally a simple shared lock. This means we
don't need to wait anymore, we acquired the lock! */
break;
}
}
YieldProcessor();
}
}
}
VOID
NTAPI
RtlInitializeSRWLock(OUT PRTL_SRWLOCK SRWLock)
{
SRWLock->Ptr = NULL;
}
VOID
NTAPI
RtlAcquireSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock)
{
__ALIGNED(16) RTLP_SRWLOCK_WAITBLOCK StackWaitBlock;
RTLP_SRWLOCK_SHARED_WAKE SharedWake;
LONG_PTR CurrentValue, NewValue;
PRTLP_SRWLOCK_WAITBLOCK First, Shared, FirstWait;
while (1)
{
CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
if (CurrentValue & RTL_SRWLOCK_SHARED)
{
/* NOTE: It is possible that the RTL_SRWLOCK_OWNED bit is set! */
if (CurrentValue & RTL_SRWLOCK_CONTENDED)
{
/* There's other waiters already, lock the wait blocks and
increment the shared count */
First = RtlpAcquireWaitBlockLock(SRWLock);
if (First != NULL)
{
FirstWait = NULL;
if (First->Exclusive)
{
/* We need to setup a new wait block! Although
we're currently in a shared lock and we're acquiring
a shared lock, there are exclusive locks queued. We need
to wait until those are released. */
Shared = First->Last;
if (Shared->Exclusive)
{
StackWaitBlock.Exclusive = FALSE;
StackWaitBlock.SharedCount = 1;
StackWaitBlock.Next = NULL;
StackWaitBlock.Last = &StackWaitBlock;
StackWaitBlock.SharedWakeChain = &SharedWake;
Shared->Next = &StackWaitBlock;
First->Last = &StackWaitBlock;
Shared = &StackWaitBlock;
FirstWait = &StackWaitBlock;
}
else
{
Shared->LastSharedWake->Next = &SharedWake;
Shared->SharedCount++;
}
}
else
{
Shared = First;
Shared->LastSharedWake->Next = &SharedWake;
Shared->SharedCount++;
}
SharedWake.Next = NULL;
SharedWake.Wake = 0;
Shared->LastSharedWake = &SharedWake;
ASSERT_SRW_WAITBLOCK(Shared);
RtlpReleaseWaitBlockLock(SRWLock);
RtlpAcquireSRWLockSharedWait(SRWLock,
FirstWait,
&SharedWake);
/* Successfully incremented the shared count, we acquired the lock */
break;
}
}
else
{
/* This is a fastest path, just increment the number of
current shared locks */
/* Since the RTL_SRWLOCK_SHARED bit is set, the RTL_SRWLOCK_OWNED bit also has
to be set! */
ASSERT(CurrentValue & RTL_SRWLOCK_OWNED);
NewValue = (CurrentValue >> RTL_SRWLOCK_BITS) + 1;
NewValue = (NewValue << RTL_SRWLOCK_BITS) | (CurrentValue & RTL_SRWLOCK_MASK);
if (InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == (PVOID)CurrentValue)
{
/* Successfully incremented the shared count, we acquired the lock */
break;
}
}
}
else
{
if (CurrentValue & RTL_SRWLOCK_OWNED)
{
/* The resource is currently acquired exclusively */
if (CurrentValue & RTL_SRWLOCK_CONTENDED)
{
SharedWake.Next = NULL;
SharedWake.Wake = 0;
/* There's other waiters already, lock the wait blocks and
increment the shared count. If the last block in the chain
is an exclusive lock, add another block. */
StackWaitBlock.Exclusive = FALSE;
StackWaitBlock.SharedCount = 0;
StackWaitBlock.Next = NULL;
StackWaitBlock.Last = &StackWaitBlock;
StackWaitBlock.SharedWakeChain = &SharedWake;
First = RtlpAcquireWaitBlockLock(SRWLock);
if (First != NULL)
{
Shared = First->Last;
if (Shared->Exclusive)
{
Shared->Next = &StackWaitBlock;
First->Last = &StackWaitBlock;
Shared = &StackWaitBlock;
FirstWait = &StackWaitBlock;
}
else
{
FirstWait = NULL;
Shared->LastSharedWake->Next = &SharedWake;
}
ASSERT_SRW_WAITBLOCK(Shared);
Shared->SharedCount++;
Shared->LastSharedWake = &SharedWake;
RtlpReleaseWaitBlockLock(SRWLock);
RtlpAcquireSRWLockSharedWait(SRWLock,
FirstWait,
&SharedWake);
/* Successfully incremented the shared count, we acquired the lock */
break;
}
}
else
{
SharedWake.Next = NULL;
SharedWake.Wake = 0;
/* We need to setup the first wait block. Currently an exclusive lock is
held, change the lock to contended mode. */
StackWaitBlock.Exclusive = FALSE;
StackWaitBlock.SharedCount = 1;
StackWaitBlock.Next = NULL;
StackWaitBlock.Last = &StackWaitBlock;
StackWaitBlock.SharedWakeChain = &SharedWake;
StackWaitBlock.LastSharedWake = &SharedWake;
ASSERT_SRW_WAITBLOCK(&StackWaitBlock);
NewValue = (ULONG_PTR)&StackWaitBlock | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
if (InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == (PVOID)CurrentValue)
{
RtlpAcquireSRWLockSharedWait(SRWLock,
&StackWaitBlock,
&SharedWake);
/* Successfully set the shared count, we acquired the lock */
break;
}
}
}
else
{
/* This is a fast path, we can simply try to set the shared count to 1 */
NewValue = (1 << RTL_SRWLOCK_BITS) | RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED;
/* The RTL_SRWLOCK_CONTENDED bit should never be set if neither the
RTL_SRWLOCK_SHARED nor the RTL_SRWLOCK_OWNED bit is set */
ASSERT(!(CurrentValue & RTL_SRWLOCK_CONTENDED));
if (InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == (PVOID)CurrentValue)
{
/* Successfully set the shared count, we acquired the lock */
break;
}
}
}
YieldProcessor();
}
}
VOID
NTAPI
RtlReleaseSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock)
{
LONG_PTR CurrentValue, NewValue;
PRTLP_SRWLOCK_WAITBLOCK WaitBlock;
BOOLEAN LastShared;
while (1)
{
CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
if (CurrentValue & RTL_SRWLOCK_SHARED)
{
if (CurrentValue & RTL_SRWLOCK_CONTENDED)
{
/* There's a wait block, we need to wake a pending
exclusive acquirer if this is the last shared release */
WaitBlock = RtlpAcquireWaitBlockLock(SRWLock);
if (WaitBlock != NULL)
{
LastShared = (--WaitBlock->SharedCount == 0);
if (LastShared)
RtlpReleaseWaitBlockLockLastShared(SRWLock,
WaitBlock);
else
RtlpReleaseWaitBlockLock(SRWLock);
/* We released the lock */
break;
}
}
else
{
/* This is a fast path, we can simply decrement the shared
count and store the pointer */
NewValue = CurrentValue >> RTL_SRWLOCK_BITS;
if (--NewValue != 0)
{
NewValue = (NewValue << RTL_SRWLOCK_BITS) | RTL_SRWLOCK_SHARED | RTL_SRWLOCK_OWNED;
}
if (InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == (PVOID)CurrentValue)
{
/* Successfully released the lock */
break;
}
}
}
else
{
/* The RTL_SRWLOCK_SHARED bit has to be present now,
even in the contended case! */
RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
}
YieldProcessor();
}
}
VOID
NTAPI
RtlAcquireSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock)
{
__ALIGNED(16) RTLP_SRWLOCK_WAITBLOCK StackWaitBlock;
PRTLP_SRWLOCK_WAITBLOCK First, Last;
if (InterlockedBitTestAndSetPointer(&SRWLock->Ptr,
RTL_SRWLOCK_OWNED_BIT))
{
LONG_PTR CurrentValue, NewValue;
while (1)
{
CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
if (CurrentValue & RTL_SRWLOCK_SHARED)
{
/* A shared lock is being held right now. We need to add a wait block! */
if (CurrentValue & RTL_SRWLOCK_CONTENDED)
{
goto AddWaitBlock;
}
else
{
/* There are no wait blocks so far, we need to add ourselves as the first
wait block. We need to keep the shared count! */
StackWaitBlock.Exclusive = TRUE;
StackWaitBlock.SharedCount = CurrentValue >> RTL_SRWLOCK_BITS;
StackWaitBlock.Next = NULL;
StackWaitBlock.Last = &StackWaitBlock;
StackWaitBlock.Wake = 0;
ASSERT_SRW_WAITBLOCK(&StackWaitBlock);
NewValue = (ULONG_PTR)&StackWaitBlock | RTL_SRWLOCK_SHARED | RTL_SRWLOCK_CONTENDED | RTL_SRWLOCK_OWNED;
if (InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == (PVOID)CurrentValue)
{
RtlpAcquireSRWLockExclusiveWait(SRWLock,
&StackWaitBlock);
/* Successfully acquired the exclusive lock */
break;
}
}
}
else
{
if (CurrentValue & RTL_SRWLOCK_OWNED)
{
/* An exclusive lock is being held right now. We need to add a wait block! */
if (CurrentValue & RTL_SRWLOCK_CONTENDED)
{
AddWaitBlock:
StackWaitBlock.Exclusive = TRUE;
StackWaitBlock.SharedCount = 0;
StackWaitBlock.Next = NULL;
StackWaitBlock.Last = &StackWaitBlock;
StackWaitBlock.Wake = 0;
ASSERT_SRW_WAITBLOCK(&StackWaitBlock);
First = RtlpAcquireWaitBlockLock(SRWLock);
if (First != NULL)
{
Last = First->Last;
Last->Next = &StackWaitBlock;
First->Last = &StackWaitBlock;
RtlpReleaseWaitBlockLock(SRWLock);
RtlpAcquireSRWLockExclusiveWait(SRWLock,
&StackWaitBlock);
/* Successfully acquired the exclusive lock */
break;
}
}
else
{
/* There are no wait blocks so far, we need to add ourselves as the first
wait block. We need to keep the shared count! */
StackWaitBlock.Exclusive = TRUE;
StackWaitBlock.SharedCount = 0;
StackWaitBlock.Next = NULL;
StackWaitBlock.Last = &StackWaitBlock;
StackWaitBlock.Wake = 0;
ASSERT_SRW_WAITBLOCK(&StackWaitBlock);
NewValue = (ULONG_PTR)&StackWaitBlock | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
if (InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == (PVOID)CurrentValue)
{
RtlpAcquireSRWLockExclusiveWait(SRWLock,
&StackWaitBlock);
/* Successfully acquired the exclusive lock */
break;
}
}
}
else
{
if (!InterlockedBitTestAndSetPointer(&SRWLock->Ptr,
RTL_SRWLOCK_OWNED_BIT))
{
/* We managed to get hold of a simple exclusive lock! */
break;
}
}
}
YieldProcessor();
}
}
}
VOID
NTAPI
RtlReleaseSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock)
{
LONG_PTR CurrentValue, NewValue;
PRTLP_SRWLOCK_WAITBLOCK WaitBlock;
while (1)
{
CurrentValue = *(volatile LONG_PTR *)&SRWLock->Ptr;
if (!(CurrentValue & RTL_SRWLOCK_OWNED))
{
RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
}
if (!(CurrentValue & RTL_SRWLOCK_SHARED))
{
if (CurrentValue & RTL_SRWLOCK_CONTENDED)
{
/* There's a wait block, we need to wake the next pending
acquirer (exclusive or shared) */
WaitBlock = RtlpAcquireWaitBlockLock(SRWLock);
if (WaitBlock != NULL)
{
RtlpReleaseWaitBlockLockExclusive(SRWLock,
WaitBlock);
/* We released the lock */
break;
}
}
else
{
/* This is a fast path, we can simply clear the RTL_SRWLOCK_OWNED
bit. All other bits should be 0 now because this is a simple
exclusive lock and no one is waiting. */
ASSERT(!(CurrentValue & ~RTL_SRWLOCK_OWNED));
NewValue = 0;
if (InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == (PVOID)CurrentValue)
{
/* We released the lock */
break;
}
}
}
else
{
/* The RTL_SRWLOCK_SHARED bit must not be present now,
not even in the contended case! */
RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED);
}
YieldProcessor();
}
}