[PATCH 1/4] ntdll: Implement NtAlertThreadByThreadId and NtWaitForAlertByThreadId.

Zebediah Figura zfigura at codeweavers.com
Tue Nov 16 21:50:41 CST 2021


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 at 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*);
-- 
2.33.0




More information about the wine-devel mailing list