diff --git a/reactos/dll/win32/CMakeLists.txt b/reactos/dll/win32/CMakeLists.txt index 54621f96a15..291f5ab167f 100644 --- a/reactos/dll/win32/CMakeLists.txt +++ b/reactos/dll/win32/CMakeLists.txt @@ -134,6 +134,7 @@ add_subdirectory(netevent) add_subdirectory(netid) add_subdirectory(newdev) add_subdirectory(npptools) +add_subdirectory(ntdll_vista) add_subdirectory(ntdsapi) add_subdirectory(ntlanman) add_subdirectory(ntmarta) diff --git a/reactos/dll/win32/ntdll_vista/CMakeLists.txt b/reactos/dll/win32/ntdll_vista/CMakeLists.txt new file mode 100644 index 00000000000..6557a5154e7 --- /dev/null +++ b/reactos/dll/win32/ntdll_vista/CMakeLists.txt @@ -0,0 +1,17 @@ + +remove_definitions(-D_WIN32_WINNT=0x502 -DWINVER=0x502) +add_definitions(-D_WIN32_WINNT=0x600 -DWINVER=0x600) + +spec2def(ntdll_vista.dll ntdll_vista.spec ADD_IMPORTLIB) + +list(APPEND SOURCE + DllMain.c + condvar.c + srw.c + ${CMAKE_CURRENT_BINARY_DIR}/ntdll_vista.def) + +add_library(ntdll_vista SHARED ${SOURCE}) +set_module_type(ntdll_vista win32dll ENTRYPOINT DllMain 12) +add_importlibs(ntdll_vista ntdll kernel32) +add_dependencies(ntdll_vista psdk) +add_cd_file(TARGET ntdll_vista DESTINATION reactos/system32 FOR all) diff --git a/reactos/dll/win32/ntdll_vista/DllMain.c b/reactos/dll/win32/ntdll_vista/DllMain.c new file mode 100644 index 00000000000..a7d5a08c001 --- /dev/null +++ b/reactos/dll/win32/ntdll_vista/DllMain.c @@ -0,0 +1,36 @@ +#include + +#define WIN32_NO_STATUS + +#include +#include +#include +#include +#include + +#define NDEBUG +#include + +VOID +RtlpInitializeKeyedEvent(VOID); + +VOID +RtlpCloseKeyedEvent(VOID); + +BOOL +WINAPI +DllMain(HANDLE hDll, + DWORD dwReason, + LPVOID lpReserved) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hDll); + RtlpInitializeKeyedEvent(); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + RtlpCloseKeyedEvent(); + } + return TRUE; +} diff --git a/reactos/dll/win32/ntdll_vista/condvar.c b/reactos/dll/win32/ntdll_vista/condvar.c new file mode 100644 index 00000000000..bd033afa251 --- /dev/null +++ b/reactos/dll/win32/ntdll_vista/condvar.c @@ -0,0 +1,525 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * PURPOSE: Condition Variable Routines + * PROGRAMMERS: Thomas Weidenmueller + * Stephan A. R�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 + +#define NDEBUG +#include + +/* 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); + } + } + +#if _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); +} + +NTSYSAPI +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); +} + +NTSYSAPI +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 */ diff --git a/reactos/dll/win32/ntdll_vista/ntdll_vista.spec b/reactos/dll/win32/ntdll_vista/ntdll_vista.spec new file mode 100644 index 00000000000..2bcde610d01 --- /dev/null +++ b/reactos/dll/win32/ntdll_vista/ntdll_vista.spec @@ -0,0 +1,10 @@ +@ stdcall RtlInitializeConditionVariable(ptr) +@ stdcall RtlWakeConditionVariable(ptr) +@ stdcall RtlWakeAllConditionVariable(ptr) +@ stdcall RtlSleepConditionVariableCS(ptr ptr ptr) +@ stdcall RtlSleepConditionVariableSRW(ptr ptr ptr long) +@ stdcall RtlInitializeSRWLock(ptr) +@ stdcall RtlAcquireSRWLockShared(ptr) +@ stdcall RtlReleaseSRWLockShared(ptr) +@ stdcall RtlAcquireSRWLockExclusive(ptr) +@ stdcall RtlReleaseSRWLockExclusive(ptr) diff --git a/reactos/dll/win32/ntdll_vista/rtl_vista.h b/reactos/dll/win32/ntdll_vista/rtl_vista.h new file mode 100644 index 00000000000..9f96f15b50d --- /dev/null +++ b/reactos/dll/win32/ntdll_vista/rtl_vista.h @@ -0,0 +1,57 @@ +/* + * 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_H +#define RTL_H + +/* We're a core NT DLL, we don't import syscalls */ +#define _INC_SWPRINTF_INL_ +#undef __MSVCRT__ + +/* C Headers */ +#include +#include + +/* PSDK/NDK Headers */ +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H +#define COBJMACROS +#define CONST_VTABLE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SEH support with PSEH */ +#include + +/* 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_H */ diff --git a/reactos/sdk/lib/rtl/srw.c b/reactos/dll/win32/ntdll_vista/srw.c similarity index 99% rename from reactos/sdk/lib/rtl/srw.c rename to reactos/dll/win32/ntdll_vista/srw.c index bd62787f428..31730174888 100644 --- a/reactos/sdk/lib/rtl/srw.c +++ b/reactos/dll/win32/ntdll_vista/srw.c @@ -13,7 +13,7 @@ /* INCLUDES *****************************************************************/ -#include +#include #define NDEBUG #include diff --git a/reactos/sdk/lib/rtl/CMakeLists.txt b/reactos/sdk/lib/rtl/CMakeLists.txt index 5c4cbe7eb8f..7279a2e2eac 100644 --- a/reactos/sdk/lib/rtl/CMakeLists.txt +++ b/reactos/sdk/lib/rtl/CMakeLists.txt @@ -16,7 +16,6 @@ list(APPEND SOURCE bitmap.c bootdata.c compress.c - condvar.c crc32.c critical.c dbgbuffer.c @@ -56,7 +55,6 @@ list(APPEND SOURCE security.c slist.c sid.c - srw.c splaytree.c thread.c time.c