mirror of
https://github.com/reactos/reactos.git
synced 2025-08-02 06:15:52 +00:00
[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:
parent
2aca4b2795
commit
61192390cf
12 changed files with 159 additions and 115 deletions
|
@ -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
523
sdk/lib/rtl/condvar.c
Normal 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
62
sdk/lib/rtl/rtl_vista.h
Normal 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
121
sdk/lib/rtl/runonce.c
Normal 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
765
sdk/lib/rtl/srw.c
Normal 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();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue