[KERNEL32_VISTA][NTDLL_VISTA][RTL_VISTA] Move Vista Rtl functions from kernel32_vista and ntdll_vista to rtl_vista (#3149)

* Move RtlRunOnce functions from kernel32_vista to rtl_vista and export them from ntdll_vista
* Move condvar.c and srw.c from ntdll_vista to rtl_vista
* Move ntdll_vista build script to a subfolder of ntdll

The RtlRunOnce functions are taken from wine, completely unmodified.
The code that was in kernel32_vista had change that used a global keyed_event handle, but was never initialized, so we were still passing NULL thus using the global ExpCritSecOutOfMemoryEvent.
This commit is contained in:
Timo Kreuzer 2020-09-12 15:04:02 +02:00 committed by GitHub
parent 2aca4b2795
commit 61192390cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 159 additions and 115 deletions

View file

@ -114,3 +114,13 @@ add_asm_files(rtl_asm ${ASM_SOURCE})
add_library(rtl ${SOURCE} ${rtl_asm})
add_pch(rtl rtl.h SOURCE)
add_dependencies(rtl psdk asm)
list(APPEND SOURCE_VISTA
condvar.c
runonce.c
srw.c
)
add_library(rtl_vista ${SOURCE_VISTA})
add_pch(rtl rtl_vista.h SOURCE_VISTA)
add_dependencies(rtl_vista psdk)

523
sdk/lib/rtl/condvar.c Normal file
View file

@ -0,0 +1,523 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* PURPOSE: Condition Variable Routines
* PROGRAMMERS: Thomas Weidenmueller <w3seek@reactos.com>
* Stephan A. R<EFBFBD>ger
*/
/* NOTE: This functionality can be optimized for releasing single
threads or for releasing all waiting threads at once. This
implementation is optimized for releasing a single thread at a time.
It wakes up sleeping threads in FIFO order. */
/* INCLUDES ******************************************************************/
#include <rtl_vista.h>
#define NDEBUG
#include <debug.h>
/* INTERNAL TYPES ************************************************************/
#define COND_VAR_UNUSED_FLAG ((ULONG_PTR)1)
#define COND_VAR_LOCKED_FLAG ((ULONG_PTR)2)
#define COND_VAR_FLAGS_MASK ((ULONG_PTR)3)
#define COND_VAR_ADDRESS_MASK (~COND_VAR_FLAGS_MASK)
typedef struct _COND_VAR_WAIT_ENTRY
{
/* ListEntry must have an alignment of at least 32-bits, since we
want COND_VAR_ADDRESS_MASK to cover all of the address. */
LIST_ENTRY ListEntry;
PVOID WaitKey;
BOOLEAN ListRemovalHandled;
} COND_VAR_WAIT_ENTRY, * PCOND_VAR_WAIT_ENTRY;
#define CONTAINING_COND_VAR_WAIT_ENTRY(address, field) \
CONTAINING_RECORD(address, COND_VAR_WAIT_ENTRY, field)
/* GLOBALS *******************************************************************/
static HANDLE CondVarKeyedEventHandle = NULL;
/* INTERNAL FUNCTIONS ********************************************************/
FORCEINLINE
ULONG_PTR
InternalCmpXChgCondVarAcq(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable,
IN ULONG_PTR Exchange,
IN ULONG_PTR Comperand)
{
return (ULONG_PTR)InterlockedCompareExchangePointerAcquire(&ConditionVariable->Ptr,
(PVOID)Exchange,
(PVOID)Comperand);
}
FORCEINLINE
ULONG_PTR
InternalCmpXChgCondVarRel(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable,
IN ULONG_PTR Exchange,
IN ULONG_PTR Comperand)
{
return (ULONG_PTR)InterlockedCompareExchangePointerRelease(&ConditionVariable->Ptr,
(PVOID)Exchange,
(PVOID)Comperand);
}
FORCEINLINE
BOOLEAN *
InternalGetListRemovalHandledFlag(IN PCOND_VAR_WAIT_ENTRY Entry)
{
return (BOOLEAN *)&Entry->ListRemovalHandled;
}
static
PCOND_VAR_WAIT_ENTRY
InternalLockCondVar(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable,
IN PCOND_VAR_WAIT_ENTRY InsertEntry OPTIONAL,
IN BOOLEAN * AbortIfLocked OPTIONAL)
{
/* InsertEntry and AbortIfLocked may be NULL on entry. This routine
will return NULL if the lock was not acquired. Otherwise it has
successfully acquired the lock and the return value is a valid
reference to the list head associated with ConditionVariable.
The caller must in this case call InternalUnlockCondVar later
in order to unlock the condition variable.
If InsertEntry is NULL and there are no entries on the list, this
routine will not acquire the lock and return NULL. If InsertEntry
is not NULL this routine ensures that InsertEntry will be on the
list when it returns successfully.
If the lock is owned by another thread and AbortIfLocked is NULL,
this routine will block until it acquires the lock. If AbortIfLocked
is not NULL and the lock is owned by another thread, this routine
will periodically check if *AbortIfLocked is nonzero and if so, will
return NULL instead of continuing the wait. */
ULONG_PTR OldVal = (ULONG_PTR)ConditionVariable->Ptr;
for (;;)
{
ULONG_PTR NewVal, LockRes;
PLIST_ENTRY OldListHead;
if (OldVal & COND_VAR_LOCKED_FLAG)
{
/* The locked flag is set, indicating someone else currently
holds the lock. We'll spin until this flag becomes
clear or we're asked to abort. */
YieldProcessor();
if ((AbortIfLocked != NULL) && *AbortIfLocked)
{
/* The caller wants us to abort in this case. */
return NULL;
}
/* Refresh OldVal and try again. */
OldVal = *(ULONG_PTR *)&ConditionVariable->Ptr;
continue;
}
/* Retrieve the list head currently associated with the
condition variable. */
OldListHead = (PLIST_ENTRY)(OldVal & COND_VAR_ADDRESS_MASK);
if (InsertEntry == NULL)
{
/* The caller doesn't want to put any entry on the list. */
if (OldListHead == NULL)
{
/* The list is empty, so there is nothing to lock. */
return NULL;
}
/* The list isn't empty. In this case we need to preserve
all of OldVal. */
NewVal = OldVal;
}
else
{
/* Let InsertEntry be the new list head. Preserve only the
bits inside the COND_VAR_FLAGS_MASK range. */
NewVal = ((OldVal & COND_VAR_FLAGS_MASK) |
(ULONG_PTR)&InsertEntry->ListEntry);
}
/* Set the flag that indicates someone is holding the lock and
try to update the condition variable thread-safe. */
NewVal |= COND_VAR_LOCKED_FLAG;
LockRes = InternalCmpXChgCondVarAcq(ConditionVariable, NewVal, OldVal);
if (LockRes == OldVal)
{
/* We successfully updated ConditionVariable the way we
wanted and now hold the lock. */
if (InsertEntry == NULL)
{
/* We know that OldVal contains a valid address in
this case. */
ASSERT(OldListHead != NULL);
return CONTAINING_COND_VAR_WAIT_ENTRY(OldListHead, ListEntry);
}
/* InsertEntry is not on the list yet, so add it. In any
case InsertEntry will be the new list head. */
if (OldListHead == NULL)
{
/* List was empty before. */
InitializeListHead(&InsertEntry->ListEntry);
}
else
{
/* Make InsertEntry the last entry of the old list.
As InsertEntry will take the role as new list head,
OldListHead will become the second entry (InsertEntry->Flink)
on the new list. */
InsertTailList(OldListHead, &InsertEntry->ListEntry);
}
return InsertEntry;
}
/* We didn't manage to update ConditionVariable, so try again. */
OldVal = LockRes;
}
}
static
VOID
InternalUnlockCondVar(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable,
IN PCOND_VAR_WAIT_ENTRY RemoveEntry OPTIONAL)
{
/* This routine assumes that the lock is being held on entry.
RemoveEntry may be NULL. If it is not NULL, this routine
assumes that RemoveEntry is on the list and will remove it
before releasing the lock. */
ULONG_PTR OldVal = (ULONG_PTR)ConditionVariable->Ptr;
PLIST_ENTRY NewHeadEntry;
ASSERT((OldVal & COND_VAR_LOCKED_FLAG) &&
(OldVal & COND_VAR_ADDRESS_MASK));
NewHeadEntry = (PLIST_ENTRY)(OldVal & COND_VAR_ADDRESS_MASK);
if (RemoveEntry != NULL)
{
/* We have to drop RemoveEntry from the list. */
if (&RemoveEntry->ListEntry == NewHeadEntry)
{
/* RemoveEntry is the list head. */
if (!IsListEmpty(NewHeadEntry))
{
/* The second entry in the list will become the new
list head. It's from the thread that arrived
right before the owner of RemoveEntry. */
NewHeadEntry = NewHeadEntry->Flink;
RemoveEntryList(&RemoveEntry->ListEntry);
}
else
{
/* The list will be empty, so discard the list. */
NewHeadEntry = NULL;
}
}
else
{
/* RemoveEntry is not the list head. The current list head
will remain. */
RemoveEntryList(&RemoveEntry->ListEntry);
}
/* Indicate to the owner of RemoveEntry that the entry
was removed from the list. RemoveEntry may not be touched
from here on. We don't use volatile semantics here since
the cache will anyway be flushed soon when we update
ConditionVariable. */
RemoveEntry->ListRemovalHandled = TRUE;
}
/* Now unlock thread-safe, while preserving any flags within the
COND_VAR_FLAGS_MASK range except for COND_VAR_LOCKED_FLAG. */
for (;;)
{
ULONG_PTR NewVal = ((OldVal & (COND_VAR_FLAGS_MASK ^ COND_VAR_LOCKED_FLAG)) |
(ULONG_PTR)NewHeadEntry);
ULONG_PTR LockRes = InternalCmpXChgCondVarRel(ConditionVariable, NewVal, OldVal);
if (LockRes == OldVal)
{
/* We unlocked. */
break;
}
/* Try again. */
OldVal = LockRes;
}
}
static
VOID
InternalWake(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable,
IN BOOLEAN ReleaseAll)
{
/* If ReleaseAll is zero on entry, one thread at most will be woken.
Otherwise all waiting threads are woken. Wakeups happen in FIFO
order. */
PCOND_VAR_WAIT_ENTRY CONST HeadEntry = InternalLockCondVar(ConditionVariable, NULL, NULL);
PCOND_VAR_WAIT_ENTRY Entry;
PCOND_VAR_WAIT_ENTRY NextEntry;
LARGE_INTEGER Timeout;
PCOND_VAR_WAIT_ENTRY RemoveOnUnlockEntry;
ASSERT(CondVarKeyedEventHandle != NULL);
if (HeadEntry == NULL)
{
/* There is noone there to wake up. In this case do nothing
and return immediately. We don't stockpile releases. */
return;
}
Timeout.QuadPart = 0;
RemoveOnUnlockEntry = NULL;
/* Release sleeping threads. We will iterate from the last entry on
the list to the first. Note that the loop condition is always
true for the initial test. */
for (Entry = CONTAINING_COND_VAR_WAIT_ENTRY(HeadEntry->ListEntry.Blink, ListEntry);
Entry != NULL;
Entry = NextEntry)
{
NTSTATUS Status;
if (HeadEntry == Entry)
{
/* After the current entry we've iterated through the
entire list in backward direction. Then exit.*/
NextEntry = NULL;
}
else
{
/* Store away the next reference right now, since we may
not touch Entry anymore at the end of the block. */
NextEntry = CONTAINING_COND_VAR_WAIT_ENTRY(Entry->ListEntry.Blink, ListEntry);
}
/* Wake the thread associated with this event. We will
immediately return if we failed (zero timeout). */
Status = NtReleaseKeyedEvent(CondVarKeyedEventHandle,
&Entry->WaitKey,
FALSE,
&Timeout);
if (!NT_SUCCESS(Status))
{
/* We failed to wake a thread. We'll keep trying. */
ASSERT(STATUS_INVALID_HANDLE != Status);
continue;
}
/* We've woken a thread and will make sure this thread
is removed from the list. */
if (HeadEntry == Entry)
{
/* This is the list head. We can't remove it as easily as
other entries and will pass it to the unlock routine
later (we will exit the loop after this round anyway). */
RemoveOnUnlockEntry = HeadEntry;
}
else
{
/* We can remove the entry right away. */
RemoveEntryList(&Entry->ListEntry);
/* Now tell the woken thread that removal from the list was
already taken care of here so that this thread can resume
its normal operation more quickly. We may not touch
Entry after signaling this, since it may lie in invalid
memory from there on. */
*InternalGetListRemovalHandledFlag(Entry) = TRUE;
}
if (!ReleaseAll)
{
/* We've successfully woken one thread as the caller
demanded. */
break;
}
}
InternalUnlockCondVar(ConditionVariable, RemoveOnUnlockEntry);
}
VOID
NTAPI
RtlAcquireSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock);
VOID
NTAPI
RtlAcquireSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock);
VOID
NTAPI
RtlReleaseSRWLockExclusive(IN OUT PRTL_SRWLOCK SRWLock);
VOID
NTAPI
RtlReleaseSRWLockShared(IN OUT PRTL_SRWLOCK SRWLock);
static
NTSTATUS
InternalSleep(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable,
IN OUT PRTL_CRITICAL_SECTION CriticalSection OPTIONAL,
IN OUT PRTL_SRWLOCK SRWLock OPTIONAL,
IN ULONG SRWFlags,
IN const LARGE_INTEGER * TimeOut OPTIONAL)
{
/* Either CriticalSection or SRWLock must be NULL, but not both.
These caller provided lock must be held on entry and will be
held again on return. */
COND_VAR_WAIT_ENTRY OwnEntry;
NTSTATUS Status;
ASSERT(CondVarKeyedEventHandle != NULL);
ASSERT((CriticalSection == NULL) != (SRWLock == NULL));
RtlZeroMemory(&OwnEntry, sizeof(OwnEntry));
/* Put OwnEntry on the list. */
InternalLockCondVar(ConditionVariable, &OwnEntry, NULL);
InternalUnlockCondVar(ConditionVariable, NULL);
/* We can now drop the caller provided lock as a preparation for
going to sleep. */
if (CriticalSection == NULL)
{
if (0 == (RTL_CONDITION_VARIABLE_LOCKMODE_SHARED & SRWFlags))
{
RtlReleaseSRWLockExclusive(SRWLock);
}
else
{
RtlReleaseSRWLockShared(SRWLock);
}
}
else
{
RtlLeaveCriticalSection(CriticalSection);
}
/* Now sleep using the caller provided timeout. */
Status = NtWaitForKeyedEvent(CondVarKeyedEventHandle,
&OwnEntry.WaitKey,
FALSE,
(PLARGE_INTEGER)TimeOut);
ASSERT(STATUS_INVALID_HANDLE != Status);
if (!*InternalGetListRemovalHandledFlag(&OwnEntry))
{
/* Remove OwnEntry from the list again, since it still seems to
be on the list. We will know for sure once we've acquired
the lock. */
if (InternalLockCondVar(ConditionVariable,
NULL,
InternalGetListRemovalHandledFlag(&OwnEntry)))
{
/* Unlock and potentially remove OwnEntry. Self-removal is
usually only necessary when a timeout occurred. */
InternalUnlockCondVar(ConditionVariable,
!OwnEntry.ListRemovalHandled ?
&OwnEntry : NULL);
}
}
#ifdef _DEBUG
/* Clear OwnEntry to aid in detecting bugs. */
RtlZeroMemory(&OwnEntry, sizeof(OwnEntry));
#endif
/* Reacquire the caller provided lock, as we are about to return. */
if (CriticalSection == NULL)
{
if (0 == (RTL_CONDITION_VARIABLE_LOCKMODE_SHARED & SRWFlags))
{
RtlAcquireSRWLockExclusive(SRWLock);
}
else
{
RtlAcquireSRWLockShared(SRWLock);
}
}
else
{
RtlEnterCriticalSection(CriticalSection);
}
/* Return whatever NtWaitForKeyedEvent returned. */
return Status;
}
VOID
RtlpInitializeKeyedEvent(VOID)
{
ASSERT(CondVarKeyedEventHandle == NULL);
NtCreateKeyedEvent(&CondVarKeyedEventHandle, EVENT_ALL_ACCESS, NULL, 0);
}
VOID
RtlpCloseKeyedEvent(VOID)
{
ASSERT(CondVarKeyedEventHandle != NULL);
NtClose(CondVarKeyedEventHandle);
CondVarKeyedEventHandle = NULL;
}
/* EXPORTED FUNCTIONS ********************************************************/
VOID
NTAPI
RtlInitializeConditionVariable(OUT PRTL_CONDITION_VARIABLE ConditionVariable)
{
ConditionVariable->Ptr = NULL;
}
VOID
NTAPI
RtlWakeConditionVariable(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable)
{
InternalWake(ConditionVariable, FALSE);
}
VOID
NTAPI
RtlWakeAllConditionVariable(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable)
{
InternalWake(ConditionVariable, TRUE);
}
NTSTATUS
NTAPI
RtlSleepConditionVariableCS(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable,
IN OUT PRTL_CRITICAL_SECTION CriticalSection,
IN const LARGE_INTEGER * TimeOut OPTIONAL)
{
return InternalSleep(ConditionVariable,
CriticalSection,
(PRTL_SRWLOCK)NULL,
0,
TimeOut);
}
NTSTATUS
NTAPI
RtlSleepConditionVariableSRW(IN OUT PRTL_CONDITION_VARIABLE ConditionVariable,
IN OUT PRTL_SRWLOCK SRWLock,
IN const LARGE_INTEGER * TimeOut OPTIONAL,
IN ULONG Flags)
{
return InternalSleep(ConditionVariable,
(PRTL_CRITICAL_SECTION)NULL,
SRWLock,
Flags,
TimeOut);
}
/* EOF */

62
sdk/lib/rtl/rtl_vista.h Normal file
View file

@ -0,0 +1,62 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS System Libraries
* FILE: lib/rtl/rtl.h
* PURPOSE: Run-Time Libary Header
* PROGRAMMER: Alex Ionescu
*/
#ifndef RTL_VISTA_H
#define RTL_VISTA_H
#undef _WIN32_WINNT
#undef WINVER
#define _WIN32_WINNT 0x600
#define WINVER 0x600
/* We're a core NT DLL, we don't import syscalls */
#define _INC_SWPRINTF_INL_
#undef __MSVCRT__
/* C Headers */
#include <stdlib.h>
#include <stdio.h>
/* PSDK/NDK Headers */
#define WIN32_NO_STATUS
#define _INC_WINDOWS
#define COM_NO_WINDOWS_H
#define COBJMACROS
#define CONST_VTABLE
#include <windef.h>
#include <winbase.h>
#include <winreg.h>
#include <objbase.h>
#include <ntintsafe.h>
#include <ndk/exfuncs.h>
#include <ndk/iofuncs.h>
#include <ndk/kefuncs.h>
#include <ndk/ldrfuncs.h>
#include <ndk/mmfuncs.h>
#include <ndk/obfuncs.h>
#include <ndk/psfuncs.h>
#include <ndk/rtlfuncs.h>
#include <ndk/setypes.h>
#include <ndk/sefuncs.h>
#include <ndk/umfuncs.h>
/* SEH support with PSEH */
#include <pseh/pseh2.h>
/* Use intrinsics for x86 and x64 */
#if defined(_M_IX86) || defined(_M_AMD64)
#define InterlockedCompareExchange _InterlockedCompareExchange
#define InterlockedIncrement _InterlockedIncrement
#define InterlockedDecrement _InterlockedDecrement
#define InterlockedExchangeAdd _InterlockedExchangeAdd
#define InterlockedExchange _InterlockedExchange
#define InterlockedBitTestAndSet _interlockedbittestandset
#define InterlockedBitTestAndSet64 _interlockedbittestandset64
#endif
#endif /* RTL_VISTA_H */

121
sdk/lib/rtl/runonce.c Normal file
View file

@ -0,0 +1,121 @@
/* Taken from Wine ntdll/sync.c */
#include "rtl_vista.h"
#include <wine/config.h>
#include <wine/port.h>
/******************************************************************
* RtlRunOnceInitialize (NTDLL.@)
*/
void WINAPI RtlRunOnceInitialize( RTL_RUN_ONCE *once )
{
once->Ptr = NULL;
}
/******************************************************************
* RtlRunOnceBeginInitialize (NTDLL.@)
*/
DWORD WINAPI RtlRunOnceBeginInitialize( RTL_RUN_ONCE *once, ULONG flags, void **context )
{
if (flags & RTL_RUN_ONCE_CHECK_ONLY)
{
ULONG_PTR val = (ULONG_PTR)once->Ptr;
if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
if ((val & 3) != 2) return STATUS_UNSUCCESSFUL;
if (context) *context = (void *)(val & ~3);
return STATUS_SUCCESS;
}
for (;;)
{
ULONG_PTR next, val = (ULONG_PTR)once->Ptr;
switch (val & 3)
{
case 0: /* first time */
if (!interlocked_cmpxchg_ptr( &once->Ptr,
(flags & RTL_RUN_ONCE_ASYNC) ? (void *)3 : (void *)1, 0 ))
return STATUS_PENDING;
break;
case 1: /* in progress, wait */
if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
next = val & ~3;
if (interlocked_cmpxchg_ptr( &once->Ptr, (void *)((ULONG_PTR)&next | 1),
(void *)val ) == (void *)val)
NtWaitForKeyedEvent( 0, &next, FALSE, NULL );
break;
case 2: /* done */
if (context) *context = (void *)(val & ~3);
return STATUS_SUCCESS;
case 3: /* in progress, async */
if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
return STATUS_PENDING;
}
}
}
/******************************************************************
* RtlRunOnceComplete (NTDLL.@)
*/
DWORD WINAPI RtlRunOnceComplete( RTL_RUN_ONCE *once, ULONG flags, void *context )
{
if ((ULONG_PTR)context & 3) return STATUS_INVALID_PARAMETER;
if (flags & RTL_RUN_ONCE_INIT_FAILED)
{
if (context) return STATUS_INVALID_PARAMETER;
if (flags & RTL_RUN_ONCE_ASYNC) return STATUS_INVALID_PARAMETER;
}
else context = (void *)((ULONG_PTR)context | 2);
for (;;)
{
ULONG_PTR val = (ULONG_PTR)once->Ptr;
switch (val & 3)
{
case 1: /* in progress */
if (interlocked_cmpxchg_ptr( &once->Ptr, context, (void *)val ) != (void *)val) break;
val &= ~3;
while (val)
{
ULONG_PTR next = *(ULONG_PTR *)val;
NtReleaseKeyedEvent( 0, (void *)val, FALSE, NULL );
val = next;
}
return STATUS_SUCCESS;
case 3: /* in progress, async */
if (!(flags & RTL_RUN_ONCE_ASYNC)) return STATUS_INVALID_PARAMETER;
if (interlocked_cmpxchg_ptr( &once->Ptr, context, (void *)val ) != (void *)val) break;
return STATUS_SUCCESS;
default:
return STATUS_UNSUCCESSFUL;
}
}
}
/******************************************************************
* RtlRunOnceExecuteOnce (NTDLL.@)
*/
DWORD WINAPI RtlRunOnceExecuteOnce( RTL_RUN_ONCE *once, PRTL_RUN_ONCE_INIT_FN func,
void *param, void **context )
{
DWORD ret = RtlRunOnceBeginInitialize( once, 0, context );
if (ret != STATUS_PENDING) return ret;
if (!func( once, param, context ))
{
RtlRunOnceComplete( once, RTL_RUN_ONCE_INIT_FAILED, NULL );
return STATUS_UNSUCCESSFUL;
}
return RtlRunOnceComplete( once, 0, context ? *context : NULL );
}

