Signed-off-by: Daniel Lehman dlehman25@gmail.com --- dlls/ntdll/ntdll.spec | 3 ++ dlls/ntdll/sync.c | 64 +++++++++++++++++++++++++++++++++ dlls/ntdll/tests/om.c | 83 +++++++++++++++++++++++++++++++++++++++++++ include/winternl.h | 3 ++ 4 files changed, 153 insertions(+)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 247f05f561..818ae0090c 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -978,6 +978,9 @@ # @ stub RtlValidateUnicodeString @ stdcall RtlVerifyVersionInfo(ptr long int64) @ stdcall -arch=x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) +@ stdcall RtlWaitOnAddress(ptr ptr long ptr) +@ stdcall RtlWakeAddressAll(ptr) +@ stdcall RtlWakeAddressSingle(ptr) @ stdcall RtlWakeAllConditionVariable(ptr) @ stdcall RtlWakeConditionVariable(ptr) @ stub RtlWalkFrameChain diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 4ae8e36ce0..dc6f7ecf32 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -1955,3 +1955,67 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, RtlAcquireSRWLockExclusive( lock ); return status; } + +static HANDLE woa_event; +static RTL_RUN_ONCE init_once_woa = RTL_RUN_ONCE_INIT; +static DWORD WINAPI init_woa( RTL_RUN_ONCE *once, void *param, void **context ) +{ + NtCreateKeyedEvent( &woa_event, GENERIC_READ|GENERIC_WRITE, NULL, 0 ); + return TRUE; +} + +/*********************************************************************** + * RtlWaitOnAddress (NTDLL.@) + */ +NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size, + const LARGE_INTEGER *timeout ) +{ + switch (size) + { + case 1: + if (*(const UCHAR *)addr != *(const UCHAR *)cmp) + return STATUS_SUCCESS; + break; + case 2: + if (*(const USHORT *)addr != *(const USHORT *)cmp) + return STATUS_SUCCESS; + break; + case 4: + if (*(const ULONG *)addr != *(const ULONG *)cmp) + return STATUS_SUCCESS; + break; + case 8: + if (*(const ULONG64 *)addr != *(const ULONG64 *)cmp) + return STATUS_SUCCESS; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + RtlRunOnceExecuteOnce( &init_once_woa, init_woa, NULL, NULL ); + return NtWaitForKeyedEvent( woa_event, addr, 0, timeout ); +} + +/*********************************************************************** + * RtlWakeAddressAll (NTDLL.@) + */ +void WINAPI RtlWakeAddressAll( const void *addr ) +{ + LARGE_INTEGER now; + + RtlRunOnceExecuteOnce( &init_once_woa, init_woa, NULL, NULL ); + NtQuerySystemTime( &now ); + while (NtReleaseKeyedEvent( woa_event, addr, 0, &now ) == STATUS_SUCCESS) {} +} + +/*********************************************************************** + * RtlWakeAddressSingle (NTDLL.@) + */ +void WINAPI RtlWakeAddressSingle( const void *addr ) +{ + LARGE_INTEGER now; + + RtlRunOnceExecuteOnce( &init_once_woa, init_woa, NULL, NULL ); + NtQuerySystemTime( &now ); + NtReleaseKeyedEvent( woa_event, addr, 0, &now ); +} diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c index 524085474d..b83a8fc841 100644 --- a/dlls/ntdll/tests/om.c +++ b/dlls/ntdll/tests/om.c @@ -70,6 +70,10 @@ static NTSTATUS (WINAPI *pNtReleaseKeyedEvent)( HANDLE, const void *, BOOLEAN, c static NTSTATUS (WINAPI *pNtCreateIoCompletion)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG); static NTSTATUS (WINAPI *pNtOpenIoCompletion)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); static NTSTATUS (WINAPI *pNtQueryInformationFile)(HANDLE, PIO_STATUS_BLOCK, void *, ULONG, FILE_INFORMATION_CLASS); +static NTSTATUS (WINAPI *pNtQuerySystemTime)( LARGE_INTEGER * ); +static NTSTATUS (WINAPI *pRtlWaitOnAddress)( const void *, const void *, SIZE_T, const LARGE_INTEGER * ); +static void (WINAPI *pRtlWakeAddressAll)( const void * ); +static void (WINAPI *pRtlWakeAddressSingle)( const void * );
#define KEYEDEVENT_WAIT 0x0001 #define KEYEDEVENT_WAKE 0x0002 @@ -2064,6 +2068,80 @@ static void test_mutant(void) NtClose( mutant ); }
+static void test_wait_on_address(void) +{ + DWORD ticks; + SIZE_T size; + NTSTATUS status; + LARGE_INTEGER timeout; + LONG64 address, compare; + + if (!pRtlWaitOnAddress) + { + win_skip("RtlWaitOnAddress not supported, skipping test\n"); + return; + } + + if (0) /* crash on Windows */ + { + pRtlWaitOnAddress(&address, NULL, 8, NULL); + pRtlWaitOnAddress(NULL, &compare, 8, NULL); + pRtlWaitOnAddress(NULL, NULL, 8, NULL); + } + + /* don't crash */ + pRtlWakeAddressSingle(NULL); + pRtlWakeAddressAll(NULL); + + /* invalid values */ + address = 0; + compare = 0; + status = pRtlWaitOnAddress(&address, &compare, 5, NULL); + ok(status == STATUS_INVALID_PARAMETER, "got %x\n", status); + + /* values match */ + address = 0; + compare = 0; + pNtQuerySystemTime(&timeout); + timeout.QuadPart += (LONGLONG)1000*10000; + ticks = GetTickCount(); + status = pRtlWaitOnAddress(&address, &compare, 8, &timeout); + ticks = GetTickCount() - ticks; + ok(status == STATUS_TIMEOUT, "got 0x%08x\n", status); + ok(ticks >= 1000 && ticks <= 1500, "got %u\n", ticks); + ok(address == 0, "got %ld\n", address); + ok(compare == 0, "got %ld\n", compare); + + /* different address size */ + for (size = 1; size <= 4; size <<= 1) + { + compare = ~0; + compare <<= size * 8; + + pNtQuerySystemTime(&timeout); + timeout.QuadPart += (LONGLONG)1000 * 10000; + ticks = GetTickCount(); + status = pRtlWaitOnAddress(&address, &compare, size, &timeout); + ticks = GetTickCount() - ticks; + ok(status == STATUS_TIMEOUT, "got 0x%08x\n", status); + ok(ticks >= 1000 && ticks <= 1500, "got %u\n", ticks); + + status = pRtlWaitOnAddress(&address, &compare, size << 1, &timeout); + ok(!status, "got 0x%08x\n", status); + } + address = 0; + compare = 1; + status = pRtlWaitOnAddress(&address, &compare, 8, NULL); + ok(!status, "got 0x%08x\n", status); + + /* no waiters */ + address = 0; + pRtlWakeAddressSingle(&address); + ok(address == 0, "got %ld\n", address); + pRtlWakeAddressAll(&address); + ok(address == 0, "got %ld\n", address); +} + START_TEST(om) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); @@ -2117,6 +2195,10 @@ START_TEST(om) pNtCreateIoCompletion = (void *)GetProcAddress(hntdll, "NtCreateIoCompletion"); pNtOpenIoCompletion = (void *)GetProcAddress(hntdll, "NtOpenIoCompletion"); pNtQueryInformationFile = (void *)GetProcAddress(hntdll, "NtQueryInformationFile"); + pNtQuerySystemTime = (void *)GetProcAddress(hntdll, "NtQuerySystemTime"); + pRtlWaitOnAddress = (void *)GetProcAddress(hntdll, "RtlWaitOnAddress"); + pRtlWakeAddressAll = (void *)GetProcAddress(hntdll, "RtlWakeAddressAll"); + pRtlWakeAddressSingle = (void *)GetProcAddress(hntdll, "RtlWakeAddressSingle");
test_case_sensitive(); test_namespace_pipe(); @@ -2130,4 +2212,5 @@ START_TEST(om) test_mutant(); test_keyed_events(); test_null_device(); + test_wait_on_address(); } diff --git a/include/winternl.h b/include/winternl.h index 1cec3cfc96..9c8861334a 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -2865,6 +2865,9 @@ NTSYSAPI BOOLEAN WINAPI RtlValidAcl(PACL); NTSYSAPI BOOLEAN WINAPI RtlValidSid(PSID); NTSYSAPI BOOLEAN WINAPI RtlValidateHeap(HANDLE,ULONG,LPCVOID); NTSYSAPI NTSTATUS WINAPI RtlVerifyVersionInfo(const RTL_OSVERSIONINFOEXW*,DWORD,DWORDLONG); +NTSYSAPI NTSTATUS WINAPI RtlWaitOnAddress(const void *,const void *,SIZE_T,const LARGE_INTEGER *); +NTSYSAPI void WINAPI RtlWakeAddressAll(const void *); +NTSYSAPI void WINAPI RtlWakeAddressSingle(const void *); NTSYSAPI void WINAPI RtlWakeAllConditionVariable(RTL_CONDITION_VARIABLE *); NTSYSAPI void WINAPI RtlWakeConditionVariable(RTL_CONDITION_VARIABLE *); NTSYSAPI NTSTATUS WINAPI RtlWalkHeap(HANDLE,PVOID);
Signed-off-by: Daniel Lehman dlehman25@gmail.com --- dlls/kernelbase/kernelbase.spec | 6 +- dlls/kernelbase/main.c | 29 +++++ dlls/kernelbase/tests/Makefile.in | 3 +- dlls/kernelbase/tests/sync.c | 186 ++++++++++++++++++++++++++++++ include/synchapi.h | 34 ++++++ include/winbase.h | 1 + 6 files changed, 255 insertions(+), 4 deletions(-) create mode 100644 dlls/kernelbase/tests/sync.c create mode 100644 include/synchapi.h
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index e12f14bf42..e01f109823 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1696,10 +1696,10 @@ @ stdcall WaitForThreadpoolWorkCallbacks(ptr long) kernel32.WaitForThreadpoolWorkCallbacks # @ stub WaitForUserPolicyForegroundProcessingInternal @ stdcall WaitNamedPipeW(wstr long) kernel32.WaitNamedPipeW -# @ stub WaitOnAddress +@ stdcall WaitOnAddress(ptr ptr long long) @ stdcall WakeAllConditionVariable(ptr) kernel32.WakeAllConditionVariable -# @ stub WakeByAddressAll -# @ stub WakeByAddressSingle +@ stdcall WakeByAddressAll(ptr) ntdll.RtlWakeAddressAll +@ stdcall WakeByAddressSingle(ptr) ntdll.RtlWakeAddressSingle @ stdcall WakeConditionVariable(ptr) kernel32.WakeConditionVariable # @ stub WerGetFlags @ stdcall WerRegisterFile(wstr long long) kernel32.WerRegisterFile diff --git a/dlls/kernelbase/main.c b/dlls/kernelbase/main.c index 6871aca41a..031a00d63a 100644 --- a/dlls/kernelbase/main.c +++ b/dlls/kernelbase/main.c @@ -17,10 +17,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windows.h" #include "appmodel.h"
#include "wine/debug.h" +#include "winternl.h"
WINE_DEFAULT_DEBUG_CHANNEL(kernelbase);
@@ -97,3 +100,29 @@ BOOL WINAPI QuirkIsEnabled3(void *unk1, void *unk2)
return FALSE; } + +/*********************************************************************** + * WaitOnAddress (KERNELBASE.@) + */ +BOOL WINAPI WaitOnAddress(volatile void *addr, void *cmp, SIZE_T size, DWORD timeout) +{ + LARGE_INTEGER to; + NTSTATUS status; + + if (timeout != INFINITE) + { + NtQuerySystemTime(&to); + to.QuadPart += (LONGLONG)timeout*10000; + status = RtlWaitOnAddress((const void *)addr, cmp, size, &to); + } + else + status = RtlWaitOnAddress((const void *)addr, cmp, size, NULL); + + if (status != STATUS_SUCCESS) + { + SetLastError( RtlNtStatusToDosError(status) ); + return FALSE; + } + + return TRUE; +} diff --git a/dlls/kernelbase/tests/Makefile.in b/dlls/kernelbase/tests/Makefile.in index ac8e1fcaa6..22e4a17a58 100644 --- a/dlls/kernelbase/tests/Makefile.in +++ b/dlls/kernelbase/tests/Makefile.in @@ -1,4 +1,5 @@ TESTDLL = kernelbase.dll
C_SRCS = \ - path.c + path.c \ + sync.c diff --git a/dlls/kernelbase/tests/sync.c b/dlls/kernelbase/tests/sync.c new file mode 100644 index 0000000000..7b80a7e61d --- /dev/null +++ b/dlls/kernelbase/tests/sync.c @@ -0,0 +1,186 @@ +/* + * Synchronization tests + * + * Copyright 2018 Daniel Lehman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> +#include <windef.h> +#include <winbase.h> +#include <stdlib.h> +#include <winerror.h> + +#include "wine/test.h" + +static BOOL (WINAPI *pWaitOnAddress)(volatile void *, void *, SIZE_T, DWORD); +static void (WINAPI *pWakeByAddressAll)(void *); +static void (WINAPI *pWakeByAddressSingle)(void *); + +static LONG64 address; +static LONG64 compare; +static DWORD WINAPI test_WaitOnAddress_func(void *arg) +{ + BOOL ret = FALSE; + DWORD gle; + while (address == compare) + { + SetLastError(0xdeadbeef); + ret = pWaitOnAddress(&address, &compare, sizeof(compare), INFINITE); + gle = GetLastError(); + ok(gle == 0xdeadbeef || broken(gle == ERROR_SUCCESS) /* Win 8 */, "got %d\n", gle); + } + ok(ret, "got %d\n", ret); + return 0; +} + +static void test_WaitOnAddress(void) +{ + DWORD gle, val, nthreads; + HANDLE threads[8]; + BOOL ret; + + if (!pWaitOnAddress) + { + win_skip("WaitOnAddress not supported, skipping test\n"); + return; + } + + address = 0; + compare = 0; + if (0) /* crash on Windows */ + { + ret = pWaitOnAddress(&address, NULL, 8, 0); + ret = pWaitOnAddress(NULL, &compare, 8, 0); + } + + /* invalid arguments */ + SetLastError(0xdeadbeef); + pWakeByAddressSingle(NULL); + gle = GetLastError(); + ok(gle == 0xdeadbeef, "got %d\n", gle); + + SetLastError(0xdeadbeef); + pWakeByAddressAll(NULL); + gle = GetLastError(); + ok(gle == 0xdeadbeef, "got %d\n", gle); + + SetLastError(0xdeadbeef); + ret = pWaitOnAddress(NULL, NULL, 0, 0); + gle = GetLastError(); + ok(gle == ERROR_INVALID_PARAMETER, "got %d\n", gle); + ok(!ret, "got %d\n", ret); + + address = 0; + compare = 0; + SetLastError(0xdeadbeef); + ret = pWaitOnAddress(&address, &compare, 5, 0); + gle = GetLastError(); + ok(gle == ERROR_INVALID_PARAMETER, "got %d\n", gle); + ok(!ret, "got %d\n", ret); + ok(address == 0, "got %ld\n", address); + ok(compare == 0, "got %ld\n", compare); + + /* no waiters */ + address = 0; + SetLastError(0xdeadbeef); + pWakeByAddressSingle(&address); + gle = GetLastError(); + ok(gle == 0xdeadbeef, "got %d\n", gle); + ok(address == 0, "got %ld\n", address); + + SetLastError(0xdeadbeef); + pWakeByAddressAll(&address); + gle = GetLastError(); + ok(gle == 0xdeadbeef, "got %d\n", gle); + ok(address == 0, "got %ld\n", address); + + /* different address size */ + address = 0; + compare = 0xffff0000; + SetLastError(0xdeadbeef); + ret = pWaitOnAddress(&address, &compare, 4, 0); + gle = GetLastError(); + ok(gle == 0xdeadbeef || broken(gle == ERROR_SUCCESS) /* Win 8 */, "got %d\n", gle); + ok(ret, "got %d\n", ret); + + SetLastError(0xdeadbeef); + ret = pWaitOnAddress(&address, &compare, 2, 0); + gle = GetLastError(); + ok(gle == ERROR_TIMEOUT, "got %d\n", gle); + ok(!ret, "got %d\n", ret); + + /* simple wait case */ + address = 0; + compare = 1; + SetLastError(0xdeadbeef); + ret = pWaitOnAddress(&address, &compare, 8, 0); + gle = GetLastError(); + ok(gle == 0xdeadbeef || broken(gle == ERROR_SUCCESS) /* Win 8 */, "got %d\n", gle); + ok(ret, "got %d\n", ret); + + /* WakeByAddressAll */ + address = 0; + compare = 0; + for (int i = 0; i < ARRAY_SIZE(threads); i++) + threads[i] = CreateThread(NULL, 0, test_WaitOnAddress_func, NULL, 0, NULL); + + Sleep(1000); + address = ~0; + pWakeByAddressAll(&address); + val = WaitForMultipleObjects(ARRAY_SIZE(threads), threads, TRUE, 5000); + ok(val == WAIT_OBJECT_0, "got %d\n", val); + for (int i = 0; i < ARRAY_SIZE(threads); i++) + CloseHandle(threads[i]); + + /* WakeByAddressSingle */ + address = 0; + for (int i = 0; i < ARRAY_SIZE(threads); i++) + threads[i] = CreateThread(NULL, 0, test_WaitOnAddress_func, NULL, 0, NULL); + + Sleep(1000); + address = 1; + nthreads = ARRAY_SIZE(threads); + while (nthreads) + { + val = WaitForMultipleObjects(nthreads, threads, FALSE, 0); + ok(val == STATUS_TIMEOUT, "got %u\n", val); + + pWakeByAddressSingle(&address); + val = WaitForMultipleObjects(nthreads, threads, FALSE, 2000); + ok(val < WAIT_OBJECT_0 + nthreads, "got %u\n", val); + CloseHandle(threads[val]); + memmove(&threads[val], &threads[val+1], (nthreads - val - 1) * sizeof(threads[0])); + nthreads--; + } + +} + +START_TEST(sync) +{ + HMODULE hmod; + + hmod = LoadLibraryA("kernel32.dll"); + pWaitOnAddress = (void *)GetProcAddress(hmod, "WaitOnAddress"); + ok(!pWaitOnAddress, "expected only in kernelbase.dll\n"); + + hmod = LoadLibraryA("kernelbase.dll"); + pWaitOnAddress = (void *)GetProcAddress(hmod, "WaitOnAddress"); + pWakeByAddressAll = (void *)GetProcAddress(hmod, "WakeByAddressAll"); + pWakeByAddressSingle = (void *)GetProcAddress(hmod, "WakeByAddressSingle"); + + test_WaitOnAddress(); +} diff --git a/include/synchapi.h b/include/synchapi.h new file mode 100644 index 0000000000..124a53a8b2 --- /dev/null +++ b/include/synchapi.h @@ -0,0 +1,34 @@ +/* + * Copyright 2018 Daniel Lehman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_SYNCHAPI_H +#define __WINE_SYNCHAPI_H + +#ifdef __cplusplus +extern "C" { +#endif + +BOOL WINAPI WaitOnAddress(volatile void*, void*, SIZE_T, DWORD); +void WINAPI WakeByAddressAll(void*); +void WINAPI WakeByAddressSingle(void*); + +#ifdef __cplusplus +} +#endif + +#endif /* __WINE_SYNCHAPI_H */ diff --git a/include/winbase.h b/include/winbase.h index c38da9c158..d4fc108534 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -38,6 +38,7 @@ extern "C" { #endif
#include <libloaderapi.h> +#include <synchapi.h>
/* Windows Exit Procedure flag values */ #define WEP_FREE_DLL 0