These will be used to implement RtlWaitOnAddress() and other in-process synchronization primitives, as they are on Windows.
These patches went through quite a few revisions in order to ensure that they had sufficient performance and correctness compared to the current implementation. I am particularly grateful to Etienne Juvigny and Dmitry Skvortsov for performing extensive testing.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/ntdll.spec | 4 ++ dlls/ntdll/unix/loader.c | 2 + dlls/ntdll/unix/sync.c | 93 ++++++++++++++++++++++++++++++++++++++++ dlls/wow64/sync.c | 23 ++++++++++ dlls/wow64/syscall.h | 2 + include/winternl.h | 2 + 6 files changed, 126 insertions(+)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index e7c1fa1dcf3..e5a49ba1a1f 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -140,6 +140,7 @@ @ stdcall -syscall NtAdjustPrivilegesToken(long long ptr long ptr ptr) @ stdcall -syscall NtAlertResumeThread(long ptr) @ stdcall -syscall NtAlertThread(long) +@ stdcall -syscall NtAlertThreadByThreadId(ptr) @ stdcall -syscall NtAllocateLocallyUniqueId(ptr) # @ stub NtAllocateUserPhysicalPages @ stdcall -syscall NtAllocateUuids(ptr ptr ptr ptr) @@ -424,6 +425,7 @@ @ stdcall -syscall NtUnmapViewOfSection(long ptr) # @ stub NtVdmControl # @ stub NtW32Call +@ stdcall -syscall NtWaitForAlertByThreadId(ptr ptr) @ stdcall -syscall NtWaitForDebugEvent(long long ptr ptr) @ stdcall -syscall NtWaitForKeyedEvent(long ptr long ptr) @ stdcall -syscall NtWaitForMultipleObjects(long ptr long long ptr) @@ -1161,6 +1163,7 @@ @ stdcall -private -syscall ZwAdjustPrivilegesToken(long long ptr long ptr ptr) NtAdjustPrivilegesToken @ stdcall -private -syscall ZwAlertResumeThread(long ptr) NtAlertResumeThread @ stdcall -private -syscall ZwAlertThread(long) NtAlertThread +@ stdcall -private -syscall ZwAlertThreadByThreadId(ptr) NtAlertThreadByThreadId @ stdcall -private -syscall ZwAllocateLocallyUniqueId(ptr) NtAllocateLocallyUniqueId # @ stub ZwAllocateUserPhysicalPages @ stdcall -private -syscall ZwAllocateUuids(ptr ptr ptr ptr) NtAllocateUuids @@ -1443,6 +1446,7 @@ @ stdcall -private -syscall ZwUnmapViewOfSection(long ptr) NtUnmapViewOfSection # @ stub ZwVdmControl # @ stub ZwW32Call +@ stdcall -private -syscall ZwWaitForAlertByThreadId(ptr ptr) NtWaitForAlertByThreadId @ stdcall -private -syscall ZwWaitForDebugEvent(long long ptr ptr) NtWaitForDebugEvent @ stdcall -private -syscall ZwWaitForKeyedEvent(long ptr long ptr) NtWaitForKeyedEvent @ stdcall -private -syscall ZwWaitForMultipleObjects(long ptr long long ptr) NtWaitForMultipleObjects diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 426d18e52d6..a411b01a389 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -133,6 +133,7 @@ static void * const syscalls[] = NtAdjustPrivilegesToken, NtAlertResumeThread, NtAlertThread, + NtAlertThreadByThreadId, NtAllocateLocallyUniqueId, NtAllocateUuids, NtAllocateVirtualMemory, @@ -335,6 +336,7 @@ static void * const syscalls[] = NtUnlockFile, NtUnlockVirtualMemory, NtUnmapViewOfSection, + NtWaitForAlertByThreadId, NtWaitForDebugEvent, NtWaitForKeyedEvent, NtWaitForMultipleObjects, diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 964458a28a2..e58f3bd9261 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -33,6 +33,9 @@ #include <limits.h> #include <signal.h> #include <sys/types.h> +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif #ifdef HAVE_SYS_SYSCALL_H #include <sys/syscall.h> #endif @@ -75,6 +78,12 @@ static const LARGE_INTEGER zero_timeout;
static pthread_mutex_t addr_mutex = PTHREAD_MUTEX_INITIALIZER;
+static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) +{ + if (!timeout) return "(infinite)"; + return wine_dbgstr_longlong( timeout->QuadPart ); +} + /* return a monotonic time counter, in Win32 ticks */ static inline ULONGLONG monotonic_counter(void) { @@ -2388,6 +2397,90 @@ NTSTATUS WINAPI NtQueryInformationAtom( RTL_ATOM atom, ATOM_INFORMATION_CLASS cl }
+union tid_alert_entry +{ + HANDLE event; +}; + +#define TID_ALERT_BLOCK_SIZE (65536 / sizeof(union tid_alert_entry)) +static union tid_alert_entry *tid_alert_blocks[4096]; + +static unsigned int handle_to_index( HANDLE handle, unsigned int *block_idx ) +{ + unsigned int idx = (wine_server_obj_handle(handle) >> 2) - 1; + *block_idx = idx / TID_ALERT_BLOCK_SIZE; + return idx % TID_ALERT_BLOCK_SIZE; +} + +static union tid_alert_entry *get_tid_alert_entry( HANDLE tid ) +{ + unsigned int block_idx, idx = handle_to_index( tid, &block_idx ); + union tid_alert_entry *entry; + + if (block_idx > ARRAY_SIZE(tid_alert_blocks)) + { + FIXME( "tid %p is too high\n", tid ); + return NULL; + } + + if (!tid_alert_blocks[block_idx]) + { + static const size_t size = TID_ALERT_BLOCK_SIZE * sizeof(union tid_alert_entry); + void *ptr = anon_mmap_alloc( size, PROT_READ | PROT_WRITE ); + if (ptr == MAP_FAILED) return NULL; + if (InterlockedCompareExchangePointer( (void **)&tid_alert_blocks[block_idx], ptr, NULL )) + munmap( ptr, size ); /* someone beat us to it */ + } + + entry = &tid_alert_blocks[block_idx][idx % TID_ALERT_BLOCK_SIZE]; + + if (!entry->event) + { + HANDLE event; + + if (NtCreateEvent( &event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE )) + return NULL; + if (InterlockedCompareExchangePointer( &entry->event, event, NULL )) + NtClose( event ); + } + + return entry; +} + + +/*********************************************************************** + * NtAlertThreadByThreadId (NTDLL.@) + */ +NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid ) +{ + union tid_alert_entry *entry = get_tid_alert_entry( tid ); + + TRACE( "%p\n", tid ); + + if (!entry) return STATUS_INVALID_CID; + + return NtSetEvent( entry->event, NULL ); +} + + +/*********************************************************************** + * NtWaitForAlertByThreadId (NTDLL.@) + */ +NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEGER *timeout ) +{ + union tid_alert_entry *entry = get_tid_alert_entry( NtCurrentTeb()->ClientId.UniqueThread ); + NTSTATUS status; + + TRACE( "%p %s\n", address, debugstr_timeout( timeout ) ); + + if (!entry) return STATUS_INVALID_CID; + + status = NtWaitForSingleObject( entry->event, FALSE, timeout ); + if (!status) return STATUS_ALERTED; + return status; +} + + #ifdef __linux__
NTSTATUS CDECL fast_RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit, int timeout ) diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index e2e9468a727..fbd039f644c 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -1587,3 +1587,26 @@ NTSTATUS WINAPI wow64_NtYieldExecution( UINT *args ) { return NtYieldExecution(); } + + +/********************************************************************** + * wow64_NtAlertThreadByThreadId + */ +NTSTATUS WINAPI wow64_NtAlertThreadByThreadId( UINT *args ) +{ + HANDLE tid = get_handle( &args ); + + return NtAlertThreadByThreadId( tid ); +} + + +/********************************************************************** + * wow64_NtWaitForAlertByThreadId + */ +NTSTATUS WINAPI wow64_NtWaitForAlertByThreadId( UINT *args ) +{ + const void *address = get_ptr( &args ); + const LARGE_INTEGER *timeout = get_ptr( &args ); + + return NtWaitForAlertByThreadId( address, timeout ); +} diff --git a/dlls/wow64/syscall.h b/dlls/wow64/syscall.h index bc403dd6d31..112711875f7 100644 --- a/dlls/wow64/syscall.h +++ b/dlls/wow64/syscall.h @@ -30,6 +30,7 @@ SYSCALL_ENTRY( NtAdjustPrivilegesToken ) \ SYSCALL_ENTRY( NtAlertResumeThread ) \ SYSCALL_ENTRY( NtAlertThread ) \ + SYSCALL_ENTRY( NtAlertThreadByThreadId ) \ SYSCALL_ENTRY( NtAllocateLocallyUniqueId ) \ SYSCALL_ENTRY( NtAllocateUuids ) \ SYSCALL_ENTRY( NtAllocateVirtualMemory ) \ @@ -231,6 +232,7 @@ SYSCALL_ENTRY( NtUnlockFile ) \ SYSCALL_ENTRY( NtUnlockVirtualMemory ) \ SYSCALL_ENTRY( NtUnmapViewOfSection ) \ + SYSCALL_ENTRY( NtWaitForAlertByThreadId ) \ SYSCALL_ENTRY( NtWaitForDebugEvent ) \ SYSCALL_ENTRY( NtWaitForKeyedEvent ) \ SYSCALL_ENTRY( NtWaitForMultipleObjects ) \ diff --git a/include/winternl.h b/include/winternl.h index eea97f1238b..cfd83f16337 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -3858,6 +3858,7 @@ NTSYSAPI NTSTATUS WINAPI NtAdjustGroupsToken(HANDLE,BOOLEAN,PTOKEN_GROUPS,ULONG NTSYSAPI NTSTATUS WINAPI NtAdjustPrivilegesToken(HANDLE,BOOLEAN,PTOKEN_PRIVILEGES,DWORD,PTOKEN_PRIVILEGES,PDWORD); NTSYSAPI NTSTATUS WINAPI NtAlertResumeThread(HANDLE,PULONG); NTSYSAPI NTSTATUS WINAPI NtAlertThread(HANDLE ThreadHandle); +NTSYSAPI NTSTATUS WINAPI NtAlertThreadByThreadId(HANDLE); NTSYSAPI NTSTATUS WINAPI NtAllocateLocallyUniqueId(PLUID lpLuid); NTSYSAPI NTSTATUS WINAPI NtAllocateUuids(PULARGE_INTEGER,PULONG,PULONG,PUCHAR); NTSYSAPI NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE,PVOID*,ULONG_PTR,SIZE_T*,ULONG,ULONG); @@ -4097,6 +4098,7 @@ NTSYSAPI NTSTATUS WINAPI NtUnlockFile(HANDLE,PIO_STATUS_BLOCK,PLARGE_INTEGER,PL NTSYSAPI NTSTATUS WINAPI NtUnlockVirtualMemory(HANDLE,PVOID*,SIZE_T*,ULONG); NTSYSAPI NTSTATUS WINAPI NtUnmapViewOfSection(HANDLE,PVOID); NTSYSAPI NTSTATUS WINAPI NtVdmControl(ULONG,PVOID); +NTSYSAPI NTSTATUS WINAPI NtWaitForAlertByThreadId(const void*,const LARGE_INTEGER*); NTSYSAPI NTSTATUS WINAPI NtWaitForDebugEvent(HANDLE,BOOLEAN,LARGE_INTEGER*,DBGUI_WAIT_STATE_CHANGE*); NTSYSAPI NTSTATUS WINAPI NtWaitForKeyedEvent(HANDLE,const void*,BOOLEAN,const LARGE_INTEGER*); NTSYSAPI NTSTATUS WINAPI NtWaitForSingleObject(HANDLE,BOOLEAN,const LARGE_INTEGER*);
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/tests/sync.c | 92 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+)
diff --git a/dlls/ntdll/tests/sync.c b/dlls/ntdll/tests/sync.c index f2499ea648a..15ae67c1b9b 100644 --- a/dlls/ntdll/tests/sync.c +++ b/dlls/ntdll/tests/sync.c @@ -26,6 +26,7 @@ #include "winternl.h" #include "wine/test.h"
+static NTSTATUS (WINAPI *pNtAlertThreadByThreadId)( HANDLE ); static NTSTATUS (WINAPI *pNtClose)( HANDLE ); static NTSTATUS (WINAPI *pNtCreateEvent) ( PHANDLE, ACCESS_MASK, const OBJECT_ATTRIBUTES *, EVENT_TYPE, BOOLEAN); static NTSTATUS (WINAPI *pNtCreateKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, ULONG ); @@ -43,6 +44,7 @@ static NTSTATUS (WINAPI *pNtReleaseMutant)( HANDLE, LONG * ); static NTSTATUS (WINAPI *pNtReleaseSemaphore)( HANDLE, ULONG, ULONG * ); static NTSTATUS (WINAPI *pNtResetEvent)( HANDLE, LONG * ); static NTSTATUS (WINAPI *pNtSetEvent)( HANDLE, LONG * ); +static NTSTATUS (WINAPI *pNtWaitForAlertByThreadId)( void *, const LARGE_INTEGER * ); static NTSTATUS (WINAPI *pNtWaitForKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); static BOOLEAN (WINAPI *pRtlAcquireResourceExclusive)( RTL_RWLOCK *, BOOLEAN ); static BOOLEAN (WINAPI *pRtlAcquireResourceShared)( RTL_RWLOCK *, BOOLEAN ); @@ -754,10 +756,98 @@ static void test_resource(void) pRtlDeleteResource(&resource); }
+static DWORD WINAPI tid_alert_thread( void *arg ) +{ + NTSTATUS ret; + + ret = pNtAlertThreadByThreadId( arg ); + ok(!ret, "got %#x\n", ret); + + ret = pNtWaitForAlertByThreadId( (void *)0x123, NULL ); + ok(ret == STATUS_ALERTED, "got %#x\n", ret); + + return 0; +} + +static void test_tid_alert( char **argv ) +{ + LARGE_INTEGER timeout = {0}; + char cmdline[MAX_PATH]; + STARTUPINFOA si = {0}; + PROCESS_INFORMATION pi; + HANDLE thread; + NTSTATUS ret; + DWORD tid; + + if (!pNtWaitForAlertByThreadId) + { + win_skip("NtWaitForAlertByThreadId is not available\n"); + return; + } + + ret = pNtWaitForAlertByThreadId( (void *)0x123, &timeout ); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + ret = pNtAlertThreadByThreadId( 0 ); + ok(ret == STATUS_INVALID_CID, "got %#x\n", ret); + + ret = pNtAlertThreadByThreadId( (HANDLE)0xdeadbeef ); + ok(ret == STATUS_INVALID_CID, "got %#x\n", ret); + + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)GetCurrentThreadId() ); + ok(!ret, "got %#x\n", ret); + + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)GetCurrentThreadId() ); + ok(!ret, "got %#x\n", ret); + + ret = pNtWaitForAlertByThreadId( (void *)0x123, &timeout ); + ok(ret == STATUS_ALERTED, "got %#x\n", ret); + + ret = pNtWaitForAlertByThreadId( (void *)0x123, &timeout ); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + ret = pNtWaitForAlertByThreadId( (void *)0x321, &timeout ); + ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); + + thread = CreateThread( NULL, 0, tid_alert_thread, (HANDLE)(DWORD_PTR)GetCurrentThreadId(), 0, &tid ); + timeout.QuadPart = -1000 * 10000; + ret = pNtWaitForAlertByThreadId( (void *)0x123, &timeout ); + ok(ret == STATUS_ALERTED, "got %#x\n", ret); + + ret = WaitForSingleObject( thread, 100 ); + ok(ret == WAIT_TIMEOUT, "got %d\n", ret); + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tid ); + ok(!ret, "got %#x\n", ret); + + ret = WaitForSingleObject( thread, 1000 ); + ok(!ret, "got %d\n", ret); + + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)tid ); + ok(!ret, "got %#x\n", ret); + + CloseHandle(thread); + + sprintf( cmdline, "%s %s subprocess", argv[0], argv[1] ); + ret = CreateProcessA( NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ); + ok(ret, "failed to create process, error %u\n", GetLastError()); + ret = pNtAlertThreadByThreadId( (HANDLE)(DWORD_PTR)pi.dwThreadId ); + todo_wine ok(ret == STATUS_ACCESS_DENIED, "got %#x\n", ret); + ok(!WaitForSingleObject( pi.hProcess, 1000 ), "wait failed\n"); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); +} + START_TEST(sync) { HMODULE module = GetModuleHandleA("ntdll.dll"); + char **argv; + int argc;
+ argc = winetest_get_mainargs( &argv ); + + if (argc > 2) return; + + pNtAlertThreadByThreadId = (void *)GetProcAddress(module, "NtAlertThreadByThreadId"); pNtClose = (void *)GetProcAddress(module, "NtClose"); pNtCreateEvent = (void *)GetProcAddress(module, "NtCreateEvent"); pNtCreateKeyedEvent = (void *)GetProcAddress(module, "NtCreateKeyedEvent"); @@ -775,6 +865,7 @@ START_TEST(sync) pNtReleaseSemaphore = (void *)GetProcAddress(module, "NtReleaseSemaphore"); pNtResetEvent = (void *)GetProcAddress(module, "NtResetEvent"); pNtSetEvent = (void *)GetProcAddress(module, "NtSetEvent"); + pNtWaitForAlertByThreadId = (void *)GetProcAddress(module, "NtWaitForAlertByThreadId"); pNtWaitForKeyedEvent = (void *)GetProcAddress(module, "NtWaitForKeyedEvent"); pRtlAcquireResourceExclusive = (void *)GetProcAddress(module, "RtlAcquireResourceExclusive"); pRtlAcquireResourceShared = (void *)GetProcAddress(module, "RtlAcquireResourceShared"); @@ -792,4 +883,5 @@ START_TEST(sync) test_semaphore(); test_keyed_events(); test_resource(); + test_tid_alert( argv ); }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102086
Your paranoid android.
=== debiant2 (build log) ===
0048:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0048:err:winediag:nodrv_CreateWindow Unknown error (998). 0040:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0040:err:winediag:nodrv_CreateWindow Unknown error (998). 0050:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0050:err:winediag:nodrv_CreateWindow Unknown error (998). 002c:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 002c:err:winediag:nodrv_CreateWindow Unknown error (998). 007c:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 007c:err:winediag:nodrv_CreateWindow Unknown error (998). 00c0:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 00c0:err:winediag:nodrv_CreateWindow Unknown error (998). 006c:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 006c:err:winediag:nodrv_CreateWindow Unknown error (998). Error: Mount manager not running, most likely your WINEPREFIX wasn't created correctly. Task: WineTest did not produce the wow32 report
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/unix/sync.c | 76 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+)
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index e58f3bd9261..47687d4d4e5 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2400,6 +2400,9 @@ NTSTATUS WINAPI NtQueryInformationAtom( RTL_ATOM atom, ATOM_INFORMATION_CLASS cl union tid_alert_entry { HANDLE event; +#ifdef __linux__ + int futex; +#endif };
#define TID_ALERT_BLOCK_SIZE (65536 / sizeof(union tid_alert_entry)) @@ -2434,6 +2437,11 @@ static union tid_alert_entry *get_tid_alert_entry( HANDLE tid )
entry = &tid_alert_blocks[block_idx][idx % TID_ALERT_BLOCK_SIZE];
+#ifdef __linux__ + if (use_futexes()) + return entry; +#endif + if (!entry->event) { HANDLE event; @@ -2459,10 +2467,43 @@ NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid )
if (!entry) return STATUS_INVALID_CID;
+#ifdef __linux__ + if (use_futexes()) + { + int *futex = &entry->futex; + if (!InterlockedExchange( futex, 1 )) + futex_wake( futex, 1 ); + return STATUS_SUCCESS; + } +#endif + return NtSetEvent( entry->event, NULL ); }
+#ifdef __linux__ +static LONGLONG get_absolute_timeout( const LARGE_INTEGER *timeout ) +{ + LARGE_INTEGER now; + + if (timeout->QuadPart >= 0) return timeout->QuadPart; + NtQuerySystemTime( &now ); + return now.QuadPart - timeout->QuadPart; +} + +static LONGLONG update_timeout( ULONGLONG end ) +{ + LARGE_INTEGER now; + LONGLONG timeleft; + + NtQuerySystemTime( &now ); + timeleft = end - now.QuadPart; + if (timeleft < 0) timeleft = 0; + return timeleft; +} +#endif + + /*********************************************************************** * NtWaitForAlertByThreadId (NTDLL.@) */ @@ -2475,6 +2516,41 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG
if (!entry) return STATUS_INVALID_CID;
+#ifdef __linux__ + if (use_futexes()) + { + int *futex = &entry->futex; + ULONGLONG end; + int ret; + + if (timeout) + { + if (timeout->QuadPart == TIMEOUT_INFINITE) + timeout = NULL; + else + end = get_absolute_timeout( timeout ); + } + + while (!InterlockedExchange( futex, 0 )) + { + if (timeout) + { + LONGLONG timeleft = update_timeout( end ); + struct timespec timespec; + + timespec.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; + timespec.tv_nsec = (timeleft % TICKSPERSEC) * 100; + ret = futex_wait( futex, 0, ×pec ); + } + else + ret = futex_wait( futex, 0, NULL ); + + if (ret == -1 && errno == ETIMEDOUT) return STATUS_TIMEOUT; + } + return STATUS_ALERTED; + } +#endif + status = NtWaitForSingleObject( entry->event, FALSE, timeout ); if (!status) return STATUS_ALERTED; return status;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102087
Your paranoid android.
=== debiant2 (build log) ===
0048:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0048:err:winediag:nodrv_CreateWindow Unknown error (998). 002c:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 002c:err:winediag:nodrv_CreateWindow Unknown error (998). 0050:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0050:err:winediag:nodrv_CreateWindow Unknown error (998). 0040:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0040:err:winediag:nodrv_CreateWindow Unknown error (998). 0080:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0080:err:winediag:nodrv_CreateWindow Unknown error (998). 00c8:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 00c8:err:winediag:nodrv_CreateWindow Unknown error (998). 0070:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0070:err:winediag:nodrv_CreateWindow Unknown error (998). Error: Mount manager not running, most likely your WINEPREFIX wasn't created correctly. Task: WineTest did not produce the wow32 report
On 11/17/21 06:50, Zebediah Figura wrote:
while (!InterlockedExchange( futex, 0 ))
{
if (timeout)
{
LONGLONG timeleft = update_timeout( end );
struct timespec timespec;
timespec.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC;
timespec.tv_nsec = (timeleft % TICKSPERSEC) * 100;
ret = futex_wait( futex, 0, ×pec );
}
else
ret = futex_wait( futex, 0, NULL );
if (ret == -1 && errno == ETIMEDOUT) return STATUS_TIMEOUT;
}
return STATUS_ALERTED;
- }
+#endif
Do you think it is possible or makes sense to handle EINTR and retry wait to avoid delivering spurious wakeups to the application in this case? We have a lot of SIGUSR1 for Wine async I/O handling. Also, maybe it would be interesting to log unexpected errors? Not that anything besides EINTR should ever happen as I understand, but sometimes unexpected errors happen and may indicate a real issue and that happened once in the past with the current upstream in process sync.
On 11/17/21 11:48, Paul Gofman wrote:
On 11/17/21 06:50, Zebediah Figura wrote:
+ while (!InterlockedExchange( futex, 0 )) + { + if (timeout) + { + LONGLONG timeleft = update_timeout( end ); + struct timespec timespec;
+ timespec.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; + timespec.tv_nsec = (timeleft % TICKSPERSEC) * 100; + ret = futex_wait( futex, 0, ×pec ); + } + else + ret = futex_wait( futex, 0, NULL );
+ if (ret == -1 && errno == ETIMEDOUT) return STATUS_TIMEOUT; + } + return STATUS_ALERTED; + } +#endif
Do you think it is possible or makes sense to handle EINTR and retry wait to avoid delivering spurious wakeups to the application in this case? We have a lot of SIGUSR1 for Wine async I/O handling. Also, maybe it would be interesting to log unexpected errors? Not that anything besides EINTR should ever happen as I understand, but sometimes unexpected errors happen and may indicate a real issue and that happened once in the past with the current upstream in process sync.
Eh, please disregard the first part about EINTR, that doesn't relate to the present code.
On 11/17/21 2:52 AM, Paul Gofman wrote:
On 11/17/21 11:48, Paul Gofman wrote:
On 11/17/21 06:50, Zebediah Figura wrote:
+ while (!InterlockedExchange( futex, 0 )) + { + if (timeout) + { + LONGLONG timeleft = update_timeout( end ); + struct timespec timespec;
+ timespec.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; + timespec.tv_nsec = (timeleft % TICKSPERSEC) * 100; + ret = futex_wait( futex, 0, ×pec ); + } + else + ret = futex_wait( futex, 0, NULL );
+ if (ret == -1 && errno == ETIMEDOUT) return STATUS_TIMEOUT; + } + return STATUS_ALERTED; + } +#endif
Do you think it is possible or makes sense to handle EINTR and retry wait to avoid delivering spurious wakeups to the application in this case? We have a lot of SIGUSR1 for Wine async I/O handling. Also, maybe it would be interesting to log unexpected errors? Not that anything besides EINTR should ever happen as I understand, but sometimes unexpected errors happen and may indicate a real issue and that happened once in the past with the current upstream in process sync.
Eh, please disregard the first part about EINTR, that doesn't relate to the present code.
EINTR isn't relevant here, yes.
Complaining louder on unexpected errors is more reasonable, although we're in much greater control here relative to e.g. the old implementation of condition variables. I don't feel a pressing need to send a patch, but I won't object to one...
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/unix/sync.c | 75 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 47687d4d4e5..ce78c11fd4b 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2399,10 +2399,14 @@ NTSTATUS WINAPI NtQueryInformationAtom( RTL_ATOM atom, ATOM_INFORMATION_CLASS cl
union tid_alert_entry { +#ifdef __APPLE__ + semaphore_t sem; +#else HANDLE event; #ifdef __linux__ int futex; #endif +#endif };
#define TID_ALERT_BLOCK_SIZE (65536 / sizeof(union tid_alert_entry)) @@ -2437,6 +2441,17 @@ static union tid_alert_entry *get_tid_alert_entry( HANDLE tid )
entry = &tid_alert_blocks[block_idx][idx % TID_ALERT_BLOCK_SIZE];
+#ifdef __APPLE__ + if (!entry->sem) + { + semaphore_t sem; + + if (semaphore_create( mach_task_self(), &sem, SYNC_POLICY_FIFO, 0 )) + return NULL; + if (InterlockedCompareExchange( (int *)&entry->sem, sem, NULL )) + semaphore_destroy( mach_task_self(), sem ); + } +#else #ifdef __linux__ if (use_futexes()) return entry; @@ -2451,6 +2466,7 @@ static union tid_alert_entry *get_tid_alert_entry( HANDLE tid ) if (InterlockedCompareExchangePointer( &entry->event, event, NULL )) NtClose( event ); } +#endif
return entry; } @@ -2467,6 +2483,10 @@ NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid )
if (!entry) return STATUS_INVALID_CID;
+#ifdef __APPLE__ + semaphore_signal( entry->sem ); + return STATUS_SUCCESS; +#else #ifdef __linux__ if (use_futexes()) { @@ -2478,10 +2498,11 @@ NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid ) #endif
return NtSetEvent( entry->event, NULL ); +#endif }
-#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) static LONGLONG get_absolute_timeout( const LARGE_INTEGER *timeout ) { LARGE_INTEGER now; @@ -2504,6 +2525,57 @@ static LONGLONG update_timeout( ULONGLONG end ) #endif
+#ifdef __APPLE__ + +/*********************************************************************** + * NtWaitForAlertByThreadId (NTDLL.@) + */ +NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEGER *timeout ) +{ + union tid_alert_entry *entry = get_tid_alert_entry( NtCurrentTeb()->ClientId.UniqueThread ); + semaphore_t sem; + ULONGLONG end; + kern_return_t ret; + + TRACE( "%p %s\n", address, debugstr_timeout( timeout ) ); + + if (!entry) return STATUS_INVALID_CID; + sem = entry->sem; + + if (timeout) + { + if (timeout->QuadPart == TIMEOUT_INFINITE) + timeout = NULL; + else + end = get_absolute_timeout( timeout ); + } + + for (;;) + { + if (timeout) + { + LONGLONG timeleft = update_timeout( end ); + mach_timespec_t timespec; + + timespec.tv_sec = timeleft / (ULONGLONG)TICKSPERSEC; + timespec.tv_nsec = (timeleft % TICKSPERSEC) * 100; + ret = semaphore_timedwait( sem, timespec ); + } + else + ret = semaphore_wait( sem ); + + switch (ret) + { + case KERN_SUCCESS: return STATUS_ALERTED; + case KERN_ABORTED: continue; + case KERN_OPERATION_TIMED_OUT: return STATUS_TIMEOUT; + default: return STATUS_INVALID_HANDLE; + } + } +} + +#else + /*********************************************************************** * NtWaitForAlertByThreadId (NTDLL.@) */ @@ -2556,6 +2628,7 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG return status; }
+#endif
#ifdef __linux__
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102088
Your paranoid android.
=== debiant2 (build log) ===
../wine/dlls/ntdll/unix/sync.c:2439:2: error: #endif without #if ../wine/dlls/ntdll/unix/sync.c:2466: error: unterminated #else Task: The win32 Wine build failed
=== debiant2 (build log) ===
../wine/dlls/ntdll/unix/sync.c:2439:2: error: #endif without #if ../wine/dlls/ntdll/unix/sync.c:2466: error: unterminated #else Task: The wow64 Wine build failed
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=102085
Your paranoid android.
=== debiant2 (build log) ===
0048:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0048:err:winediag:nodrv_CreateWindow Unknown error (998). 0040:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0040:err:winediag:nodrv_CreateWindow Unknown error (998). 0050:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0050:err:winediag:nodrv_CreateWindow Unknown error (998). 002c:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 002c:err:winediag:nodrv_CreateWindow Unknown error (998). 007c:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 007c:err:winediag:nodrv_CreateWindow Unknown error (998). 00c0:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 00c0:err:winediag:nodrv_CreateWindow Unknown error (998). 0070:err:winediag:nodrv_CreateWindow Application tried to create a window, but no driver could be loaded. 0070:err:winediag:nodrv_CreateWindow Unknown error (998). Error: Mount manager not running, most likely your WINEPREFIX wasn't created correctly. Task: WineTest did not produce the wow32 report