765
sdk/lib/rtl/srw.c Normal file
View file

@ -0,0 +1,765 @@
/*
* 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_vista.h>
#define NDEBUG
#include <debug.h>
/* 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
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, NextWake;
/* If we were the first one to acquire the shared
lock, we now need to wake all others... */
WakeChain = FirstWaitBlock->SharedWakeChain;
do
{
NextWake = WakeChain->Next;
(void)InterlockedOr((PLONG)&WakeChain->Wake,
TRUE);
WakeChain = NextWake;
} 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);
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;
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 ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == 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;
}
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;
NewValue = (ULONG_PTR)&StackWaitBlock | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == 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 ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == 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 ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == 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 = (LONG)(CurrentValue >> RTL_SRWLOCK_BITS);
StackWaitBlock.Next = NULL;
StackWaitBlock.Last = &StackWaitBlock;
StackWaitBlock.Wake = 0;
NewValue = (ULONG_PTR)&StackWaitBlock | RTL_SRWLOCK_SHARED | RTL_SRWLOCK_CONTENDED | RTL_SRWLOCK_OWNED;
if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == 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;
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;
NewValue = (ULONG_PTR)&StackWaitBlock | RTL_SRWLOCK_OWNED | RTL_SRWLOCK_CONTENDED;
if ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == 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 ((LONG_PTR)InterlockedCompareExchangePointer(&SRWLock->Ptr,
(PVOID)NewValue,
(PVOID)CurrentValue) == 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();
}
}