diff --git a/patches/Staging/0001-kernel32-Add-winediag-message-to-show-warning-that-t.patch b/patches/Staging/0001-kernel32-Add-winediag-message-to-show-warning-that-t.patch index d120deb7..edc6abb7 100644 --- a/patches/Staging/0001-kernel32-Add-winediag-message-to-show-warning-that-t.patch +++ b/patches/Staging/0001-kernel32-Add-winediag-message-to-show-warning-that-t.patch @@ -1,4 +1,4 @@ -From 0cf6433af95363c5fbba2af482b2ba50b863dfb7 Mon Sep 17 00:00:00 2001 +From d02623b1c4d0fa97ea990d160529ecd98da08a85 Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Thu, 2 Oct 2014 19:44:31 +0200 Subject: [PATCH] ntdll: Print a warning message specifying the wine-staging @@ -9,7 +9,7 @@ Subject: [PATCH] ntdll: Print a warning message specifying the wine-staging 1 file changed, 15 insertions(+) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c -index 20bc3f977d1..c2187a19397 100644 +index 440a77d4063..3e3acdc5eac 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -44,6 +44,7 @@ WINE_DECLARE_DEBUG_CHANNEL(relay); @@ -20,7 +20,7 @@ index 20bc3f977d1..c2187a19397 100644 #ifdef _WIN64 #define DEFAULT_SECURITY_COOKIE_64 (((ULONGLONG)0x00002b99 << 32) | 0x2ddfa232) -@@ -3456,6 +3457,7 @@ static void process_breakpoint(void) +@@ -3509,6 +3510,7 @@ static void process_breakpoint(void) __ENDTRY } @@ -28,7 +28,7 @@ index 20bc3f977d1..c2187a19397 100644 /****************************************************************** * LdrInitializeThunk (NTDLL.@) -@@ -3465,6 +3467,9 @@ static void process_breakpoint(void) +@@ -3518,6 +3520,9 @@ static void process_breakpoint(void) */ void WINAPI LdrInitializeThunk( CONTEXT *context, ULONG_PTR unknown2, ULONG_PTR unknown3, ULONG_PTR unknown4 ) { @@ -38,7 +38,7 @@ index 20bc3f977d1..c2187a19397 100644 static int attach_done; int i; NTSTATUS status; -@@ -3483,6 +3488,16 @@ void WINAPI LdrInitializeThunk( CONTEXT *context, ULONG_PTR unknown2, ULONG_PTR +@@ -3536,6 +3541,16 @@ void WINAPI LdrInitializeThunk( CONTEXT *context, ULONG_PTR unknown2, ULONG_PTR entry = (void **)&context->u.s.X0; #endif @@ -54,7 +54,7 @@ index 20bc3f977d1..c2187a19397 100644 + if (process_detaching) NtTerminateThread( GetCurrentThread(), 0 ); - RtlEnterCriticalSection( &loader_section ); + teb_list_wrlock(); -- -2.28.0 +2.29.2 diff --git a/patches/Staging/0002-winelib-Append-Staging-at-the-end-of-the-version-s.patch b/patches/Staging/0002-winelib-Append-Staging-at-the-end-of-the-version-s.patch deleted file mode 100644 index 9772c69e..00000000 --- a/patches/Staging/0002-winelib-Append-Staging-at-the-end-of-the-version-s.patch +++ /dev/null @@ -1,35 +0,0 @@ -From ce5e1fc75139e4de9d92dfe27b4a513a96da013c Mon Sep 17 00:00:00 2001 -From: Sebastian Lackner -Date: Thu, 2 Oct 2014 19:53:46 +0200 -Subject: [PATCH] winelib: Append '(Staging)' at the end of the version string. - ---- - Makefile.in | 2 +- - dlls/ntdll/Makefile.in | 1 + - 2 files changed, 2 insertions(+), 1 deletion(-) - -diff --git a/Makefile.in b/Makefile.in -index 307a95b3b1a..61019fed949 100644 ---- a/Makefile.in -+++ b/Makefile.in -@@ -116,7 +116,7 @@ install-manpages:: manpages - # Rules for generated source files - - dlls/ntdll/unix/version.c: dummy -- @version=`(GIT_DIR=$(srcdir)/.git git describe HEAD 2>/dev/null || echo "wine-$(PACKAGE_VERSION)") | sed -n -e '$$s/\(.*\)/const char wine_build[] = "\1";/p'` && (echo $$version | cmp -s - $@) || echo $$version >$@ || ($(RM) $@ && exit 1) -+ @version=`(GIT_DIR=$(srcdir)/.git git describe HEAD 2>/dev/null || echo "wine-$(PACKAGE_VERSION)") | sed -n -e '$$s/\(.*\)/const char wine_build[] = "\1 (Staging)";/p'` && (echo $$version | cmp -s - $@) || echo $$version >$@ || (rm -f $@ && exit 1) - - programs/winetest/build.rc: dummy - @build="STRINGTABLE { 1 \"`GIT_DIR=$(srcdir)/.git git rev-parse HEAD 2>/dev/null`\" }" && (echo $$build | cmp -s - $@) || echo $$build >$@ || (rm -f $@ && exit 1) -diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in -index f39ffb42c6f..67847bb9392 100644 ---- a/dlls/ntdll/Makefile.in -+++ b/dlls/ntdll/Makefile.in -@@ -79,3 +79,4 @@ unix_loader_EXTRADEFS = \ - -DBINDIR=\"${bindir}\" \ - -DDLL_TO_BINDIR=\"`${MAKEDEP} -R ${dlldir} ${bindir}`\" \ - -DBIN_TO_DATADIR=\"`${MAKEDEP} -R ${bindir} ${datadir}/wine`\" -+ --- -2.28.0 - diff --git a/patches/Staging/definition b/patches/Staging/definition index a3db58fd..0e0ff02e 100644 --- a/patches/Staging/definition +++ b/patches/Staging/definition @@ -1 +1 @@ -#Depends: ntdll-FLS_Callbacks +Depends: ntdll-NtAlertThreadByThreadId diff --git a/patches/eventfd_synchronization/0035-ntdll-server-Implement-alertable-waits.patch b/patches/eventfd_synchronization/0035-ntdll-server-Implement-alertable-waits.patch index dd4bfbf7..15d43da0 100644 --- a/patches/eventfd_synchronization/0035-ntdll-server-Implement-alertable-waits.patch +++ b/patches/eventfd_synchronization/0035-ntdll-server-Implement-alertable-waits.patch @@ -1,4 +1,4 @@ -From ec0fc4f9f1919616dcaecd4712749761d271c7c8 Mon Sep 17 00:00:00 2001 +From b04ab24c90337271146425edc2059328e42743b6 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Mon, 6 Jul 2020 17:17:31 -0500 Subject: [PATCH] ntdll, server: Implement alertable waits. @@ -164,29 +164,29 @@ index d1a282c65e9..2a7c3865617 100644 /* We need to let the server know when we are doing a message wait, and when we diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h -index c3ad0a41098..c2f215320ee 100644 +index 23731f0fdc6..b2020b6311a 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h -@@ -57,6 +57,7 @@ struct ntdll_thread_data - struct list entry; /* entry in TEB list */ - PRTL_THREAD_START_ROUTINE start; /* thread entry point */ - void *param; /* thread entry point parameter */ +@@ -50,6 +50,7 @@ struct ntdll_thread_data + void *cpu_data[16]; /* reserved for CPU-specific data */ + struct debug_info *debug_info; /* info for debugstr functions */ + void *start_stack; /* stack for thread startup */ + int esync_apc_fd; /* fd to wait on for user APCs */ - }; - - C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); + int request_fd; /* fd for sending server requests */ + int reply_fd; /* fd for receiving server replies */ + int wait_fd[2]; /* fd for sleeping server requests */ diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index d79e3de662e..6b83072b750 100644 +index 785583a340a..93646bc51f4 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c -@@ -2555,6 +2555,7 @@ static void init_teb( TEB *teb, PEB *peb ) +@@ -2552,6 +2552,7 @@ static void init_teb( TEB *teb, PEB *peb ) + InitializeListHead( &teb->ActivationContextStack.FrameListCache ); + teb->StaticUnicodeString.Buffer = teb->StaticUnicodeBuffer; + teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); ++ thread_data->esync_apc_fd = -1; + thread_data->request_fd = -1; thread_data->reply_fd = -1; thread_data->wait_fd[0] = -1; - thread_data->wait_fd[1] = -1; -+ thread_data->esync_apc_fd = -1; - list_add_head( &teb_list, &thread_data->entry ); - } - diff --git a/server/esync.c b/server/esync.c index 7b5b0761516..33a448d0a57 100644 --- a/server/esync.c @@ -244,10 +244,10 @@ index aeb58c5469c..cea025d9308 100644 void esync_clear( int fd ); diff --git a/server/protocol.def b/server/protocol.def -index 5b8f66ffd80..a110c4c60a6 100644 +index 30296aedde8..d2772aacec2 100644 --- a/server/protocol.def +++ b/server/protocol.def -@@ -3714,3 +3714,7 @@ enum esync_type +@@ -3707,3 +3707,7 @@ enum esync_type @REQ(esync_msgwait) int in_msgwait; /* are we in a message wait? */ @END @@ -256,7 +256,7 @@ index 5b8f66ffd80..a110c4c60a6 100644 +@REQ(get_esync_apc_fd) +@END diff --git a/server/thread.c b/server/thread.c -index d22555ca317..6bf4be04af5 100644 +index b52a1f5048c..6fb8684f5e0 100644 --- a/server/thread.c +++ b/server/thread.c @@ -227,6 +227,7 @@ static inline void init_thread_structure( struct thread *thread ) @@ -267,7 +267,7 @@ index d22555ca317..6bf4be04af5 100644 thread->debug_ctx = NULL; thread->system_regs = 0; thread->queue = NULL; -@@ -363,7 +364,10 @@ struct thread *create_thread( int fd, struct process *process, const struct secu +@@ -364,7 +365,10 @@ struct thread *create_thread( int fd, struct process *process, const struct secu } if (do_esync()) @@ -278,7 +278,7 @@ index d22555ca317..6bf4be04af5 100644 set_fd_events( thread->request_fd, POLLIN ); /* start listening to events */ add_process_thread( thread->process, thread ); -@@ -1150,8 +1154,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr +@@ -1152,8 +1156,13 @@ static int queue_apc( struct process *process, struct thread *thread, struct thr grab_object( apc ); list_add_tail( queue, &apc->entry ); if (!list_prev( queue, &apc->entry )) /* first one */ @@ -292,7 +292,7 @@ index d22555ca317..6bf4be04af5 100644 return 1; } -@@ -1197,6 +1206,10 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system +@@ -1199,6 +1208,10 @@ static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system apc = LIST_ENTRY( ptr, struct thread_apc, entry ); list_remove( ptr ); } @@ -304,7 +304,7 @@ index d22555ca317..6bf4be04af5 100644 } diff --git a/server/thread.h b/server/thread.h -index f19080988e9..5faa2456a81 100644 +index faffe13d795..0f6108b684a 100644 --- a/server/thread.h +++ b/server/thread.h @@ -55,6 +55,7 @@ struct thread @@ -316,5 +316,5 @@ index f19080988e9..5faa2456a81 100644 unsigned int system_regs; /* which system regs have been set */ struct msg_queue *queue; /* message queue */ -- -2.28.0 +2.29.2 diff --git a/patches/ntdll-Dealloc_Thread_Stack/definition b/patches/ntdll-Dealloc_Thread_Stack/definition index b0f50a6f..faabfc59 100644 --- a/patches/ntdll-Dealloc_Thread_Stack/definition +++ b/patches/ntdll-Dealloc_Thread_Stack/definition @@ -1 +1,2 @@ Fixes: Do not allow to deallocate thread stack for current thread +Depends: ntdll-NtAlertThreadByThreadId diff --git a/patches/ntdll-NtAlertThreadByThreadId/0001-ntdll-tests-Move-some-tests-to-a-new-sync.c-file.patch b/patches/ntdll-NtAlertThreadByThreadId/0001-ntdll-tests-Move-some-tests-to-a-new-sync.c-file.patch new file mode 100644 index 00000000..e9903b98 --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0001-ntdll-tests-Move-some-tests-to-a-new-sync.c-file.patch @@ -0,0 +1,1109 @@ +From 195fb3b4d9c203bcac532d0c5d7db7a37d5298c4 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 9 Jan 2020 13:44:01 -0600 +Subject: [PATCH] ntdll/tests: Move some tests to a new sync.c file. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/tests/Makefile.in | 1 + + dlls/ntdll/tests/om.c | 478 ------------------------------- + dlls/ntdll/tests/sync.c | 527 +++++++++++++++++++++++++++++++++++ + 3 files changed, 528 insertions(+), 478 deletions(-) + create mode 100644 dlls/ntdll/tests/sync.c + +diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in +index ed15c51339f..9a99c01bd7c 100644 +--- a/dlls/ntdll/tests/Makefile.in ++++ b/dlls/ntdll/tests/Makefile.in +@@ -21,6 +21,7 @@ C_SRCS = \ + rtlbitmap.c \ + rtlstr.c \ + string.c \ ++ sync.c \ + threadpool.c \ + time.c \ + virtual.c +diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c +index cebac8b36c0..dabc81a73f3 100644 +--- a/dlls/ntdll/tests/om.c ++++ b/dlls/ntdll/tests/om.c +@@ -28,10 +28,6 @@ + static VOID (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR ); + static NTSTATUS (WINAPI *pNtCreateEvent) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, BOOLEAN, BOOLEAN); + static NTSTATUS (WINAPI *pNtOpenEvent) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES); +-static NTSTATUS (WINAPI *pNtPulseEvent) ( HANDLE, PLONG ); +-static NTSTATUS (WINAPI *pNtQueryEvent) ( HANDLE, EVENT_INFORMATION_CLASS, PVOID, ULONG, PULONG ); +-static NTSTATUS (WINAPI *pNtResetEvent) ( HANDLE, LONG* ); +-static NTSTATUS (WINAPI *pNtSetEvent) ( HANDLE, LONG* ); + static NTSTATUS (WINAPI *pNtCreateJobObject)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); + static NTSTATUS (WINAPI *pNtOpenJobObject)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES ); + static NTSTATUS (WINAPI *pNtCreateKey)( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG, +@@ -42,8 +38,6 @@ static NTSTATUS (WINAPI *pNtCreateMailslotFile)( PHANDLE, ACCESS_MASK, POBJECT_A + ULONG, ULONG, ULONG, PLARGE_INTEGER ); + static NTSTATUS (WINAPI *pNtCreateMutant)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, BOOLEAN ); + static NTSTATUS (WINAPI *pNtOpenMutant) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES ); +-static NTSTATUS (WINAPI *pNtQueryMutant) ( HANDLE, MUTANT_INFORMATION_CLASS, PVOID, ULONG, PULONG ); +-static NTSTATUS (WINAPI *pNtReleaseMutant)( HANDLE, PLONG ); + static NTSTATUS (WINAPI *pNtCreateSemaphore)( PHANDLE, ACCESS_MASK,const POBJECT_ATTRIBUTES,LONG,LONG ); + static NTSTATUS (WINAPI *pNtOpenSemaphore)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES ); + static NTSTATUS (WINAPI *pNtCreateTimer) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, TIMER_TYPE ); +@@ -64,22 +58,12 @@ static NTSTATUS (WINAPI *pNtQueryObject)(HANDLE,OBJECT_INFORMATION_CLASS,PVOID,U + static NTSTATUS (WINAPI *pNtReleaseSemaphore)(HANDLE, ULONG, PULONG); + static NTSTATUS (WINAPI *pNtCreateKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, ULONG ); + static NTSTATUS (WINAPI *pNtOpenKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES * ); +-static NTSTATUS (WINAPI *pNtWaitForKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); +-static NTSTATUS (WINAPI *pNtReleaseKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); + 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 * ); + static NTSTATUS (WINAPI *pNtOpenProcess)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, const CLIENT_ID * ); + static NTSTATUS (WINAPI *pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG); + +-#define KEYEDEVENT_WAIT 0x0001 +-#define KEYEDEVENT_WAKE 0x0002 +-#define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x0003) +- + #define ROUND_UP(value, alignment) (((value) + ((alignment) - 1)) & ~((alignment)-1)) + + static LPCSTR wine_dbgstr_us( const UNICODE_STRING *us ) +@@ -1696,266 +1680,6 @@ static void test_type_mismatch(void) + pNtClose( h ); + } + +-static void test_event(void) +-{ +- HANDLE Event; +- HANDLE Event2; +- LONG prev_state = 0xdeadbeef; +- NTSTATUS status; +- UNICODE_STRING str; +- OBJECT_ATTRIBUTES attr; +- EVENT_BASIC_INFORMATION info; +- +- pRtlInitUnicodeString( &str, L"\\BaseNamedObjects\\testEvent" ); +- InitializeObjectAttributes(&attr, &str, 0, 0, NULL); +- +- status = pNtCreateEvent(&Event, GENERIC_ALL, &attr, 1, 0); +- ok( status == STATUS_SUCCESS, "NtCreateEvent failed %08x\n", status ); +- +- status = pNtPulseEvent(Event, &prev_state); +- ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08x\n", status ); +- ok( !prev_state, "prev_state = %x\n", prev_state ); +- +- status = pNtQueryEvent(Event, EventBasicInformation, &info, sizeof(info), NULL); +- ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08x\n", status ); +- ok( info.EventType == 1 && info.EventState == 0, +- "NtQueryEvent failed, expected 1 0, got %d %d\n", info.EventType, info.EventState ); +- +- status = pNtOpenEvent(&Event2, GENERIC_ALL, &attr); +- ok( status == STATUS_SUCCESS, "NtOpenEvent failed %08x\n", status ); +- +- pNtClose(Event); +- Event = Event2; +- +- status = pNtQueryEvent(Event, EventBasicInformation, &info, sizeof(info), NULL); +- ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08x\n", status ); +- ok( info.EventType == 1 && info.EventState == 0, +- "NtQueryEvent failed, expected 1 0, got %d %d\n", info.EventType, info.EventState ); +- +- status = pNtSetEvent( Event, &prev_state ); +- ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); +- ok( !prev_state, "prev_state = %x\n", prev_state ); +- +- status = pNtSetEvent( Event, &prev_state ); +- ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); +- ok( prev_state == 1, "prev_state = %x\n", prev_state ); +- +- status = pNtResetEvent( Event, &prev_state ); +- ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); +- ok( prev_state == 1, "prev_state = %x\n", prev_state ); +- +- status = pNtResetEvent( Event, &prev_state ); +- ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); +- ok( !prev_state, "prev_state = %x\n", prev_state ); +- +- status = pNtPulseEvent( Event, &prev_state ); +- ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08x\n", status ); +- ok( !prev_state, "prev_state = %x\n", prev_state ); +- +- status = pNtSetEvent( Event, &prev_state ); +- ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); +- ok( !prev_state, "prev_state = %x\n", prev_state ); +- +- status = pNtPulseEvent( Event, &prev_state ); +- ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08x\n", status ); +- ok( prev_state == 1, "prev_state = %x\n", prev_state ); +- +- pNtClose(Event); +-} +- +-static const WCHAR keyed_nameW[] = L"\\BaseNamedObjects\\WineTestEvent"; +- +-static DWORD WINAPI keyed_event_thread( void *arg ) +-{ +- HANDLE handle; +- NTSTATUS status; +- LARGE_INTEGER timeout; +- OBJECT_ATTRIBUTES attr; +- UNICODE_STRING str; +- ULONG_PTR i; +- +- attr.Length = sizeof(attr); +- attr.RootDirectory = 0; +- attr.ObjectName = &str; +- attr.Attributes = 0; +- attr.SecurityDescriptor = NULL; +- attr.SecurityQualityOfService = NULL; +- RtlInitUnicodeString( &str, keyed_nameW ); +- +- status = pNtOpenKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS, &attr ); +- ok( !status, "NtOpenKeyedEvent failed %x\n", status ); +- +- for (i = 0; i < 20; i++) +- { +- if (i & 1) +- status = pNtWaitForKeyedEvent( handle, (void *)(i * 2), 0, NULL ); +- else +- status = pNtReleaseKeyedEvent( handle, (void *)(i * 2), 0, NULL ); +- ok( status == STATUS_SUCCESS, "%li: failed %x\n", i, status ); +- Sleep( 20 - i ); +- } +- +- status = pNtReleaseKeyedEvent( handle, (void *)0x1234, 0, NULL ); +- ok( status == STATUS_SUCCESS, "NtReleaseKeyedEvent %x\n", status ); +- +- timeout.QuadPart = -10000; +- status = pNtWaitForKeyedEvent( handle, (void *)0x5678, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, (void *)0x9abc, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); +- +- NtClose( handle ); +- return 0; +-} +- +-static void test_keyed_events(void) +-{ +- OBJECT_ATTRIBUTES attr; +- UNICODE_STRING str; +- HANDLE handle, event, thread; +- NTSTATUS status; +- LARGE_INTEGER timeout; +- ULONG_PTR i; +- +- if (!pNtCreateKeyedEvent) +- { +- win_skip( "Keyed events not supported\n" ); +- return; +- } +- +- attr.Length = sizeof(attr); +- attr.RootDirectory = 0; +- attr.ObjectName = &str; +- attr.Attributes = 0; +- attr.SecurityDescriptor = NULL; +- attr.SecurityQualityOfService = NULL; +- RtlInitUnicodeString( &str, keyed_nameW ); +- +- status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS | SYNCHRONIZE, &attr, 0 ); +- ok( !status, "NtCreateKeyedEvent failed %x\n", status ); +- +- status = WaitForSingleObject( handle, 1000 ); +- ok( status == 0, "WaitForSingleObject %x\n", status ); +- +- timeout.QuadPart = -100000; +- status = pNtWaitForKeyedEvent( handle, (void *)255, 0, &timeout ); +- ok( status == STATUS_INVALID_PARAMETER_1, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, (void *)255, 0, &timeout ); +- ok( status == STATUS_INVALID_PARAMETER_1, "NtReleaseKeyedEvent %x\n", status ); +- +- status = pNtWaitForKeyedEvent( handle, (void *)254, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, (void *)254, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); +- +- status = pNtWaitForKeyedEvent( handle, NULL, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, NULL, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); +- +- status = pNtWaitForKeyedEvent( NULL, (void *)8, 0, &timeout ); +- ok( status == STATUS_TIMEOUT || broken(status == STATUS_INVALID_HANDLE), /* XP/2003 */ +- "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( NULL, (void *)8, 0, &timeout ); +- ok( status == STATUS_TIMEOUT || broken(status == STATUS_INVALID_HANDLE), /* XP/2003 */ +- "NtReleaseKeyedEvent %x\n", status ); +- +- status = pNtWaitForKeyedEvent( (HANDLE)0xdeadbeef, (void *)9, 0, &timeout ); +- ok( status == STATUS_INVALID_PARAMETER_1, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( (HANDLE)0xdeadbeef, (void *)9, 0, &timeout ); +- ok( status == STATUS_INVALID_PARAMETER_1, "NtReleaseKeyedEvent %x\n", status ); +- +- status = pNtWaitForKeyedEvent( (HANDLE)0xdeadbeef, (void *)8, 0, &timeout ); +- ok( status == STATUS_INVALID_HANDLE, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( (HANDLE)0xdeadbeef, (void *)8, 0, &timeout ); +- ok( status == STATUS_INVALID_HANDLE, "NtReleaseKeyedEvent %x\n", status ); +- +- thread = CreateThread( NULL, 0, keyed_event_thread, 0, 0, NULL ); +- for (i = 0; i < 20; i++) +- { +- if (i & 1) +- status = pNtReleaseKeyedEvent( handle, (void *)(i * 2), 0, NULL ); +- else +- status = pNtWaitForKeyedEvent( handle, (void *)(i * 2), 0, NULL ); +- ok( status == STATUS_SUCCESS, "%li: failed %x\n", i, status ); +- Sleep( i ); +- } +- status = pNtWaitForKeyedEvent( handle, (void *)0x1234, 0, &timeout ); +- ok( status == STATUS_SUCCESS, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtWaitForKeyedEvent( handle, (void *)0x5678, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, (void *)0x9abc, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); +- +- ok( WaitForSingleObject( thread, 30000 ) == 0, "wait failed\n" ); +- +- NtClose( handle ); +- +- /* test access rights */ +- +- status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_WAIT, &attr, 0 ); +- ok( !status, "NtCreateKeyedEvent failed %x\n", status ); +- status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_ACCESS_DENIED, "NtReleaseKeyedEvent %x\n", status ); +- NtClose( handle ); +- +- status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_WAKE, &attr, 0 ); +- ok( !status, "NtCreateKeyedEvent failed %x\n", status ); +- status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_ACCESS_DENIED, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); +- NtClose( handle ); +- +- status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS, &attr, 0 ); +- ok( !status, "NtCreateKeyedEvent failed %x\n", status ); +- status = WaitForSingleObject( handle, 1000 ); +- ok( status == WAIT_FAILED && GetLastError() == ERROR_ACCESS_DENIED, +- "WaitForSingleObject %x err %u\n", status, GetLastError() ); +- status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); +- NtClose( handle ); +- +- /* GENERIC_READ gives wait access */ +- status = pNtCreateKeyedEvent( &handle, GENERIC_READ, &attr, 0 ); +- ok( !status, "NtCreateKeyedEvent failed %x\n", status ); +- status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_ACCESS_DENIED, "NtReleaseKeyedEvent %x\n", status ); +- NtClose( handle ); +- +- /* GENERIC_WRITE gives wake access */ +- status = pNtCreateKeyedEvent( &handle, GENERIC_WRITE, &attr, 0 ); +- ok( !status, "NtCreateKeyedEvent failed %x\n", status ); +- status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_ACCESS_DENIED, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); +- ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); +- +- /* it's not an event */ +- status = pNtPulseEvent( handle, NULL ); +- ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtPulseEvent %x\n", status ); +- +- status = pNtCreateEvent( &event, GENERIC_ALL, &attr, FALSE, FALSE ); +- ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_TYPE_MISMATCH /* 7+ */, +- "CreateEvent %x\n", status ); +- +- NtClose( handle ); +- +- status = pNtCreateEvent( &event, GENERIC_ALL, &attr, FALSE, FALSE ); +- ok( status == 0, "CreateEvent %x\n", status ); +- status = pNtWaitForKeyedEvent( event, (void *)8, 0, &timeout ); +- ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtWaitForKeyedEvent %x\n", status ); +- status = pNtReleaseKeyedEvent( event, (void *)8, 0, &timeout ); +- ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtReleaseKeyedEvent %x\n", status ); +- NtClose( event ); +-} +- + static void test_null_device(void) + { + OBJECT_ATTRIBUTES attr; +@@ -2029,192 +1753,6 @@ static void test_null_device(void) + CloseHandle(ov.hEvent); + } + +-static DWORD WINAPI mutant_thread( void *arg ) +-{ +- MUTANT_BASIC_INFORMATION info; +- NTSTATUS status; +- HANDLE mutant; +- DWORD ret; +- +- mutant = arg; +- ret = WaitForSingleObject( mutant, 1000 ); +- ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); +- +- memset(&info, 0xcc, sizeof(info)); +- status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); +- ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); +- ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); +- ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); +- ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); +- /* abandon mutant */ +- +- return 0; +-} +- +-static void test_mutant(void) +-{ +- MUTANT_BASIC_INFORMATION info; +- OBJECT_ATTRIBUTES attr; +- UNICODE_STRING str; +- NTSTATUS status; +- HANDLE mutant; +- HANDLE thread; +- DWORD ret; +- ULONG len; +- LONG prev; +- +- pRtlInitUnicodeString(&str, L"\\BaseNamedObjects\\test_mutant"); +- InitializeObjectAttributes(&attr, &str, 0, 0, NULL); +- status = pNtCreateMutant(&mutant, GENERIC_ALL, &attr, TRUE); +- ok( status == STATUS_SUCCESS, "Failed to create Mutant(%08x)\n", status ); +- +- /* bogus */ +- status = pNtQueryMutant(mutant, MutantBasicInformation, &info, 0, NULL); +- ok( status == STATUS_INFO_LENGTH_MISMATCH, +- "Failed to NtQueryMutant, expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status ); +- status = pNtQueryMutant(mutant, 0x42, &info, sizeof(info), NULL); +- ok( status == STATUS_INVALID_INFO_CLASS || broken(status == STATUS_NOT_IMPLEMENTED), /* 32-bit on Vista/2k8 */ +- "Failed to NtQueryMutant, expected STATUS_INVALID_INFO_CLASS, got %08x\n", status ); +- status = pNtQueryMutant((HANDLE)0xdeadbeef, MutantBasicInformation, &info, sizeof(info), NULL); +- ok( status == STATUS_INVALID_HANDLE, +- "Failed to NtQueryMutant, expected STATUS_INVALID_HANDLE, got %08x\n", status ); +- +- /* new */ +- len = -1; +- memset(&info, 0xcc, sizeof(info)); +- status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), &len); +- ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); +- ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); +- ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); +- ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); +- ok( len == sizeof(info), "got %u\n", len ); +- +- ret = WaitForSingleObject( mutant, 1000 ); +- ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); +- +- memset(&info, 0xcc, sizeof(info)); +- status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); +- ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); +- ok( info.CurrentCount == -1, "expected -1, got %d\n", info.CurrentCount ); +- ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); +- ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); +- +- prev = 0xdeadbeef; +- status = pNtReleaseMutant(mutant, &prev); +- ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status ); +- ok( prev == -1, "NtQueryRelease failed, expected -1, got %d\n", prev ); +- +- prev = 0xdeadbeef; +- status = pNtReleaseMutant(mutant, &prev); +- ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status ); +- ok( prev == 0, "NtQueryRelease failed, expected 0, got %d\n", prev ); +- +- memset(&info, 0xcc, sizeof(info)); +- status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); +- ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); +- ok( info.CurrentCount == 1, "expected 1, got %d\n", info.CurrentCount ); +- ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); +- ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); +- +- /* abandoned */ +- thread = CreateThread( NULL, 0, mutant_thread, mutant, 0, NULL ); +- ret = WaitForSingleObject( thread, 1000 ); +- ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); +- CloseHandle( thread ); +- +- memset(&info, 0xcc, sizeof(info)); +- status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); +- ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); +- ok( info.CurrentCount == 1, "expected 0, got %d\n", info.CurrentCount ); +- ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); +- ok( info.AbandonedState == TRUE, "expected TRUE, got %d\n", info.AbandonedState ); +- +- ret = WaitForSingleObject( mutant, 1000 ); +- ok( ret == WAIT_ABANDONED_0, "WaitForSingleObject failed %08x\n", ret ); +- +- memset(&info, 0xcc, sizeof(info)); +- status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); +- ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); +- ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); +- ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); +- ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); +- +- 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 += 100*10000; +- ticks = GetTickCount(); +- status = pRtlWaitOnAddress(&address, &compare, 8, &timeout); +- ticks = GetTickCount() - ticks; +- ok(status == STATUS_TIMEOUT, "got 0x%08x\n", status); +- ok(ticks >= 90 && ticks <= 1000, "got %u\n", ticks); +- ok(address == 0, "got %s\n", wine_dbgstr_longlong(address)); +- ok(compare == 0, "got %s\n", wine_dbgstr_longlong(compare)); +- +- /* different address size */ +- for (size = 1; size <= 4; size <<= 1) +- { +- compare = ~0; +- compare <<= size * 8; +- +- timeout.QuadPart = -100 * 10000; +- ticks = GetTickCount(); +- status = pRtlWaitOnAddress(&address, &compare, size, &timeout); +- ticks = GetTickCount() - ticks; +- ok(status == STATUS_TIMEOUT, "got 0x%08x\n", status); +- ok(ticks >= 90 && ticks <= 1000, "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 %s\n", wine_dbgstr_longlong(address)); +- pRtlWakeAddressAll(&address); +- ok(address == 0, "got %s\n", wine_dbgstr_longlong(address)); +-} +- + static void test_process(void) + { + OBJECT_ATTRIBUTES attr; +@@ -2278,13 +1816,7 @@ START_TEST(om) + pNtCreateMailslotFile = (void *)GetProcAddress(hntdll, "NtCreateMailslotFile"); + pNtCreateMutant = (void *)GetProcAddress(hntdll, "NtCreateMutant"); + pNtOpenEvent = (void *)GetProcAddress(hntdll, "NtOpenEvent"); +- pNtQueryEvent = (void *)GetProcAddress(hntdll, "NtQueryEvent"); +- pNtPulseEvent = (void *)GetProcAddress(hntdll, "NtPulseEvent"); +- pNtResetEvent = (void *)GetProcAddress(hntdll, "NtResetEvent"); +- pNtSetEvent = (void *)GetProcAddress(hntdll, "NtSetEvent"); + pNtOpenMutant = (void *)GetProcAddress(hntdll, "NtOpenMutant"); +- pNtQueryMutant = (void *)GetProcAddress(hntdll, "NtQueryMutant"); +- pNtReleaseMutant = (void *)GetProcAddress(hntdll, "NtReleaseMutant"); + pNtOpenFile = (void *)GetProcAddress(hntdll, "NtOpenFile"); + pNtClose = (void *)GetProcAddress(hntdll, "NtClose"); + pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString"); +@@ -2304,15 +1836,9 @@ START_TEST(om) + pNtReleaseSemaphore = (void *)GetProcAddress(hntdll, "NtReleaseSemaphore"); + pNtCreateKeyedEvent = (void *)GetProcAddress(hntdll, "NtCreateKeyedEvent"); + pNtOpenKeyedEvent = (void *)GetProcAddress(hntdll, "NtOpenKeyedEvent"); +- pNtWaitForKeyedEvent = (void *)GetProcAddress(hntdll, "NtWaitForKeyedEvent"); +- pNtReleaseKeyedEvent = (void *)GetProcAddress(hntdll, "NtReleaseKeyedEvent"); + 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"); + pNtOpenProcess = (void *)GetProcAddress(hntdll, "NtOpenProcess"); + pNtQuerySystemInformation = (void *)GetProcAddress(hntdll, "NtQuerySystemInformation"); + +@@ -2325,10 +1851,6 @@ START_TEST(om) + test_query_object(); + test_query_object_types(); + test_type_mismatch(); +- test_event(); +- test_mutant(); +- test_keyed_events(); + test_null_device(); +- test_wait_on_address(); + test_process(); + } +diff --git a/dlls/ntdll/tests/sync.c b/dlls/ntdll/tests/sync.c +new file mode 100644 +index 00000000000..9967a2be257 +--- /dev/null ++++ b/dlls/ntdll/tests/sync.c +@@ -0,0 +1,527 @@ ++/* ++ * Unit tests for NT synchronization objects ++ * ++ * Copyright 2020 Zebediah Figura ++ * ++ * 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 ++ ++#include "ntstatus.h" ++#define WIN32_NO_STATUS ++#include "windef.h" ++#include "winternl.h" ++#include "wine/test.h" ++ ++static NTSTATUS (WINAPI *pNtClose)( HANDLE ); ++static NTSTATUS (WINAPI *pNtCreateEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, BOOLEAN, BOOLEAN ); ++static NTSTATUS (WINAPI *pNtCreateKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, ULONG ); ++static NTSTATUS (WINAPI *pNtCreateMutant)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, BOOLEAN ); ++static NTSTATUS (WINAPI *pNtOpenEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES * ); ++static NTSTATUS (WINAPI *pNtOpenKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES * ); ++static NTSTATUS (WINAPI *pNtPulseEvent)( HANDLE, LONG * ); ++static NTSTATUS (WINAPI *pNtQueryEvent)( HANDLE, EVENT_INFORMATION_CLASS, void *, ULONG, ULONG * ); ++static NTSTATUS (WINAPI *pNtQueryMutant)( HANDLE, MUTANT_INFORMATION_CLASS, void *, ULONG, ULONG * ); ++static NTSTATUS (WINAPI *pNtQuerySystemTime)( LARGE_INTEGER * ); ++static NTSTATUS (WINAPI *pNtReleaseKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); ++static NTSTATUS (WINAPI *pNtReleaseMutant)( HANDLE, LONG * ); ++static NTSTATUS (WINAPI *pNtResetEvent)( HANDLE, LONG * ); ++static NTSTATUS (WINAPI *pNtSetEvent)( HANDLE, LONG * ); ++static NTSTATUS (WINAPI *pNtWaitForKeyedEvent)( HANDLE, const void *, BOOLEAN, const LARGE_INTEGER * ); ++static void (WINAPI *pRtlInitUnicodeString)( UNICODE_STRING *, const WCHAR * ); ++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 ++#define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x0003) ++ ++static void test_event(void) ++{ ++ HANDLE Event; ++ HANDLE Event2; ++ LONG prev_state = 0xdeadbeef; ++ NTSTATUS status; ++ UNICODE_STRING str; ++ OBJECT_ATTRIBUTES attr; ++ EVENT_BASIC_INFORMATION info; ++ ++ pRtlInitUnicodeString(&str, L"\\BaseNamedObjects\\testEvent"); ++ InitializeObjectAttributes(&attr, &str, 0, 0, NULL); ++ ++ status = pNtCreateEvent(&Event, GENERIC_ALL, &attr, 1, 0); ++ ok( status == STATUS_SUCCESS, "NtCreateEvent failed %08x\n", status ); ++ ++ status = pNtPulseEvent(Event, &prev_state); ++ ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08x\n", status ); ++ ok( !prev_state, "prev_state = %x\n", prev_state ); ++ ++ status = pNtQueryEvent(Event, EventBasicInformation, &info, sizeof(info), NULL); ++ ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08x\n", status ); ++ ok( info.EventType == 1 && info.EventState == 0, ++ "NtQueryEvent failed, expected 1 0, got %d %d\n", info.EventType, info.EventState ); ++ ++ status = pNtOpenEvent(&Event2, GENERIC_ALL, &attr); ++ ok( status == STATUS_SUCCESS, "NtOpenEvent failed %08x\n", status ); ++ ++ pNtClose(Event); ++ Event = Event2; ++ ++ status = pNtQueryEvent(Event, EventBasicInformation, &info, sizeof(info), NULL); ++ ok( status == STATUS_SUCCESS, "NtQueryEvent failed %08x\n", status ); ++ ok( info.EventType == 1 && info.EventState == 0, ++ "NtQueryEvent failed, expected 1 0, got %d %d\n", info.EventType, info.EventState ); ++ ++ status = pNtSetEvent( Event, &prev_state ); ++ ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); ++ ok( !prev_state, "prev_state = %x\n", prev_state ); ++ ++ status = pNtSetEvent( Event, &prev_state ); ++ ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); ++ ok( prev_state == 1, "prev_state = %x\n", prev_state ); ++ ++ status = pNtResetEvent( Event, &prev_state ); ++ ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); ++ ok( prev_state == 1, "prev_state = %x\n", prev_state ); ++ ++ status = pNtResetEvent( Event, &prev_state ); ++ ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); ++ ok( !prev_state, "prev_state = %x\n", prev_state ); ++ ++ status = pNtPulseEvent( Event, &prev_state ); ++ ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08x\n", status ); ++ ok( !prev_state, "prev_state = %x\n", prev_state ); ++ ++ status = pNtSetEvent( Event, &prev_state ); ++ ok( status == STATUS_SUCCESS, "NtSetEvent failed: %08x\n", status ); ++ ok( !prev_state, "prev_state = %x\n", prev_state ); ++ ++ status = pNtPulseEvent( Event, &prev_state ); ++ ok( status == STATUS_SUCCESS, "NtPulseEvent failed %08x\n", status ); ++ ok( prev_state == 1, "prev_state = %x\n", prev_state ); ++ ++ pNtClose(Event); ++} ++ ++static const WCHAR keyed_nameW[] = L"\\BaseNamedObjects\\WineTestEvent"; ++ ++static DWORD WINAPI keyed_event_thread( void *arg ) ++{ ++ HANDLE handle; ++ NTSTATUS status; ++ LARGE_INTEGER timeout; ++ OBJECT_ATTRIBUTES attr; ++ UNICODE_STRING str; ++ ULONG_PTR i; ++ ++ attr.Length = sizeof(attr); ++ attr.RootDirectory = 0; ++ attr.ObjectName = &str; ++ attr.Attributes = 0; ++ attr.SecurityDescriptor = NULL; ++ attr.SecurityQualityOfService = NULL; ++ RtlInitUnicodeString( &str, keyed_nameW ); ++ ++ status = pNtOpenKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS, &attr ); ++ ok( !status, "NtOpenKeyedEvent failed %x\n", status ); ++ ++ for (i = 0; i < 20; i++) ++ { ++ if (i & 1) ++ status = pNtWaitForKeyedEvent( handle, (void *)(i * 2), 0, NULL ); ++ else ++ status = pNtReleaseKeyedEvent( handle, (void *)(i * 2), 0, NULL ); ++ ok( status == STATUS_SUCCESS, "%li: failed %x\n", i, status ); ++ Sleep( 20 - i ); ++ } ++ ++ status = pNtReleaseKeyedEvent( handle, (void *)0x1234, 0, NULL ); ++ ok( status == STATUS_SUCCESS, "NtReleaseKeyedEvent %x\n", status ); ++ ++ timeout.QuadPart = -10000; ++ status = pNtWaitForKeyedEvent( handle, (void *)0x5678, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, (void *)0x9abc, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); ++ ++ NtClose( handle ); ++ return 0; ++} ++ ++static void test_keyed_events(void) ++{ ++ OBJECT_ATTRIBUTES attr; ++ UNICODE_STRING str; ++ HANDLE handle, event, thread; ++ NTSTATUS status; ++ LARGE_INTEGER timeout; ++ ULONG_PTR i; ++ ++ if (!pNtCreateKeyedEvent) ++ { ++ win_skip( "Keyed events not supported\n" ); ++ return; ++ } ++ ++ attr.Length = sizeof(attr); ++ attr.RootDirectory = 0; ++ attr.ObjectName = &str; ++ attr.Attributes = 0; ++ attr.SecurityDescriptor = NULL; ++ attr.SecurityQualityOfService = NULL; ++ RtlInitUnicodeString( &str, keyed_nameW ); ++ ++ status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS | SYNCHRONIZE, &attr, 0 ); ++ ok( !status, "NtCreateKeyedEvent failed %x\n", status ); ++ ++ status = WaitForSingleObject( handle, 1000 ); ++ ok( status == 0, "WaitForSingleObject %x\n", status ); ++ ++ timeout.QuadPart = -100000; ++ status = pNtWaitForKeyedEvent( handle, (void *)255, 0, &timeout ); ++ ok( status == STATUS_INVALID_PARAMETER_1, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, (void *)255, 0, &timeout ); ++ ok( status == STATUS_INVALID_PARAMETER_1, "NtReleaseKeyedEvent %x\n", status ); ++ ++ status = pNtWaitForKeyedEvent( handle, (void *)254, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, (void *)254, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); ++ ++ status = pNtWaitForKeyedEvent( handle, NULL, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, NULL, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); ++ ++ status = pNtWaitForKeyedEvent( NULL, (void *)8, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT || broken(status == STATUS_INVALID_HANDLE), /* XP/2003 */ ++ "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( NULL, (void *)8, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT || broken(status == STATUS_INVALID_HANDLE), /* XP/2003 */ ++ "NtReleaseKeyedEvent %x\n", status ); ++ ++ status = pNtWaitForKeyedEvent( (HANDLE)0xdeadbeef, (void *)9, 0, &timeout ); ++ ok( status == STATUS_INVALID_PARAMETER_1, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( (HANDLE)0xdeadbeef, (void *)9, 0, &timeout ); ++ ok( status == STATUS_INVALID_PARAMETER_1, "NtReleaseKeyedEvent %x\n", status ); ++ ++ status = pNtWaitForKeyedEvent( (HANDLE)0xdeadbeef, (void *)8, 0, &timeout ); ++ ok( status == STATUS_INVALID_HANDLE, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( (HANDLE)0xdeadbeef, (void *)8, 0, &timeout ); ++ ok( status == STATUS_INVALID_HANDLE, "NtReleaseKeyedEvent %x\n", status ); ++ ++ thread = CreateThread( NULL, 0, keyed_event_thread, 0, 0, NULL ); ++ for (i = 0; i < 20; i++) ++ { ++ if (i & 1) ++ status = pNtReleaseKeyedEvent( handle, (void *)(i * 2), 0, NULL ); ++ else ++ status = pNtWaitForKeyedEvent( handle, (void *)(i * 2), 0, NULL ); ++ ok( status == STATUS_SUCCESS, "%li: failed %x\n", i, status ); ++ Sleep( i ); ++ } ++ status = pNtWaitForKeyedEvent( handle, (void *)0x1234, 0, &timeout ); ++ ok( status == STATUS_SUCCESS, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtWaitForKeyedEvent( handle, (void *)0x5678, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, (void *)0x9abc, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); ++ ++ ok( WaitForSingleObject( thread, 30000 ) == 0, "wait failed\n" ); ++ ++ NtClose( handle ); ++ ++ /* test access rights */ ++ ++ status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_WAIT, &attr, 0 ); ++ ok( !status, "NtCreateKeyedEvent failed %x\n", status ); ++ status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_ACCESS_DENIED, "NtReleaseKeyedEvent %x\n", status ); ++ NtClose( handle ); ++ ++ status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_WAKE, &attr, 0 ); ++ ok( !status, "NtCreateKeyedEvent failed %x\n", status ); ++ status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_ACCESS_DENIED, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); ++ NtClose( handle ); ++ ++ status = pNtCreateKeyedEvent( &handle, KEYEDEVENT_ALL_ACCESS, &attr, 0 ); ++ ok( !status, "NtCreateKeyedEvent failed %x\n", status ); ++ status = WaitForSingleObject( handle, 1000 ); ++ ok( status == WAIT_FAILED && GetLastError() == ERROR_ACCESS_DENIED, ++ "WaitForSingleObject %x err %u\n", status, GetLastError() ); ++ status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); ++ NtClose( handle ); ++ ++ /* GENERIC_READ gives wait access */ ++ status = pNtCreateKeyedEvent( &handle, GENERIC_READ, &attr, 0 ); ++ ok( !status, "NtCreateKeyedEvent failed %x\n", status ); ++ status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_ACCESS_DENIED, "NtReleaseKeyedEvent %x\n", status ); ++ NtClose( handle ); ++ ++ /* GENERIC_WRITE gives wake access */ ++ status = pNtCreateKeyedEvent( &handle, GENERIC_WRITE, &attr, 0 ); ++ ok( !status, "NtCreateKeyedEvent failed %x\n", status ); ++ status = pNtWaitForKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_ACCESS_DENIED, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( handle, (void *)8, 0, &timeout ); ++ ok( status == STATUS_TIMEOUT, "NtReleaseKeyedEvent %x\n", status ); ++ ++ /* it's not an event */ ++ status = pNtPulseEvent( handle, NULL ); ++ ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtPulseEvent %x\n", status ); ++ ++ status = pNtCreateEvent( &event, GENERIC_ALL, &attr, FALSE, FALSE ); ++ ok( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_TYPE_MISMATCH, ++ "CreateEvent %x\n", status ); ++ ++ NtClose( handle ); ++ ++ status = pNtCreateEvent( &event, GENERIC_ALL, &attr, FALSE, FALSE ); ++ ok( status == 0, "CreateEvent %x\n", status ); ++ status = pNtWaitForKeyedEvent( event, (void *)8, 0, &timeout ); ++ ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtWaitForKeyedEvent %x\n", status ); ++ status = pNtReleaseKeyedEvent( event, (void *)8, 0, &timeout ); ++ ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtReleaseKeyedEvent %x\n", status ); ++ NtClose( event ); ++} ++ ++static DWORD WINAPI mutant_thread( void *arg ) ++{ ++ MUTANT_BASIC_INFORMATION info; ++ NTSTATUS status; ++ HANDLE mutant; ++ DWORD ret; ++ ++ mutant = arg; ++ ret = WaitForSingleObject( mutant, 1000 ); ++ ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); ++ ++ memset(&info, 0xcc, sizeof(info)); ++ status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); ++ ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); ++ ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); ++ ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); ++ ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); ++ /* abandon mutant */ ++ ++ return 0; ++} ++ ++static void test_mutant(void) ++{ ++ MUTANT_BASIC_INFORMATION info; ++ OBJECT_ATTRIBUTES attr; ++ UNICODE_STRING str; ++ NTSTATUS status; ++ HANDLE mutant; ++ HANDLE thread; ++ DWORD ret; ++ ULONG len; ++ LONG prev; ++ ++ pRtlInitUnicodeString(&str, L"\\BaseNamedObjects\\test_mutant"); ++ InitializeObjectAttributes(&attr, &str, 0, 0, NULL); ++ status = pNtCreateMutant(&mutant, GENERIC_ALL, &attr, TRUE); ++ ok( status == STATUS_SUCCESS, "Failed to create Mutant(%08x)\n", status ); ++ ++ /* bogus */ ++ status = pNtQueryMutant(mutant, MutantBasicInformation, &info, 0, NULL); ++ ok( status == STATUS_INFO_LENGTH_MISMATCH, ++ "Failed to NtQueryMutant, expected STATUS_INFO_LENGTH_MISMATCH, got %08x\n", status ); ++ status = pNtQueryMutant(mutant, 0x42, &info, sizeof(info), NULL); ++ ok( status == STATUS_INVALID_INFO_CLASS || broken(status == STATUS_NOT_IMPLEMENTED), /* 32-bit on Vista/2k8 */ ++ "Failed to NtQueryMutant, expected STATUS_INVALID_INFO_CLASS, got %08x\n", status ); ++ status = pNtQueryMutant((HANDLE)0xdeadbeef, MutantBasicInformation, &info, sizeof(info), NULL); ++ ok( status == STATUS_INVALID_HANDLE, ++ "Failed to NtQueryMutant, expected STATUS_INVALID_HANDLE, got %08x\n", status ); ++ ++ /* new */ ++ len = -1; ++ memset(&info, 0xcc, sizeof(info)); ++ status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), &len); ++ ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); ++ ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); ++ ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); ++ ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); ++ ok( len == sizeof(info), "got %u\n", len ); ++ ++ ret = WaitForSingleObject( mutant, 1000 ); ++ ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); ++ ++ memset(&info, 0xcc, sizeof(info)); ++ status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); ++ ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); ++ ok( info.CurrentCount == -1, "expected -1, got %d\n", info.CurrentCount ); ++ ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); ++ ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); ++ ++ prev = 0xdeadbeef; ++ status = pNtReleaseMutant(mutant, &prev); ++ ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status ); ++ ok( prev == -1, "NtQueryRelease failed, expected -1, got %d\n", prev ); ++ ++ prev = 0xdeadbeef; ++ status = pNtReleaseMutant(mutant, &prev); ++ ok( status == STATUS_SUCCESS, "NtQueryRelease failed %08x\n", status ); ++ ok( prev == 0, "NtQueryRelease failed, expected 0, got %d\n", prev ); ++ ++ memset(&info, 0xcc, sizeof(info)); ++ status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); ++ ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); ++ ok( info.CurrentCount == 1, "expected 1, got %d\n", info.CurrentCount ); ++ ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); ++ ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); ++ ++ /* abandoned */ ++ thread = CreateThread( NULL, 0, mutant_thread, mutant, 0, NULL ); ++ ret = WaitForSingleObject( thread, 1000 ); ++ ok( ret == WAIT_OBJECT_0, "WaitForSingleObject failed %08x\n", ret ); ++ CloseHandle( thread ); ++ ++ memset(&info, 0xcc, sizeof(info)); ++ status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); ++ ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); ++ ok( info.CurrentCount == 1, "expected 0, got %d\n", info.CurrentCount ); ++ ok( info.OwnedByCaller == FALSE, "expected FALSE, got %d\n", info.OwnedByCaller ); ++ ok( info.AbandonedState == TRUE, "expected TRUE, got %d\n", info.AbandonedState ); ++ ++ ret = WaitForSingleObject( mutant, 1000 ); ++ ok( ret == WAIT_ABANDONED_0, "WaitForSingleObject failed %08x\n", ret ); ++ ++ memset(&info, 0xcc, sizeof(info)); ++ status = pNtQueryMutant(mutant, MutantBasicInformation, &info, sizeof(info), NULL); ++ ok( status == STATUS_SUCCESS, "NtQueryMutant failed %08x\n", status ); ++ ok( info.CurrentCount == 0, "expected 0, got %d\n", info.CurrentCount ); ++ ok( info.OwnedByCaller == TRUE, "expected TRUE, got %d\n", info.OwnedByCaller ); ++ ok( info.AbandonedState == FALSE, "expected FALSE, got %d\n", info.AbandonedState ); ++ ++ 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 += 100*10000; ++ ticks = GetTickCount(); ++ status = pRtlWaitOnAddress(&address, &compare, 8, &timeout); ++ ticks = GetTickCount() - ticks; ++ ok(status == STATUS_TIMEOUT, "got 0x%08x\n", status); ++ ok(ticks >= 90 && ticks <= 1000, "got %u\n", ticks); ++ ok(address == 0, "got %s\n", wine_dbgstr_longlong(address)); ++ ok(compare == 0, "got %s\n", wine_dbgstr_longlong(compare)); ++ ++ /* different address size */ ++ for (size = 1; size <= 4; size <<= 1) ++ { ++ compare = ~0; ++ compare <<= size * 8; ++ ++ timeout.QuadPart = -100 * 10000; ++ ticks = GetTickCount(); ++ status = pRtlWaitOnAddress(&address, &compare, size, &timeout); ++ ticks = GetTickCount() - ticks; ++ ok(status == STATUS_TIMEOUT, "got 0x%08x\n", status); ++ ok(ticks >= 90 && ticks <= 1000, "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 %s\n", wine_dbgstr_longlong(address)); ++ pRtlWakeAddressAll(&address); ++ ok(address == 0, "got %s\n", wine_dbgstr_longlong(address)); ++} ++ ++START_TEST(sync) ++{ ++ HMODULE module = GetModuleHandleA("ntdll.dll"); ++ ++ pNtClose = (void *)GetProcAddress(module, "NtClose"); ++ pNtCreateEvent = (void *)GetProcAddress(module, "NtCreateEvent"); ++ pNtCreateKeyedEvent = (void *)GetProcAddress(module, "NtCreateKeyedEvent"); ++ pNtCreateMutant = (void *)GetProcAddress(module, "NtCreateMutant"); ++ pNtOpenEvent = (void *)GetProcAddress(module, "NtOpenEvent"); ++ pNtOpenKeyedEvent = (void *)GetProcAddress(module, "NtOpenKeyedEvent"); ++ pNtPulseEvent = (void *)GetProcAddress(module, "NtPulseEvent"); ++ pNtQueryEvent = (void *)GetProcAddress(module, "NtQueryEvent"); ++ pNtQueryMutant = (void *)GetProcAddress(module, "NtQueryMutant"); ++ pNtQuerySystemTime = (void *)GetProcAddress(module, "NtQuerySystemTime"); ++ pNtReleaseKeyedEvent = (void *)GetProcAddress(module, "NtReleaseKeyedEvent"); ++ pNtReleaseMutant = (void *)GetProcAddress(module, "NtReleaseMutant"); ++ pNtResetEvent = (void *)GetProcAddress(module, "NtResetEvent"); ++ pNtSetEvent = (void *)GetProcAddress(module, "NtSetEvent"); ++ pNtWaitForKeyedEvent = (void *)GetProcAddress(module, "NtWaitForKeyedEvent"); ++ pRtlInitUnicodeString = (void *)GetProcAddress(module, "RtlInitUnicodeString"); ++ pRtlWaitOnAddress = (void *)GetProcAddress(module, "RtlWaitOnAddress"); ++ pRtlWakeAddressAll = (void *)GetProcAddress(module, "RtlWakeAddressAll"); ++ pRtlWakeAddressSingle = (void *)GetProcAddress(module, "RtlWakeAddressSingle"); ++ ++ test_wait_on_address(); ++ test_event(); ++ test_mutant(); ++ test_keyed_events(); ++} +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0002-ntdll-tests-Add-some-tests-for-Rtl-resources.patch b/patches/ntdll-NtAlertThreadByThreadId/0002-ntdll-tests-Add-some-tests-for-Rtl-resources.patch new file mode 100644 index 00000000..c8c8ada7 --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0002-ntdll-tests-Add-some-tests-for-Rtl-resources.patch @@ -0,0 +1,241 @@ +From 2107fb9f59bb2beef3b8c12a2adf9cb2be469036 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Thu, 9 Jan 2020 14:51:05 -0600 +Subject: [PATCH 02/13] ntdll/tests: Add some tests for Rtl* resources. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/tests/sync.c | 202 ++++++++++++++++++++++++++++++++++++---- + 1 file changed, 183 insertions(+), 19 deletions(-) + +diff --git a/dlls/ntdll/tests/sync.c b/dlls/ntdll/tests/sync.c +index 9967a2be257..152cdd1530d 100644 +--- a/dlls/ntdll/tests/sync.c ++++ b/dlls/ntdll/tests/sync.c +@@ -41,7 +41,12 @@ static NTSTATUS (WINAPI *pNtReleaseMutant)( HANDLE, LONG * ); + static NTSTATUS (WINAPI *pNtResetEvent)( HANDLE, LONG * ); + static NTSTATUS (WINAPI *pNtSetEvent)( HANDLE, LONG * ); + 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 ); ++static void (WINAPI *pRtlDeleteResource)( RTL_RWLOCK * ); ++static void (WINAPI *pRtlInitializeResource)( RTL_RWLOCK * ); + static void (WINAPI *pRtlInitUnicodeString)( UNICODE_STRING *, const WCHAR * ); ++static void (WINAPI *pRtlReleaseResource)( RTL_RWLOCK * ); + 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 * ); +@@ -496,32 +501,191 @@ static void test_wait_on_address(void) + ok(address == 0, "got %s\n", wine_dbgstr_longlong(address)); + } + ++HANDLE thread_ready, thread_done; ++ ++static DWORD WINAPI resource_shared_thread(void *arg) ++{ ++ RTL_RWLOCK *resource = arg; ++ BOOLEAN ret; ++ ++ ret = pRtlAcquireResourceShared(resource, TRUE); ++ ok(ret == TRUE, "got %u\n", ret); ++ ++ SetEvent(thread_ready); ++ ok(!WaitForSingleObject(thread_done, 1000), "wait failed\n"); ++ pRtlReleaseResource(resource); ++ return 0; ++} ++ ++static DWORD WINAPI resource_exclusive_thread(void *arg) ++{ ++ RTL_RWLOCK *resource = arg; ++ BOOLEAN ret; ++ ++ ret = pRtlAcquireResourceExclusive(resource, TRUE); ++ ok(ret == TRUE, "got %u\n", ret); ++ ++ SetEvent(thread_ready); ++ ok(!WaitForSingleObject(thread_done, 1000), "wait failed\n"); ++ pRtlReleaseResource(resource); ++ return 0; ++} ++ ++static void test_resource(void) ++{ ++ HANDLE thread, thread2; ++ RTL_RWLOCK resource; ++ BOOLEAN ret; ++ ++ pRtlInitializeResource(&resource); ++ thread_ready = CreateEventA(NULL, FALSE, FALSE, NULL); ++ thread_done = CreateEventA(NULL, FALSE, FALSE, NULL); ++ ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == FALSE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ pRtlReleaseResource(&resource); ++ ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ pRtlReleaseResource(&resource); ++ pRtlReleaseResource(&resource); ++ ++ /* Do not acquire the resource ourselves, but spawn a shared thread holding it. */ ++ ++ thread = CreateThread(NULL, 0, resource_shared_thread, &resource, 0, NULL); ++ ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); ++ ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == FALSE, "got %u\n", ret); ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ ++ SetEvent(thread_done); ++ ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); ++ CloseHandle(thread); ++ ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ ++ /* Acquire the resource as exclusive, and then spawn a shared thread. */ ++ ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ thread = CreateThread(NULL, 0, resource_shared_thread, &resource, 0, NULL); ++ ok(WaitForSingleObject(thread_ready, 100) == WAIT_TIMEOUT, "expected timeout\n"); ++ ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ ++ pRtlReleaseResource(&resource); ++ ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); ++ SetEvent(thread_done); ++ ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); ++ CloseHandle(thread); ++ ++ /* Acquire the resource as shared, and then spawn an exclusive thread. */ ++ ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ thread = CreateThread(NULL, 0, resource_exclusive_thread, &resource, 0, NULL); ++ ok(WaitForSingleObject(thread_ready, 100) == WAIT_TIMEOUT, "expected timeout\n"); ++ ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == FALSE, "got %u\n", ret); ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ ++ pRtlReleaseResource(&resource); ++ ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); ++ SetEvent(thread_done); ++ ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); ++ CloseHandle(thread); ++ ++ /* Spawn a shared and then exclusive waiter. */ ++ thread = CreateThread(NULL, 0, resource_shared_thread, &resource, 0, NULL); ++ ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); ++ thread2 = CreateThread(NULL, 0, resource_exclusive_thread, &resource, 0, NULL); ++ ok(WaitForSingleObject(thread_ready, 100) == WAIT_TIMEOUT, "expected timeout\n"); ++ ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == FALSE, "got %u\n", ret); ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ ++ SetEvent(thread_done); ++ ok(!WaitForSingleObject(thread, 1000), "wait failed\n"); ++ CloseHandle(thread); ++ ++ ok(!WaitForSingleObject(thread_ready, 1000), "wait failed\n"); ++ SetEvent(thread_done); ++ ok(!WaitForSingleObject(thread2, 1000), "wait failed\n"); ++ CloseHandle(thread2); ++ ++ ret = pRtlAcquireResourceExclusive(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ ret = pRtlAcquireResourceShared(&resource, FALSE); ++ ok(ret == TRUE, "got %u\n", ret); ++ pRtlReleaseResource(&resource); ++ ++ CloseHandle(thread_ready); ++ CloseHandle(thread_done); ++ pRtlDeleteResource(&resource); ++} ++ + START_TEST(sync) + { + HMODULE module = GetModuleHandleA("ntdll.dll"); + +- pNtClose = (void *)GetProcAddress(module, "NtClose"); +- pNtCreateEvent = (void *)GetProcAddress(module, "NtCreateEvent"); +- pNtCreateKeyedEvent = (void *)GetProcAddress(module, "NtCreateKeyedEvent"); +- pNtCreateMutant = (void *)GetProcAddress(module, "NtCreateMutant"); +- pNtOpenEvent = (void *)GetProcAddress(module, "NtOpenEvent"); +- pNtOpenKeyedEvent = (void *)GetProcAddress(module, "NtOpenKeyedEvent"); +- pNtPulseEvent = (void *)GetProcAddress(module, "NtPulseEvent"); +- pNtQueryEvent = (void *)GetProcAddress(module, "NtQueryEvent"); +- pNtQueryMutant = (void *)GetProcAddress(module, "NtQueryMutant"); +- pNtQuerySystemTime = (void *)GetProcAddress(module, "NtQuerySystemTime"); +- pNtReleaseKeyedEvent = (void *)GetProcAddress(module, "NtReleaseKeyedEvent"); +- pNtReleaseMutant = (void *)GetProcAddress(module, "NtReleaseMutant"); +- pNtResetEvent = (void *)GetProcAddress(module, "NtResetEvent"); +- pNtSetEvent = (void *)GetProcAddress(module, "NtSetEvent"); +- pNtWaitForKeyedEvent = (void *)GetProcAddress(module, "NtWaitForKeyedEvent"); +- pRtlInitUnicodeString = (void *)GetProcAddress(module, "RtlInitUnicodeString"); +- pRtlWaitOnAddress = (void *)GetProcAddress(module, "RtlWaitOnAddress"); +- pRtlWakeAddressAll = (void *)GetProcAddress(module, "RtlWakeAddressAll"); +- pRtlWakeAddressSingle = (void *)GetProcAddress(module, "RtlWakeAddressSingle"); ++ pNtClose = (void *)GetProcAddress(module, "NtClose"); ++ pNtCreateEvent = (void *)GetProcAddress(module, "NtCreateEvent"); ++ pNtCreateKeyedEvent = (void *)GetProcAddress(module, "NtCreateKeyedEvent"); ++ pNtCreateMutant = (void *)GetProcAddress(module, "NtCreateMutant"); ++ pNtOpenEvent = (void *)GetProcAddress(module, "NtOpenEvent"); ++ pNtOpenKeyedEvent = (void *)GetProcAddress(module, "NtOpenKeyedEvent"); ++ pNtPulseEvent = (void *)GetProcAddress(module, "NtPulseEvent"); ++ pNtQueryEvent = (void *)GetProcAddress(module, "NtQueryEvent"); ++ pNtQueryMutant = (void *)GetProcAddress(module, "NtQueryMutant"); ++ pNtQuerySystemTime = (void *)GetProcAddress(module, "NtQuerySystemTime"); ++ pNtReleaseKeyedEvent = (void *)GetProcAddress(module, "NtReleaseKeyedEvent"); ++ pNtReleaseMutant = (void *)GetProcAddress(module, "NtReleaseMutant"); ++ pNtResetEvent = (void *)GetProcAddress(module, "NtResetEvent"); ++ pNtSetEvent = (void *)GetProcAddress(module, "NtSetEvent"); ++ pNtWaitForKeyedEvent = (void *)GetProcAddress(module, "NtWaitForKeyedEvent"); ++ pRtlAcquireResourceExclusive = (void *)GetProcAddress(module, "RtlAcquireResourceExclusive"); ++ pRtlAcquireResourceShared = (void *)GetProcAddress(module, "RtlAcquireResourceShared"); ++ pRtlDeleteResource = (void *)GetProcAddress(module, "RtlDeleteResource"); ++ pRtlInitializeResource = (void *)GetProcAddress(module, "RtlInitializeResource"); ++ pRtlInitUnicodeString = (void *)GetProcAddress(module, "RtlInitUnicodeString"); ++ pRtlReleaseResource = (void *)GetProcAddress(module, "RtlReleaseResource"); ++ pRtlWaitOnAddress = (void *)GetProcAddress(module, "RtlWaitOnAddress"); ++ pRtlWakeAddressAll = (void *)GetProcAddress(module, "RtlWakeAddressAll"); ++ pRtlWakeAddressSingle = (void *)GetProcAddress(module, "RtlWakeAddressSingle"); + + test_wait_on_address(); + test_event(); + test_mutant(); + test_keyed_events(); ++ test_resource(); + } +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0003-ntdll-Use-a-separate-mutex-to-lock-the-TEB-list.patch b/patches/ntdll-NtAlertThreadByThreadId/0003-ntdll-Use-a-separate-mutex-to-lock-the-TEB-list.patch new file mode 100644 index 00000000..e3985924 --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0003-ntdll-Use-a-separate-mutex-to-lock-the-TEB-list.patch @@ -0,0 +1,81 @@ +From 0fdd0e4d59af06ed8b225e87802001ab53cb4906 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 31 Aug 2020 22:56:03 -0500 +Subject: [PATCH 03/13] ntdll: Use a separate mutex to lock the TEB list. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/unix/virtual.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 785583a340a..2e5e94f80f8 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -163,6 +163,7 @@ static void *teb_block; + static void **next_free_teb; + static int teb_block_pos; + static struct list teb_list = LIST_INIT( teb_list ); ++static pthread_rwlock_t teb_list_lock = PTHREAD_RWLOCK_INITIALIZER; + + #define ROUND_ADDR(addr,mask) ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask))) + #define ROUND_SIZE(addr,size) (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask) +@@ -2556,7 +2557,9 @@ static void init_teb( TEB *teb, PEB *peb ) + thread_data->reply_fd = -1; + thread_data->wait_fd[0] = -1; + thread_data->wait_fd[1] = -1; ++ pthread_rwlock_wrlock( &teb_list_lock ); + list_add_head( &teb_list, &thread_data->entry ); ++ pthread_rwlock_unlock( &teb_list_lock ); + } + + +@@ -2678,7 +2681,9 @@ void virtual_free_teb( TEB *teb ) + } + + server_enter_uninterrupted_section( &virtual_mutex, &sigset ); ++ pthread_rwlock_wrlock( &teb_list_lock ); + list_remove( &thread_data->entry ); ++ pthread_rwlock_unlock( &teb_list_lock ); + ptr = (char *)teb - teb_offset; + *(void **)ptr = next_free_teb; + next_free_teb = ptr; +@@ -2692,17 +2697,16 @@ void virtual_free_teb( TEB *teb ) + NTSTATUS virtual_clear_tls_index( ULONG index ) + { + struct ntdll_thread_data *thread_data; +- sigset_t sigset; + + if (index < TLS_MINIMUM_AVAILABLE) + { +- server_enter_uninterrupted_section( &virtual_mutex, &sigset ); ++ pthread_rwlock_rdlock( &teb_list_lock ); + LIST_FOR_EACH_ENTRY( thread_data, &teb_list, struct ntdll_thread_data, entry ) + { + TEB *teb = CONTAINING_RECORD( thread_data, TEB, GdiTebBatch ); + teb->TlsSlots[index] = 0; + } +- server_leave_uninterrupted_section( &virtual_mutex, &sigset ); ++ pthread_rwlock_unlock( &teb_list_lock ); + } + else + { +@@ -2710,13 +2714,13 @@ NTSTATUS virtual_clear_tls_index( ULONG index ) + if (index >= 8 * sizeof(NtCurrentTeb()->Peb->TlsExpansionBitmapBits)) + return STATUS_INVALID_PARAMETER; + +- server_enter_uninterrupted_section( &virtual_mutex, &sigset ); ++ pthread_rwlock_rdlock( &teb_list_lock ); + LIST_FOR_EACH_ENTRY( thread_data, &teb_list, struct ntdll_thread_data, entry ) + { + TEB *teb = CONTAINING_RECORD( thread_data, TEB, GdiTebBatch ); + if (teb->TlsExpansionSlots) teb->TlsExpansionSlots[index] = 0; + } +- server_leave_uninterrupted_section( &virtual_mutex, &sigset ); ++ pthread_rwlock_unlock( &teb_list_lock ); + } + return STATUS_SUCCESS; + } +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0004-ntdll-Implement-NtAlertThreadByThreadId-and-NtWaitFo.patch b/patches/ntdll-NtAlertThreadByThreadId/0004-ntdll-Implement-NtAlertThreadByThreadId-and-NtWaitFo.patch new file mode 100644 index 00000000..5f0f29cb --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0004-ntdll-Implement-NtAlertThreadByThreadId-and-NtWaitFo.patch @@ -0,0 +1,172 @@ +From 3606a80ff21447e459950899b409416873722031 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 31 Aug 2020 23:00:16 -0500 +Subject: [PATCH 04/13] ntdll: Implement NtAlertThreadByThreadId() and + NtWaitForAlertByThreadId(). + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/ntdll.spec | 2 ++ + dlls/ntdll/unix/sync.c | 45 ++++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/thread.c | 2 ++ + dlls/ntdll/unix/unix_private.h | 3 +++ + dlls/ntdll/unix/virtual.c | 5 ++-- + include/winternl.h | 2 ++ + 6 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec +index f5ac924a945..c63b7a3e5fe 100644 +--- a/dlls/ntdll/ntdll.spec ++++ b/dlls/ntdll/ntdll.spec +@@ -137,6 +137,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) +@@ -422,6 +423,7 @@ + @ stdcall -syscall NtUnmapViewOfSection(long ptr) + @ stub NtVdmControl + @ stub NtW32Call ++@ stdcall -syscall NtWaitForAlertByThreadId(ptr ptr) + # @ stub NtWaitForDebugEvent + @ stdcall -syscall NtWaitForKeyedEvent(long ptr long ptr) + @ stdcall -syscall NtWaitForMultipleObjects(long ptr long long ptr) +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index dcc8c5447ef..2ed164368b9 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -81,6 +81,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) + { +@@ -2119,6 +2125,45 @@ NTSTATUS WINAPI NtQueryInformationAtom( RTL_ATOM atom, ATOM_INFORMATION_CLASS cl + } + + ++/*********************************************************************** ++ * NtAlertThreadByThreadId (NTDLL.@) ++ */ ++NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid ) ++{ ++ struct ntdll_thread_data *thread_data; ++ ++ TRACE( "%p\n", tid ); ++ ++ pthread_rwlock_rdlock( &teb_list_lock ); ++ ++ LIST_FOR_EACH_ENTRY( thread_data, &teb_list, struct ntdll_thread_data, entry ) ++ { ++ TEB *teb = CONTAINING_RECORD( thread_data, TEB, GdiTebBatch ); ++ ++ if (teb->ClientId.UniqueThread == tid) ++ { ++ pthread_rwlock_unlock( &teb_list_lock ); ++ NtSetEvent( thread_data->tid_alert_event, NULL ); ++ return STATUS_SUCCESS; ++ } ++ } ++ ++ pthread_rwlock_unlock( &teb_list_lock ); ++ return STATUS_INVALID_CID; ++} ++ ++ ++/*********************************************************************** ++ * NtWaitForAlertByThreadId (NTDLL.@) ++ */ ++NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEGER *timeout ) ++{ ++ TRACE( "%p %s\n", address, debugstr_timeout( timeout ) ); ++ ++ return NtWaitForSingleObject( ntdll_get_thread_data()->tid_alert_event, FALSE, timeout ); ++} ++ ++ + #ifdef __linux__ + + NTSTATUS CDECL fast_RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit, int timeout ) +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 9808fc610e3..bb55b3d29ed 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -252,6 +252,8 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT + thread_data->start = start; + thread_data->param = param; + ++ NtCreateEvent( &thread_data->tid_alert_event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE ); ++ + pthread_attr_init( &pthread_attr ); + pthread_attr_setstack( &pthread_attr, teb->DeallocationStack, + (char *)teb->Tib.StackBase + extra_stack - (char *)teb->DeallocationStack ); +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 23731f0fdc6..9ae7cba34a7 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -57,6 +57,7 @@ struct ntdll_thread_data + struct list entry; /* entry in TEB list */ + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ ++ HANDLE tid_alert_event; /* event for thread-id alerts */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +@@ -113,6 +114,8 @@ extern NTSTATUS CDECL unwind_builtin_dll( ULONG type, struct _DISPATCHER_CONTEXT + + extern void CDECL set_show_dot_files( BOOL enable ) DECLSPEC_HIDDEN; + ++extern struct list teb_list DECLSPEC_HIDDEN; ++extern pthread_rwlock_t teb_list_lock DECLSPEC_HIDDEN; + extern const char *home_dir DECLSPEC_HIDDEN; + extern const char *data_dir DECLSPEC_HIDDEN; + extern const char *build_dir DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c +index 2e5e94f80f8..96650e064c3 100644 +--- a/dlls/ntdll/unix/virtual.c ++++ b/dlls/ntdll/unix/virtual.c +@@ -162,8 +162,9 @@ struct _KUSER_SHARED_DATA *user_shared_data = (void *)0x7ffe0000; + static void *teb_block; + static void **next_free_teb; + static int teb_block_pos; +-static struct list teb_list = LIST_INIT( teb_list ); +-static pthread_rwlock_t teb_list_lock = PTHREAD_RWLOCK_INITIALIZER; ++ ++struct list teb_list = LIST_INIT( teb_list ); ++pthread_rwlock_t teb_list_lock = PTHREAD_RWLOCK_INITIALIZER; + + #define ROUND_ADDR(addr,mask) ((void *)((UINT_PTR)(addr) & ~(UINT_PTR)(mask))) + #define ROUND_SIZE(addr,size) (((SIZE_T)(size) + ((UINT_PTR)(addr) & page_mask) + page_mask) & ~page_mask) +diff --git a/include/winternl.h b/include/winternl.h +index a7b0e04aade..b155b4e39b2 100644 +--- a/include/winternl.h ++++ b/include/winternl.h +@@ -3040,6 +3040,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); +@@ -3272,6 +3273,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 NtWaitForKeyedEvent(HANDLE,const void*,BOOLEAN,const LARGE_INTEGER*); + NTSYSAPI NTSTATUS WINAPI NtWaitForSingleObject(HANDLE,BOOLEAN,const LARGE_INTEGER*); + NTSYSAPI NTSTATUS WINAPI NtWaitForMultipleObjects(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*); +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0005-ntdll-tests-Add-basic-tests-for-thread-id-alert-func.patch b/patches/ntdll-NtAlertThreadByThreadId/0005-ntdll-tests-Add-basic-tests-for-thread-id-alert-func.patch new file mode 100644 index 00000000..fb4b0ad7 --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0005-ntdll-tests-Add-basic-tests-for-thread-id-alert-func.patch @@ -0,0 +1,114 @@ +From 03fe17e93090c3cc3b9cbc9f92bc48ff39d84a07 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 31 Aug 2020 23:01:25 -0500 +Subject: [PATCH 05/13] ntdll/tests: Add basic tests for thread-id alert + functions. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/tests/sync.c | 59 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 59 insertions(+) + +diff --git a/dlls/ntdll/tests/sync.c b/dlls/ntdll/tests/sync.c +index 152cdd1530d..a6631dd40a9 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)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, BOOLEAN, BOOLEAN ); + static NTSTATUS (WINAPI *pNtCreateKeyedEvent)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, ULONG ); +@@ -40,6 +41,7 @@ static NTSTATUS (WINAPI *pNtReleaseKeyedEvent)( HANDLE, const void *, BOOLEAN, c + static NTSTATUS (WINAPI *pNtReleaseMutant)( HANDLE, LONG * ); + 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 ); +@@ -654,10 +656,65 @@ static void test_resource(void) + pRtlDeleteResource(&resource); + } + ++static void test_thread_id_alert( char **argv ) ++{ ++ static const LARGE_INTEGER zero; ++ char cmdline[MAX_PATH]; ++ STARTUPINFOA si = {0}; ++ PROCESS_INFORMATION pi; ++ NTSTATUS ret; ++ ++ if (!pNtWaitForAlertByThreadId) ++ { ++ win_skip("NtWaitForAlertByThreadId is not available\n"); ++ return; ++ } ++ ++ ret = pNtWaitForAlertByThreadId( (void *)0x123, &zero ); ++ 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, &zero ); ++ ok(ret == STATUS_ALERTED, "got %#x\n", ret); ++ ++ ret = pNtWaitForAlertByThreadId( (void *)0x123, &zero ); ++ ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); ++ ++ ret = pNtWaitForAlertByThreadId( (void *)0x321, &zero ); ++ ok(ret == STATUS_TIMEOUT, "got %#x\n", ret); ++ ++ 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"); +@@ -672,6 +729,7 @@ START_TEST(sync) + pNtReleaseMutant = (void *)GetProcAddress(module, "NtReleaseMutant"); + 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"); +@@ -688,4 +746,5 @@ START_TEST(sync) + test_mutant(); + test_keyed_events(); + test_resource(); ++ test_thread_id_alert( argv ); + } +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0006-ntdll-Implement-thread-id-alerts-on-top-of-futexes-i.patch b/patches/ntdll-NtAlertThreadByThreadId/0006-ntdll-Implement-thread-id-alerts-on-top-of-futexes-i.patch new file mode 100644 index 00000000..1a14e1ff --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0006-ntdll-Implement-thread-id-alerts-on-top-of-futexes-i.patch @@ -0,0 +1,119 @@ +From c6423d645d002113fce7b3194023ec507549c39a Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 31 Aug 2020 23:02:56 -0500 +Subject: [PATCH 06/13] ntdll: Implement thread-id alerts on top of futexes if + possible. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/unix/sync.c | 65 ++++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/unix_private.h | 3 ++ + 2 files changed, 68 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 2ed164368b9..3fd3545bbbb 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -2143,6 +2143,15 @@ NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid ) + if (teb->ClientId.UniqueThread == tid) + { + pthread_rwlock_unlock( &teb_list_lock ); ++#ifdef __linux__ ++ if (use_futexes()) ++ { ++ int *futex = &thread_data->tid_alert_futex; ++ if (!InterlockedExchange( futex, 1 )) ++ futex_wake( futex, 1 ); ++ return STATUS_SUCCESS; ++ } ++#endif + NtSetEvent( thread_data->tid_alert_event, NULL ); + return STATUS_SUCCESS; + } +@@ -2153,6 +2162,28 @@ NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid ) + } + + ++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; ++} ++ ++ + /*********************************************************************** + * NtWaitForAlertByThreadId (NTDLL.@) + */ +@@ -2160,6 +2191,40 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG + { + TRACE( "%p %s\n", address, debugstr_timeout( timeout ) ); + ++#ifdef __linux__ ++ if (use_futexes()) ++ { ++ int *futex = &ntdll_get_thread_data()->tid_alert_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 + return NtWaitForSingleObject( ntdll_get_thread_data()->tid_alert_event, FALSE, timeout ); + } + +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9ae7cba34a7..327e12519a0 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -57,6 +57,9 @@ struct ntdll_thread_data + struct list entry; /* entry in TEB list */ + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ ++#ifdef __linux__ ++ int tid_alert_futex; /* futex for thread-id alerts */ ++#endif + HANDLE tid_alert_event; /* event for thread-id alerts */ + }; + +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0007-ntdll-Implement-thread-id-alerts-on-top-of-Mach-sema.patch b/patches/ntdll-NtAlertThreadByThreadId/0007-ntdll-Implement-thread-id-alerts-on-top-of-Mach-sema.patch new file mode 100644 index 00000000..32ae9a9e --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0007-ntdll-Implement-thread-id-alerts-on-top-of-Mach-sema.patch @@ -0,0 +1,127 @@ +From b281eaa388b6dc0c0c539cc76236a0529668fcff Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 31 Aug 2020 23:03:34 -0500 +Subject: [PATCH 07/13] ntdll: Implement thread-id alerts on top of Mach + semaphores on Mac. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/unix/sync.c | 44 ++++++++++++++++++++++++++++++++++ + dlls/ntdll/unix/thread.c | 4 ++++ + dlls/ntdll/unix/unix_private.h | 4 ++++ + 3 files changed, 52 insertions(+) + +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 3fd3545bbbb..e4aa03f94da 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -2143,6 +2143,10 @@ NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid ) + if (teb->ClientId.UniqueThread == tid) + { + pthread_rwlock_unlock( &teb_list_lock ); ++#ifdef __APPLE__ ++ semaphore_signal( &thread_data->tid_alert_sem ); ++ return STATUS_SUCCESS; ++#else + #ifdef __linux__ + if (use_futexes()) + { +@@ -2154,6 +2158,7 @@ NTSTATUS WINAPI NtAlertThreadByThreadId( HANDLE tid ) + #endif + NtSetEvent( thread_data->tid_alert_event, NULL ); + return STATUS_SUCCESS; ++#endif + } + } + +@@ -2191,6 +2196,44 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG + { + TRACE( "%p %s\n", address, debugstr_timeout( timeout ) ); + ++#ifdef __APPLE__ ++ { ++ semaphore_t sem = ntdll_get_thread_data()->tid_alert_sem; ++ ULONGLONG end; ++ kern_return_t ret; ++ ++ 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, ×pec ); ++ } ++ 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 + #ifdef __linux__ + if (use_futexes()) + { +@@ -2226,6 +2269,7 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG + } + #endif + return NtWaitForSingleObject( ntdll_get_thread_data()->tid_alert_event, FALSE, timeout ); ++#endif + } + + +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index bb55b3d29ed..05157e24ace 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -252,7 +252,11 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT + thread_data->start = start; + thread_data->param = param; + ++#ifdef __APPLE__ ++ semaphore_create( mach_task_self(), &thread_data->tid_alert_sem, SYNC_POLICY_FIFO, 0 ); ++#else + NtCreateEvent( &thread_data->tid_alert_event, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE ); ++#endif + + pthread_attr_init( &pthread_attr ); + pthread_attr_setstack( &pthread_attr, teb->DeallocationStack, +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 327e12519a0..9e94c0ec13c 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -57,10 +57,14 @@ struct ntdll_thread_data + struct list entry; /* entry in TEB list */ + PRTL_THREAD_START_ROUTINE start; /* thread entry point */ + void *param; /* thread entry point parameter */ ++#ifdef __APPLE__ ++ semaphore_t tid_alert_sem; /* Mach semaphore for thread-id alerts */ ++#else + #ifdef __linux__ + int tid_alert_futex; /* futex for thread-id alerts */ + #endif + HANDLE tid_alert_event; /* event for thread-id alerts */ ++#endif + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0008-ntdll-Use-a-separate-lock-to-access-the-PE-TEB-list.patch b/patches/ntdll-NtAlertThreadByThreadId/0008-ntdll-Use-a-separate-lock-to-access-the-PE-TEB-list.patch new file mode 100644 index 00000000..69ab690f --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0008-ntdll-Use-a-separate-lock-to-access-the-PE-TEB-list.patch @@ -0,0 +1,156 @@ +From a9f438755a74e85cfb3b9dd122058c722ffa2f3c Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Wed, 28 Oct 2020 17:31:00 -0500 +Subject: [PATCH 08/13] ntdll: Use a separate lock to access the PE TEB list. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/loader.c | 70 +++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 64 insertions(+), 6 deletions(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 45ad496fd64..26443a8c4f4 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -127,7 +127,6 @@ typedef struct _wine_modref + + static UINT tls_module_count; /* number of modules with TLS directory */ + static IMAGE_TLS_DIRECTORY *tls_dirs; /* array of TLS directories */ +-LIST_ENTRY tls_links = { &tls_links, &tls_links }; + + static RTL_CRITICAL_SECTION loader_section; + static RTL_CRITICAL_SECTION_DEBUG critsect_debug = +@@ -184,6 +183,55 @@ static inline BOOL contains_path( LPCWSTR name ) + return ((*name && (name[1] == ':')) || wcschr(name, '/') || wcschr(name, '\\')); + } + ++/* We need to access this lock from inside the implementation of (process-local) ++ * synchronization functions, which makes actually scheduling more than a little ++ * difficult. ++ * ++ * On the other hand, we want to make this a read/write lock, not just to avoid ++ * lock contention, but also because lock contention is particularly expensive ++ * for a spinlock. ++ */ ++LIST_ENTRY tls_links = { &tls_links, &tls_links }; ++static LONG teb_spinlock; ++ ++#define TEB_SPINLOCK_WRITE (-1) ++ ++static inline void small_pause(void) ++{ ++#ifdef __i386__ ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++static void teb_list_rdlock(void) ++{ ++ for (;;) ++ { ++ LONG old = teb_spinlock; ++ if (old != TEB_SPINLOCK_WRITE && InterlockedCompareExchange( &teb_spinlock, old + 1, old ) == old) ++ return; ++ small_pause(); ++ } ++} ++ ++static void teb_list_rdunlock(void) ++{ ++ InterlockedDecrement( &teb_spinlock ); ++} ++ ++static void teb_list_wrlock(void) ++{ ++ while (InterlockedCompareExchange( &teb_spinlock, TEB_SPINLOCK_WRITE, 0 )) ++ small_pause(); ++} ++ ++static void teb_list_wrunlock(void) ++{ ++ InterlockedExchange( &teb_spinlock, 0 ); ++} ++ + #define RTL_UNLOAD_EVENT_TRACE_NUMBER 64 + + typedef struct _RTL_UNLOAD_EVENT_TRACE +@@ -991,13 +1039,18 @@ static SHORT alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) + if (!new_ptr) return -1; + + /* resize the pointer block in all running threads */ ++ teb_list_rdlock(); + for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) + { + TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); + void **old = teb->ThreadLocalStoragePointer; + void **new = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*new)); + +- if (!new) return -1; ++ if (!new) ++ { ++ teb_list_rdunlock(); ++ return -1; ++ } + if (old) memcpy( new, old, tls_module_count * sizeof(*new) ); + teb->ThreadLocalStoragePointer = new; + #ifdef __x86_64__ /* macOS-specific hack */ +@@ -1006,12 +1059,14 @@ static SHORT alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) + TRACE( "thread %04lx tls block %p -> %p\n", (ULONG_PTR)teb->ClientId.UniqueThread, old, new ); + /* FIXME: can't free old block here, should be freed at thread exit */ + } ++ teb_list_rdunlock(); + + tls_dirs = new_ptr; + tls_module_count = new_count; + } + + /* allocate the data block in all running threads */ ++ teb_list_rdlock(); + for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) + { + TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); +@@ -1026,6 +1081,7 @@ static SHORT alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) + RtlFreeHeap( GetProcessHeap(), 0, + InterlockedExchangePointer( (void **)teb->ThreadLocalStoragePointer + i, new_ptr )); + } ++ teb_list_rdunlock(); + + *(DWORD *)dir->AddressOfIndex = i; + tls_dirs[i] = *dir; +@@ -3243,7 +3299,9 @@ void WINAPI LdrShutdownThread(void) + if (wm->ldr.TlsIndex != -1) call_tls_callbacks( wm->ldr.DllBase, DLL_THREAD_DETACH ); + + RtlAcquirePebLock(); ++ teb_list_wrlock(); + RemoveEntryList( &NtCurrentTeb()->TlsLinks ); ++ teb_list_wrunlock(); + if ((pointers = NtCurrentTeb()->ThreadLocalStoragePointer)) + { + for (i = 0; i < tls_module_count; i++) RtlFreeHeap( GetProcessHeap(), 0, pointers[i] ); +@@ -3480,6 +3538,10 @@ void WINAPI LdrInitializeThunk( CONTEXT *context, ULONG_PTR unknown2, ULONG_PTR + + if (process_detaching) NtTerminateThread( GetCurrentThread(), 0 ); + ++ teb_list_wrlock(); ++ InsertHeadList( &tls_links, &NtCurrentTeb()->TlsLinks ); ++ teb_list_wrunlock(); ++ + RtlEnterCriticalSection( &loader_section ); + + wm = get_modref( NtCurrentTeb()->Peb->ImageBaseAddress ); +@@ -3502,10 +3564,6 @@ void WINAPI LdrInitializeThunk( CONTEXT *context, ULONG_PTR unknown2, ULONG_PTR + imports_fixup_done = TRUE; + } + +- RtlAcquirePebLock(); +- InsertHeadList( &tls_links, &NtCurrentTeb()->TlsLinks ); +- RtlReleasePebLock(); +- + NtCurrentTeb()->FlsSlots = fls_alloc_data(); + + if (!attach_done) /* first time around */ +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0009-ntdll-Reimplement-Win32-futexes-on-top-of-thread-ID-.patch b/patches/ntdll-NtAlertThreadByThreadId/0009-ntdll-Reimplement-Win32-futexes-on-top-of-thread-ID-.patch new file mode 100644 index 00000000..d42b8116 --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0009-ntdll-Reimplement-Win32-futexes-on-top-of-thread-ID-.patch @@ -0,0 +1,397 @@ +From e27be2eef2738f6c7f213d70ee5554b052d07ec1 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 2 Nov 2020 20:24:07 -0600 +Subject: [PATCH 09/13] ntdll: Reimplement Win32 futexes on top of thread-ID + alerts. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/loader.c | 4 +- + dlls/ntdll/ntdll_misc.h | 2 + + dlls/ntdll/sync.c | 77 ++++++++++++++++++- + dlls/ntdll/unix/loader.c | 3 - + dlls/ntdll/unix/sync.c | 162 --------------------------------------- + dlls/ntdll/unixlib.h | 6 +- + 6 files changed, 79 insertions(+), 175 deletions(-) + +diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c +index 26443a8c4f4..440a77d4063 100644 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -205,7 +205,7 @@ static inline void small_pause(void) + #endif + } + +-static void teb_list_rdlock(void) ++void teb_list_rdlock(void) + { + for (;;) + { +@@ -216,7 +216,7 @@ static void teb_list_rdlock(void) + } + } + +-static void teb_list_rdunlock(void) ++void teb_list_rdunlock(void) + { + InterlockedDecrement( &teb_spinlock ); + } +diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h +index 41e8666a25c..6a46946b218 100644 +--- a/dlls/ntdll/ntdll_misc.h ++++ b/dlls/ntdll/ntdll_misc.h +@@ -71,6 +71,8 @@ extern BOOL is_wow64 DECLSPEC_HIDDEN; + + /* module handling */ + extern LIST_ENTRY tls_links DECLSPEC_HIDDEN; ++extern void teb_list_rdlock(void) DECLSPEC_HIDDEN; ++extern void teb_list_rdunlock(void) DECLSPEC_HIDDEN; + extern FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports, + DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user ) DECLSPEC_HIDDEN; + extern FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports, DWORD exp_size, +diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c +index 8df7015df9f..05bccf698b6 100644 +--- a/dlls/ntdll/sync.c ++++ b/dlls/ntdll/sync.c +@@ -37,6 +37,13 @@ + #include "wine/debug.h" + #include "ntdll_misc.h" + ++WINE_DEFAULT_DEBUG_CHANNEL(sync); ++ ++static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) ++{ ++ if (!timeout) return "(infinite)"; ++ return wine_dbgstr_longlong( timeout->QuadPart ); ++} + + /****************************************************************** + * RtlRunOnceInitialize (NTDLL.@) +@@ -531,13 +538,48 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, + return status; + } + ++static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size ) ++{ ++ switch (size) ++ { ++ case 1: ++ return (*(const UCHAR *)addr == *(const UCHAR *)cmp); ++ case 2: ++ return (*(const USHORT *)addr == *(const USHORT *)cmp); ++ case 4: ++ return (*(const ULONG *)addr == *(const ULONG *)cmp); ++ case 8: ++ return (*(const ULONG64 *)addr == *(const ULONG64 *)cmp); ++ } ++ ++ return FALSE; ++} ++ + /*********************************************************************** + * RtlWaitOnAddress (NTDLL.@) + */ + NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size, + const LARGE_INTEGER *timeout ) + { +- return unix_funcs->RtlWaitOnAddress( addr, cmp, size, timeout ); ++ NTSTATUS ret; ++ ++ TRACE("addr %p cmp %p size %#Ix timeout %s\n", addr, cmp, size, debugstr_timeout( timeout )); ++ ++ if (size != 1 && size != 2 && size != 4 && size != 8) ++ return STATUS_INVALID_PARAMETER; ++ ++ InterlockedExchangePointer( &NtCurrentTeb()->ReservedForPerf, (void *)addr ); ++ ++ if (!compare_addr( addr, cmp, size )) ++ { ++ InterlockedExchangePointer( &NtCurrentTeb()->ReservedForPerf, NULL ); ++ return STATUS_SUCCESS; ++ } ++ ++ ret = NtWaitForAlertByThreadId( addr, timeout ); ++ InterlockedExchangePointer( &NtCurrentTeb()->ReservedForPerf, NULL ); ++ if (ret == STATUS_ALERTED) ret = STATUS_SUCCESS; ++ return ret; + } + + /*********************************************************************** +@@ -545,7 +587,20 @@ NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size + */ + void WINAPI RtlWakeAddressAll( const void *addr ) + { +- return unix_funcs->RtlWakeAddressAll( addr ); ++ LIST_ENTRY *entry; ++ ++ TRACE("%p\n", addr); ++ ++ if (!addr) return; ++ ++ teb_list_rdlock(); ++ for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) ++ { ++ const TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); ++ if (teb->ReservedForPerf == addr) ++ NtAlertThreadByThreadId( teb->ClientId.UniqueThread ); ++ } ++ teb_list_rdunlock(); + } + + /*********************************************************************** +@@ -553,5 +608,21 @@ void WINAPI RtlWakeAddressAll( const void *addr ) + */ + void WINAPI RtlWakeAddressSingle( const void *addr ) + { +- return unix_funcs->RtlWakeAddressSingle( addr ); ++ LIST_ENTRY *entry; ++ ++ TRACE("%p\n", addr); ++ ++ if (!addr) return; ++ ++ teb_list_rdlock(); ++ for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) ++ { ++ const TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); ++ if (teb->ReservedForPerf == addr) ++ { ++ NtAlertThreadByThreadId( teb->ClientId.UniqueThread ); ++ break; ++ } ++ } ++ teb_list_rdunlock(); + } +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 31cfb43d11f..5ab3121dd2c 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1511,9 +1511,6 @@ static struct unix_funcs unix_funcs = + NtCurrentTeb, + DbgUiIssueRemoteBreakin, + RtlGetSystemTimePrecise, +- RtlWaitOnAddress, +- RtlWakeAddressAll, +- RtlWakeAddressSingle, + fast_RtlpWaitForCriticalSection, + fast_RtlpUnWaitCriticalSection, + fast_RtlDeleteCriticalSection, +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index e4aa03f94da..2bdd3a196bc 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -77,10 +77,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(sync); + + HANDLE keyed_event = 0; + +-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)"; +@@ -190,24 +186,6 @@ static void timespec_from_timeout( struct timespec *timespec, const LARGE_INTEGE + #endif + + +-static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size ) +-{ +- switch (size) +- { +- case 1: +- return (*(const UCHAR *)addr == *(const UCHAR *)cmp); +- case 2: +- return (*(const USHORT *)addr == *(const USHORT *)cmp); +- case 4: +- return (*(const ULONG *)addr == *(const ULONG *)cmp); +- case 8: +- return (*(const ULONG64 *)addr == *(const ULONG64 *)cmp); +- } +- +- return FALSE; +-} +- +- + /* create a struct security_descriptor and contained information in one contiguous piece of memory */ + NTSTATUS alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct object_attributes **ret, + data_size_t *ret_len ) +@@ -2678,71 +2656,6 @@ NTSTATUS CDECL fast_RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable, + return STATUS_SUCCESS; + } + +- +-/* We can't map addresses to futex directly, because an application can wait on +- * 8 bytes, and we can't pass all 8 as the compare value to futex(). Instead we +- * map all addresses to a small fixed table of futexes. This may result in +- * spurious wakes, but the application is already expected to handle those. */ +- +-static int addr_futex_table[256]; +- +-static inline int *hash_addr( const void *addr ) +-{ +- ULONG_PTR val = (ULONG_PTR)addr; +- +- return &addr_futex_table[(val >> 2) & 255]; +-} +- +-static inline NTSTATUS fast_wait_addr( const void *addr, const void *cmp, SIZE_T size, +- const LARGE_INTEGER *timeout ) +-{ +- int *futex; +- int val; +- struct timespec timespec; +- int ret; +- +- if (!use_futexes()) +- return STATUS_NOT_IMPLEMENTED; +- +- futex = hash_addr( addr ); +- +- /* We must read the previous value of the futex before checking the value +- * of the address being waited on. That way, if we receive a wake between +- * now and waiting on the futex, we know that val will have changed. +- * Use an atomic load so that memory accesses are ordered between this read +- * and the increment below. */ +- val = InterlockedCompareExchange( futex, 0, 0 ); +- if (!compare_addr( addr, cmp, size )) +- return STATUS_SUCCESS; +- +- if (timeout) +- { +- timespec_from_timeout( ×pec, timeout ); +- ret = futex_wait( futex, val, ×pec ); +- } +- else +- ret = futex_wait( futex, val, NULL ); +- +- if (ret == -1 && errno == ETIMEDOUT) +- return STATUS_TIMEOUT; +- return STATUS_SUCCESS; +-} +- +-static inline NTSTATUS fast_wake_addr( const void *addr ) +-{ +- int *futex; +- +- if (!use_futexes()) +- return STATUS_NOT_IMPLEMENTED; +- +- futex = hash_addr( addr ); +- +- InterlockedIncrement( futex ); +- +- futex_wake( futex, INT_MAX ); +- return STATUS_SUCCESS; +-} +- + #else + + NTSTATUS CDECL fast_RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) +@@ -2785,79 +2698,4 @@ NTSTATUS CDECL fast_wait_cv( RTL_CONDITION_VARIABLE *variable, const void *value + return STATUS_NOT_IMPLEMENTED; + } + +-static inline NTSTATUS fast_wait_addr( const void *addr, const void *cmp, SIZE_T size, +- const LARGE_INTEGER *timeout ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-static inline NTSTATUS fast_wake_addr( const void *addr ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- + #endif +- +- +-/*********************************************************************** +- * RtlWaitOnAddress (NTDLL.@) +- */ +-NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size, +- const LARGE_INTEGER *timeout ) +-{ +- select_op_t select_op; +- NTSTATUS ret; +- timeout_t abs_timeout = timeout ? timeout->QuadPart : TIMEOUT_INFINITE; +- +- if (size != 1 && size != 2 && size != 4 && size != 8) +- return STATUS_INVALID_PARAMETER; +- +- if ((ret = fast_wait_addr( addr, cmp, size, timeout )) != STATUS_NOT_IMPLEMENTED) +- return ret; +- +- mutex_lock( &addr_mutex ); +- if (!compare_addr( addr, cmp, size )) +- { +- mutex_unlock( &addr_mutex ); +- return STATUS_SUCCESS; +- } +- +- if (abs_timeout < 0) +- { +- LARGE_INTEGER now; +- +- NtQueryPerformanceCounter( &now, NULL ); +- abs_timeout -= now.QuadPart; +- } +- +- select_op.keyed_event.op = SELECT_KEYED_EVENT_WAIT; +- select_op.keyed_event.handle = wine_server_obj_handle( keyed_event ); +- select_op.keyed_event.key = wine_server_client_ptr( addr ); +- +- return server_select( &select_op, sizeof(select_op.keyed_event), SELECT_INTERRUPTIBLE, +- abs_timeout, NULL, &addr_mutex, NULL ); +-} +- +-/*********************************************************************** +- * RtlWakeAddressAll (NTDLL.@) +- */ +-void WINAPI RtlWakeAddressAll( const void *addr ) +-{ +- if (fast_wake_addr( addr ) != STATUS_NOT_IMPLEMENTED) return; +- +- mutex_lock( &addr_mutex ); +- while (NtReleaseKeyedEvent( 0, addr, 0, &zero_timeout ) == STATUS_SUCCESS) {} +- mutex_unlock( &addr_mutex ); +-} +- +-/*********************************************************************** +- * RtlWakeAddressSingle (NTDLL.@) +- */ +-void WINAPI RtlWakeAddressSingle( const void *addr ) +-{ +- if (fast_wake_addr( addr ) != STATUS_NOT_IMPLEMENTED) return; +- +- mutex_lock( &addr_mutex ); +- NtReleaseKeyedEvent( 0, addr, 0, &zero_timeout ); +- mutex_unlock( &addr_mutex ); +-} +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 7ed3148e4c4..a9c05cbd09b 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -27,7 +27,7 @@ + struct _DISPATCHER_CONTEXT; + + /* increment this when you change the function table */ +-#define NTDLL_UNIXLIB_VERSION 106 ++#define NTDLL_UNIXLIB_VERSION 107 + + struct unix_funcs + { +@@ -37,10 +37,6 @@ struct unix_funcs + /* other Win32 API functions */ + NTSTATUS (WINAPI *DbgUiIssueRemoteBreakin)( HANDLE process ); + LONGLONG (WINAPI *RtlGetSystemTimePrecise)(void); +- NTSTATUS (WINAPI *RtlWaitOnAddress)( const void *addr, const void *cmp, SIZE_T size, +- const LARGE_INTEGER *timeout ); +- void (WINAPI *RtlWakeAddressAll)( const void *addr ); +- void (WINAPI *RtlWakeAddressSingle)( const void *addr ); + + /* fast locks */ + NTSTATUS (CDECL *fast_RtlpWaitForCriticalSection)( RTL_CRITICAL_SECTION *crit, int timeout ); +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0010-ntdll-Merge-critsection.c-into-sync.c.patch b/patches/ntdll-NtAlertThreadByThreadId/0010-ntdll-Merge-critsection.c-into-sync.c.patch new file mode 100644 index 00000000..e25d2e35 --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0010-ntdll-Merge-critsection.c-into-sync.c.patch @@ -0,0 +1,933 @@ +From 9e9f51741ee04bcf8123ae23241ba3c4f8c8b2cf Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 31 Aug 2020 23:30:52 -0500 +Subject: [PATCH 10/13] ntdll: Merge critsection.c into sync.c. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/Makefile.in | 1 - + dlls/ntdll/critsection.c | 543 --------------------------------------- + dlls/ntdll/sync.c | 334 +++++++++++++++++++++++- + 3 files changed, 333 insertions(+), 545 deletions(-) + delete mode 100644 dlls/ntdll/critsection.c + +diff --git a/dlls/ntdll/Makefile.in b/dlls/ntdll/Makefile.in +index b2f63d9f63a..5ef28d2f722 100644 +--- a/dlls/ntdll/Makefile.in ++++ b/dlls/ntdll/Makefile.in +@@ -9,7 +9,6 @@ EXTRADLLFLAGS = -mno-cygwin -nodefaultlibs -Wl,--image-base,0x7bc00000 + C_SRCS = \ + actctx.c \ + atom.c \ +- critsection.c \ + crypt.c \ + debugbuffer.c \ + directory.c \ +diff --git a/dlls/ntdll/critsection.c b/dlls/ntdll/critsection.c +deleted file mode 100644 +index fe7d933c0fa..00000000000 +--- a/dlls/ntdll/critsection.c ++++ /dev/null +@@ -1,543 +0,0 @@ +-/* +- * Win32 critical sections +- * +- * Copyright 1998 Alexandre Julliard +- * +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include "ntstatus.h" +-#define WIN32_NO_STATUS +-#include "windef.h" +-#include "winternl.h" +-#include "wine/debug.h" +-#include "ntdll_misc.h" +- +-WINE_DEFAULT_DEBUG_CHANNEL(ntdll); +-WINE_DECLARE_DEBUG_CHANNEL(relay); +- +-static inline void small_pause(void) +-{ +-#ifdef __i386__ +- __asm__ __volatile__( "rep;nop" : : : "memory" ); +-#else +- __asm__ __volatile__( "" : : : "memory" ); +-#endif +-} +- +-static void *no_debug_info_marker = (void *)(ULONG_PTR)-1; +- +-static BOOL crit_section_has_debuginfo(const RTL_CRITICAL_SECTION *crit) +-{ +- return crit->DebugInfo != NULL && crit->DebugInfo != no_debug_info_marker; +-} +- +-/*********************************************************************** +- * get_semaphore +- */ +-static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit ) +-{ +- HANDLE ret = crit->LockSemaphore; +- if (!ret) +- { +- HANDLE sem; +- if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0; +- if (!(ret = InterlockedCompareExchangePointer( &crit->LockSemaphore, sem, 0 ))) +- ret = sem; +- else +- NtClose(sem); /* somebody beat us to it */ +- } +- return ret; +-} +- +-/*********************************************************************** +- * wait_semaphore +- */ +-static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout ) +-{ +- NTSTATUS ret; +- +- /* debug info is cleared by MakeCriticalSectionGlobal */ +- if (!crit_section_has_debuginfo( crit ) || +- ((ret = unix_funcs->fast_RtlpWaitForCriticalSection( crit, timeout )) == STATUS_NOT_IMPLEMENTED)) +- { +- HANDLE sem = get_semaphore( crit ); +- LARGE_INTEGER time; +- +- time.QuadPart = timeout * (LONGLONG)-10000000; +- ret = NtWaitForSingleObject( sem, FALSE, &time ); +- } +- return ret; +-} +- +-/*********************************************************************** +- * RtlInitializeCriticalSection (NTDLL.@) +- * +- * Initialises a new critical section. +- * +- * PARAMS +- * crit [O] Critical section to initialise +- * +- * RETURNS +- * STATUS_SUCCESS. +- * +- * SEE +- * RtlInitializeCriticalSectionEx(), +- * RtlInitializeCriticalSectionAndSpinCount(), RtlDeleteCriticalSection(), +- * RtlEnterCriticalSection(), RtlLeaveCriticalSection(), +- * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount() +- */ +-NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- return RtlInitializeCriticalSectionEx( crit, 0, 0 ); +-} +- +-/*********************************************************************** +- * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@) +- * +- * Initialises a new critical section with a given spin count. +- * +- * PARAMS +- * crit [O] Critical section to initialise +- * spincount [I] Spin count for crit +- * +- * RETURNS +- * STATUS_SUCCESS. +- * +- * NOTES +- * Available on NT4 SP3 or later. +- * +- * SEE +- * RtlInitializeCriticalSectionEx(), +- * RtlInitializeCriticalSection(), RtlDeleteCriticalSection(), +- * RtlEnterCriticalSection(), RtlLeaveCriticalSection(), +- * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount() +- */ +-NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount ) +-{ +- return RtlInitializeCriticalSectionEx( crit, spincount, 0 ); +-} +- +-/*********************************************************************** +- * RtlInitializeCriticalSectionEx (NTDLL.@) +- * +- * Initialises a new critical section with a given spin count and flags. +- * +- * PARAMS +- * crit [O] Critical section to initialise. +- * spincount [I] Number of times to spin upon contention. +- * flags [I] RTL_CRITICAL_SECTION_FLAG_ flags from winnt.h. +- * +- * RETURNS +- * STATUS_SUCCESS. +- * +- * NOTES +- * Available on Vista or later. +- * +- * SEE +- * RtlInitializeCriticalSection(), RtlDeleteCriticalSection(), +- * RtlEnterCriticalSection(), RtlLeaveCriticalSection(), +- * RtlTryEnterCriticalSection(), RtlSetCriticalSectionSpinCount() +- */ +-NTSTATUS WINAPI RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION *crit, ULONG spincount, ULONG flags ) +-{ +- if (flags & (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT)) +- FIXME("(%p,%u,0x%08x) semi-stub\n", crit, spincount, flags); +- +- /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use +- * memory from a static pool to hold the debug info. Then heap.c could pass +- * this flag rather than initialising the process heap CS by hand. If this +- * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo +- * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree(). +- */ +- if (flags & RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO) +- crit->DebugInfo = no_debug_info_marker; +- else +- { +- crit->DebugInfo = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG)); +- if (crit->DebugInfo) +- { +- crit->DebugInfo->Type = 0; +- crit->DebugInfo->CreatorBackTraceIndex = 0; +- crit->DebugInfo->CriticalSection = crit; +- crit->DebugInfo->ProcessLocksList.Blink = &(crit->DebugInfo->ProcessLocksList); +- crit->DebugInfo->ProcessLocksList.Flink = &(crit->DebugInfo->ProcessLocksList); +- crit->DebugInfo->EntryCount = 0; +- crit->DebugInfo->ContentionCount = 0; +- memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) ); +- } +- } +- crit->LockCount = -1; +- crit->RecursionCount = 0; +- crit->OwningThread = 0; +- crit->LockSemaphore = 0; +- if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0; +- crit->SpinCount = spincount & ~0x80000000; +- return STATUS_SUCCESS; +-} +- +-/*********************************************************************** +- * RtlSetCriticalSectionSpinCount (NTDLL.@) +- * +- * Sets the spin count of a critical section. +- * +- * PARAMS +- * crit [I/O] Critical section +- * spincount [I] Spin count for crit +- * +- * RETURNS +- * The previous spin count. +- * +- * NOTES +- * If the system is not SMP, spincount is ignored and set to 0. +- * +- * SEE +- * RtlInitializeCriticalSectionEx(), +- * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), +- * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), +- * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() +- */ +-ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount ) +-{ +- ULONG oldspincount = crit->SpinCount; +- if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0; +- crit->SpinCount = spincount; +- return oldspincount; +-} +- +-/*********************************************************************** +- * RtlDeleteCriticalSection (NTDLL.@) +- * +- * Frees the resources used by a critical section. +- * +- * PARAMS +- * crit [I/O] Critical section to free +- * +- * RETURNS +- * STATUS_SUCCESS. +- * +- * SEE +- * RtlInitializeCriticalSectionEx(), +- * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), +- * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), +- * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() +- */ +-NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- crit->LockCount = -1; +- crit->RecursionCount = 0; +- crit->OwningThread = 0; +- if (crit_section_has_debuginfo( crit )) +- { +- /* only free the ones we made in here */ +- if (!crit->DebugInfo->Spare[0]) +- { +- RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo ); +- crit->DebugInfo = NULL; +- } +- if (unix_funcs->fast_RtlDeleteCriticalSection( crit ) == STATUS_NOT_IMPLEMENTED) +- NtClose( crit->LockSemaphore ); +- } +- else NtClose( crit->LockSemaphore ); +- crit->LockSemaphore = 0; +- return STATUS_SUCCESS; +-} +- +- +-/*********************************************************************** +- * RtlpWaitForCriticalSection (NTDLL.@) +- * +- * Waits for a busy critical section to become free. +- * +- * PARAMS +- * crit [I/O] Critical section to wait for +- * +- * RETURNS +- * STATUS_SUCCESS. +- * +- * NOTES +- * Use RtlEnterCriticalSection() instead of this function as it is often much +- * faster. +- * +- * SEE +- * RtlInitializeCriticalSectionEx(), +- * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), +- * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), +- * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() +- */ +-NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- LONGLONG timeout = NtCurrentTeb()->Peb->CriticalSectionTimeout.QuadPart / -10000000; +- +- /* Don't allow blocking on a critical section during process termination */ +- if (RtlDllShutdownInProgress()) +- { +- WARN( "process %s is shutting down, returning STATUS_SUCCESS\n", +- debugstr_w(NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer) ); +- return STATUS_SUCCESS; +- } +- +- for (;;) +- { +- EXCEPTION_RECORD rec; +- NTSTATUS status = wait_semaphore( crit, 5 ); +- timeout -= 5; +- +- if ( status == STATUS_TIMEOUT ) +- { +- const char *name = NULL; +- if (crit_section_has_debuginfo( crit )) name = (char *)crit->DebugInfo->Spare[0]; +- if (!name) name = "?"; +- ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n", +- crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) ); +- status = wait_semaphore( crit, 60 ); +- timeout -= 60; +- +- if ( status == STATUS_TIMEOUT && TRACE_ON(relay) ) +- { +- ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n", +- crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) ); +- status = wait_semaphore( crit, 300 ); +- timeout -= 300; +- } +- } +- if (status == STATUS_WAIT_0) break; +- +- /* Throw exception only for Wine internal locks */ +- if (!crit_section_has_debuginfo( crit ) || !crit->DebugInfo->Spare[0]) continue; +- +- /* only throw deadlock exception if configured timeout is reached */ +- if (timeout > 0) continue; +- +- rec.ExceptionCode = STATUS_POSSIBLE_DEADLOCK; +- rec.ExceptionFlags = 0; +- rec.ExceptionRecord = NULL; +- rec.ExceptionAddress = RtlRaiseException; /* sic */ +- rec.NumberParameters = 1; +- rec.ExceptionInformation[0] = (ULONG_PTR)crit; +- RtlRaiseException( &rec ); +- } +- if (crit_section_has_debuginfo( crit )) crit->DebugInfo->ContentionCount++; +- return STATUS_SUCCESS; +-} +- +- +-/*********************************************************************** +- * RtlpUnWaitCriticalSection (NTDLL.@) +- * +- * Notifies other threads waiting on the busy critical section that it has +- * become free. +- * +- * PARAMS +- * crit [I/O] Critical section +- * +- * RETURNS +- * Success: STATUS_SUCCESS. +- * Failure: Any error returned by NtReleaseSemaphore() +- * +- * NOTES +- * Use RtlLeaveCriticalSection() instead of this function as it is often much +- * faster. +- * +- * SEE +- * RtlInitializeCriticalSectionEx(), +- * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), +- * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), +- * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() +- */ +-NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- NTSTATUS ret; +- +- /* debug info is cleared by MakeCriticalSectionGlobal */ +- if (!crit_section_has_debuginfo( crit ) || +- ((ret = unix_funcs->fast_RtlpUnWaitCriticalSection( crit )) == STATUS_NOT_IMPLEMENTED)) +- { +- HANDLE sem = get_semaphore( crit ); +- ret = NtReleaseSemaphore( sem, 1, NULL ); +- } +- if (ret) RtlRaiseStatus( ret ); +- return ret; +-} +- +- +-/*********************************************************************** +- * RtlEnterCriticalSection (NTDLL.@) +- * +- * Enters a critical section, waiting for it to become available if necessary. +- * +- * PARAMS +- * crit [I/O] Critical section to enter +- * +- * RETURNS +- * STATUS_SUCCESS. The critical section is held by the caller. +- * +- * SEE +- * RtlInitializeCriticalSectionEx(), +- * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), +- * RtlDeleteCriticalSection(), RtlSetCriticalSectionSpinCount(), +- * RtlLeaveCriticalSection(), RtlTryEnterCriticalSection() +- */ +-NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- if (crit->SpinCount) +- { +- ULONG count; +- +- if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS; +- for (count = crit->SpinCount; count > 0; count--) +- { +- if (crit->LockCount > 0) break; /* more than one waiter, don't bother spinning */ +- if (crit->LockCount == -1) /* try again */ +- { +- if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1) goto done; +- } +- small_pause(); +- } +- } +- +- if (InterlockedIncrement( &crit->LockCount )) +- { +- if (crit->OwningThread == ULongToHandle(GetCurrentThreadId())) +- { +- crit->RecursionCount++; +- return STATUS_SUCCESS; +- } +- +- /* Now wait for it */ +- RtlpWaitForCriticalSection( crit ); +- } +-done: +- crit->OwningThread = ULongToHandle(GetCurrentThreadId()); +- crit->RecursionCount = 1; +- return STATUS_SUCCESS; +-} +- +- +-/*********************************************************************** +- * RtlTryEnterCriticalSection (NTDLL.@) +- * +- * Tries to enter a critical section without waiting. +- * +- * PARAMS +- * crit [I/O] Critical section to enter +- * +- * RETURNS +- * Success: TRUE. The critical section is held by the caller. +- * Failure: FALSE. The critical section is currently held by another thread. +- * +- * SEE +- * RtlInitializeCriticalSectionEx(), +- * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), +- * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), +- * RtlLeaveCriticalSection(), RtlSetCriticalSectionSpinCount() +- */ +-BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- BOOL ret = FALSE; +- if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1) +- { +- crit->OwningThread = ULongToHandle(GetCurrentThreadId()); +- crit->RecursionCount = 1; +- ret = TRUE; +- } +- else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId())) +- { +- InterlockedIncrement( &crit->LockCount ); +- crit->RecursionCount++; +- ret = TRUE; +- } +- return ret; +-} +- +- +-/*********************************************************************** +- * RtlIsCriticalSectionLocked (NTDLL.@) +- * +- * Checks if the critical section is locked by any thread. +- * +- * PARAMS +- * crit [I/O] Critical section to check. +- * +- * RETURNS +- * Success: TRUE. The critical section is locked. +- * Failure: FALSE. The critical section is not locked. +- */ +-BOOL WINAPI RtlIsCriticalSectionLocked( RTL_CRITICAL_SECTION *crit ) +-{ +- return crit->RecursionCount != 0; +-} +- +- +-/*********************************************************************** +- * RtlIsCriticalSectionLockedByThread (NTDLL.@) +- * +- * Checks if the critical section is locked by the current thread. +- * +- * PARAMS +- * crit [I/O] Critical section to check. +- * +- * RETURNS +- * Success: TRUE. The critical section is locked. +- * Failure: FALSE. The critical section is not locked. +- */ +-BOOL WINAPI RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION *crit ) +-{ +- return crit->OwningThread == ULongToHandle(GetCurrentThreadId()) && +- crit->RecursionCount; +-} +- +- +-/*********************************************************************** +- * RtlLeaveCriticalSection (NTDLL.@) +- * +- * Leaves a critical section. +- * +- * PARAMS +- * crit [I/O] Critical section to leave. +- * +- * RETURNS +- * STATUS_SUCCESS. +- * +- * SEE +- * RtlInitializeCriticalSectionEx(), +- * RtlInitializeCriticalSection(), RtlInitializeCriticalSectionAndSpinCount(), +- * RtlDeleteCriticalSection(), RtlEnterCriticalSection(), +- * RtlSetCriticalSectionSpinCount(), RtlTryEnterCriticalSection() +- */ +-NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- if (--crit->RecursionCount) +- { +- if (crit->RecursionCount > 0) InterlockedDecrement( &crit->LockCount ); +- else ERR( "section %p is not acquired\n", crit ); +- } +- else +- { +- crit->OwningThread = 0; +- if (InterlockedDecrement( &crit->LockCount ) >= 0) +- { +- /* someone is waiting */ +- RtlpUnWaitCriticalSection( crit ); +- } +- } +- return STATUS_SUCCESS; +-} +diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c +index 05bccf698b6..ea327172b86 100644 +--- a/dlls/ntdll/sync.c ++++ b/dlls/ntdll/sync.c +@@ -2,7 +2,7 @@ + * Process synchronisation + * + * Copyright 1996, 1997, 1998 Marcus Meissner +- * Copyright 1997, 1999 Alexandre Julliard ++ * Copyright 1997, 1998, 1999 Alexandre Julliard + * Copyright 1999, 2000 Juergen Schmied + * Copyright 2003 Eric Pouech + * +@@ -38,6 +38,7 @@ + #include "ntdll_misc.h" + + WINE_DEFAULT_DEBUG_CHANNEL(sync); ++WINE_DECLARE_DEBUG_CHANNEL(relay); + + static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) + { +@@ -626,3 +627,334 @@ void WINAPI RtlWakeAddressSingle( const void *addr ) + } + teb_list_rdunlock(); + } ++ ++ ++/*********************************************************************** ++ * Critical sections ++ ***********************************************************************/ ++ ++ ++static void *no_debug_info_marker = (void *)(ULONG_PTR)-1; ++ ++static BOOL crit_section_has_debuginfo( const RTL_CRITICAL_SECTION *crit ) ++{ ++ return crit->DebugInfo != NULL && crit->DebugInfo != no_debug_info_marker; ++} ++ ++static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit ) ++{ ++ HANDLE ret = crit->LockSemaphore; ++ if (!ret) ++ { ++ HANDLE sem; ++ if (NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 )) return 0; ++ if (!(ret = InterlockedCompareExchangePointer( &crit->LockSemaphore, sem, 0 ))) ++ ret = sem; ++ else ++ NtClose(sem); /* somebody beat us to it */ ++ } ++ return ret; ++} ++ ++static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout ) ++{ ++ NTSTATUS ret; ++ ++ /* debug info is cleared by MakeCriticalSectionGlobal */ ++ if (!crit_section_has_debuginfo( crit ) || ++ ((ret = unix_funcs->fast_RtlpWaitForCriticalSection( crit, timeout )) == STATUS_NOT_IMPLEMENTED)) ++ { ++ HANDLE sem = get_semaphore( crit ); ++ LARGE_INTEGER time; ++ ++ time.QuadPart = timeout * (LONGLONG)-10000000; ++ ret = NtWaitForSingleObject( sem, FALSE, &time ); ++ } ++ return ret; ++} ++ ++/****************************************************************************** ++ * RtlInitializeCriticalSection (NTDLL.@) ++ */ ++NTSTATUS WINAPI RtlInitializeCriticalSection( RTL_CRITICAL_SECTION *crit ) ++{ ++ return RtlInitializeCriticalSectionEx( crit, 0, 0 ); ++} ++ ++ ++/****************************************************************************** ++ * RtlInitializeCriticalSectionAndSpinCount (NTDLL.@) ++ */ ++NTSTATUS WINAPI RtlInitializeCriticalSectionAndSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount ) ++{ ++ return RtlInitializeCriticalSectionEx( crit, spincount, 0 ); ++} ++ ++ ++/****************************************************************************** ++ * RtlInitializeCriticalSectionEx (NTDLL.@) ++ */ ++NTSTATUS WINAPI RtlInitializeCriticalSectionEx( RTL_CRITICAL_SECTION *crit, ULONG spincount, ULONG flags ) ++{ ++ if (flags & (RTL_CRITICAL_SECTION_FLAG_DYNAMIC_SPIN|RTL_CRITICAL_SECTION_FLAG_STATIC_INIT)) ++ FIXME("(%p,%u,0x%08x) semi-stub\n", crit, spincount, flags); ++ ++ /* FIXME: if RTL_CRITICAL_SECTION_FLAG_STATIC_INIT is given, we should use ++ * memory from a static pool to hold the debug info. Then heap.c could pass ++ * this flag rather than initialising the process heap CS by hand. If this ++ * is done, then debug info should be managed through Rtlp[Allocate|Free]DebugInfo ++ * so (e.g.) MakeCriticalSectionGlobal() doesn't free it using HeapFree(). ++ */ ++ if (flags & RTL_CRITICAL_SECTION_FLAG_NO_DEBUG_INFO) ++ crit->DebugInfo = no_debug_info_marker; ++ else ++ { ++ crit->DebugInfo = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(RTL_CRITICAL_SECTION_DEBUG )); ++ if (crit->DebugInfo) ++ { ++ crit->DebugInfo->Type = 0; ++ crit->DebugInfo->CreatorBackTraceIndex = 0; ++ crit->DebugInfo->CriticalSection = crit; ++ crit->DebugInfo->ProcessLocksList.Blink = &crit->DebugInfo->ProcessLocksList; ++ crit->DebugInfo->ProcessLocksList.Flink = &crit->DebugInfo->ProcessLocksList; ++ crit->DebugInfo->EntryCount = 0; ++ crit->DebugInfo->ContentionCount = 0; ++ memset( crit->DebugInfo->Spare, 0, sizeof(crit->DebugInfo->Spare) ); ++ } ++ } ++ crit->LockCount = -1; ++ crit->RecursionCount = 0; ++ crit->OwningThread = 0; ++ crit->LockSemaphore = 0; ++ if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0; ++ crit->SpinCount = spincount & ~0x80000000; ++ return STATUS_SUCCESS; ++} ++ ++ ++/****************************************************************************** ++ * RtlSetCriticalSectionSpinCount (NTDLL.@) ++ */ ++ULONG WINAPI RtlSetCriticalSectionSpinCount( RTL_CRITICAL_SECTION *crit, ULONG spincount ) ++{ ++ ULONG oldspincount = crit->SpinCount; ++ if (NtCurrentTeb()->Peb->NumberOfProcessors <= 1) spincount = 0; ++ crit->SpinCount = spincount; ++ return oldspincount; ++} ++ ++ ++/****************************************************************************** ++ * RtlDeleteCriticalSection (NTDLL.@) ++ */ ++NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit ) ++{ ++ crit->LockCount = -1; ++ crit->RecursionCount = 0; ++ crit->OwningThread = 0; ++ if (crit_section_has_debuginfo( crit )) ++ { ++ /* only free the ones we made in here */ ++ if (!crit->DebugInfo->Spare[0]) ++ { ++ RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo ); ++ crit->DebugInfo = NULL; ++ } ++ if (unix_funcs->fast_RtlDeleteCriticalSection( crit ) == STATUS_NOT_IMPLEMENTED) ++ NtClose( crit->LockSemaphore ); ++ } ++ else NtClose( crit->LockSemaphore ); ++ crit->LockSemaphore = 0; ++ return STATUS_SUCCESS; ++} ++ ++ ++/****************************************************************************** ++ * RtlpWaitForCriticalSection (NTDLL.@) ++ */ ++NTSTATUS WINAPI RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit ) ++{ ++ LONGLONG timeout = NtCurrentTeb()->Peb->CriticalSectionTimeout.QuadPart / -10000000; ++ ++ /* Don't allow blocking on a critical section during process termination */ ++ if (RtlDllShutdownInProgress()) ++ { ++ WARN( "process %s is shutting down, returning STATUS_SUCCESS\n", ++ debugstr_w(NtCurrentTeb()->Peb->ProcessParameters->ImagePathName.Buffer) ); ++ return STATUS_SUCCESS; ++ } ++ ++ for (;;) ++ { ++ EXCEPTION_RECORD rec; ++ NTSTATUS status = wait_semaphore( crit, 5 ); ++ timeout -= 5; ++ ++ if ( status == STATUS_TIMEOUT ) ++ { ++ const char *name = NULL; ++ if (crit_section_has_debuginfo( crit )) name = (char *)crit->DebugInfo->Spare[0]; ++ if (!name) name = "?"; ++ ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (60 sec)\n", ++ crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) ); ++ status = wait_semaphore( crit, 60 ); ++ timeout -= 60; ++ ++ if ( status == STATUS_TIMEOUT && TRACE_ON(relay) ) ++ { ++ ERR( "section %p %s wait timed out in thread %04x, blocked by %04x, retrying (5 min)\n", ++ crit, debugstr_a(name), GetCurrentThreadId(), HandleToULong(crit->OwningThread) ); ++ status = wait_semaphore( crit, 300 ); ++ timeout -= 300; ++ } ++ } ++ if (status == STATUS_WAIT_0) break; ++ ++ /* Throw exception only for Wine internal locks */ ++ if (!crit_section_has_debuginfo( crit ) || !crit->DebugInfo->Spare[0]) continue; ++ ++ /* only throw deadlock exception if configured timeout is reached */ ++ if (timeout > 0) continue; ++ ++ rec.ExceptionCode = STATUS_POSSIBLE_DEADLOCK; ++ rec.ExceptionFlags = 0; ++ rec.ExceptionRecord = NULL; ++ rec.ExceptionAddress = RtlRaiseException; /* sic */ ++ rec.NumberParameters = 1; ++ rec.ExceptionInformation[0] = (ULONG_PTR)crit; ++ RtlRaiseException( &rec ); ++ } ++ if (crit_section_has_debuginfo( crit )) crit->DebugInfo->ContentionCount++; ++ return STATUS_SUCCESS; ++} ++ ++ ++/****************************************************************************** ++ * RtlpUnWaitCriticalSection (NTDLL.@) ++ */ ++NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit ) ++{ ++ NTSTATUS ret; ++ ++ /* debug info is cleared by MakeCriticalSectionGlobal */ ++ if (!crit_section_has_debuginfo( crit ) || ++ ((ret = unix_funcs->fast_RtlpUnWaitCriticalSection( crit )) == STATUS_NOT_IMPLEMENTED)) ++ { ++ HANDLE sem = get_semaphore( crit ); ++ ret = NtReleaseSemaphore( sem, 1, NULL ); ++ } ++ if (ret) RtlRaiseStatus( ret ); ++ return ret; ++} ++ ++ ++static inline void small_pause(void) ++{ ++#ifdef __i386__ ++ __asm__ __volatile__( "rep;nop" : : : "memory" ); ++#else ++ __asm__ __volatile__( "" : : : "memory" ); ++#endif ++} ++ ++/****************************************************************************** ++ * RtlEnterCriticalSection (NTDLL.@) ++ */ ++NTSTATUS WINAPI RtlEnterCriticalSection( RTL_CRITICAL_SECTION *crit ) ++{ ++ if (crit->SpinCount) ++ { ++ ULONG count; ++ ++ if (RtlTryEnterCriticalSection( crit )) return STATUS_SUCCESS; ++ for (count = crit->SpinCount; count > 0; count--) ++ { ++ if (crit->LockCount > 0) break; /* more than one waiter, don't bother spinning */ ++ if (crit->LockCount == -1) /* try again */ ++ { ++ if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1) goto done; ++ } ++ small_pause(); ++ } ++ } ++ ++ if (InterlockedIncrement( &crit->LockCount )) ++ { ++ if (crit->OwningThread == ULongToHandle(GetCurrentThreadId())) ++ { ++ crit->RecursionCount++; ++ return STATUS_SUCCESS; ++ } ++ ++ /* Now wait for it */ ++ RtlpWaitForCriticalSection( crit ); ++ } ++done: ++ crit->OwningThread = ULongToHandle(GetCurrentThreadId()); ++ crit->RecursionCount = 1; ++ return STATUS_SUCCESS; ++} ++ ++ ++/****************************************************************************** ++ * RtlTryEnterCriticalSection (NTDLL.@) ++ */ ++BOOL WINAPI RtlTryEnterCriticalSection( RTL_CRITICAL_SECTION *crit ) ++{ ++ BOOL ret = FALSE; ++ if (InterlockedCompareExchange( &crit->LockCount, 0, -1 ) == -1) ++ { ++ crit->OwningThread = ULongToHandle(GetCurrentThreadId()); ++ crit->RecursionCount = 1; ++ ret = TRUE; ++ } ++ else if (crit->OwningThread == ULongToHandle(GetCurrentThreadId())) ++ { ++ InterlockedIncrement( &crit->LockCount ); ++ crit->RecursionCount++; ++ ret = TRUE; ++ } ++ return ret; ++} ++ ++ ++/****************************************************************************** ++ * RtlIsCriticalSectionLocked (NTDLL.@) ++ */ ++BOOL WINAPI RtlIsCriticalSectionLocked( RTL_CRITICAL_SECTION *crit ) ++{ ++ return crit->RecursionCount != 0; ++} ++ ++ ++/****************************************************************************** ++ * RtlIsCriticalSectionLockedByThread (NTDLL.@) ++ */ ++BOOL WINAPI RtlIsCriticalSectionLockedByThread( RTL_CRITICAL_SECTION *crit ) ++{ ++ return crit->OwningThread == ULongToHandle(GetCurrentThreadId()) && ++ crit->RecursionCount; ++} ++ ++ ++/****************************************************************************** ++ * RtlLeaveCriticalSection (NTDLL.@) ++ */ ++NTSTATUS WINAPI RtlLeaveCriticalSection( RTL_CRITICAL_SECTION *crit ) ++{ ++ if (--crit->RecursionCount) ++ { ++ if (crit->RecursionCount > 0) InterlockedDecrement( &crit->LockCount ); ++ else ERR( "section %p is not acquired\n", crit ); ++ } ++ else ++ { ++ crit->OwningThread = 0; ++ if (InterlockedDecrement( &crit->LockCount ) >= 0) ++ { ++ /* someone is waiting */ ++ RtlpUnWaitCriticalSection( crit ); ++ } ++ } ++ return STATUS_SUCCESS; ++} +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0011-ntdll-Reimplement-the-critical-section-fast-path-on-.patch b/patches/ntdll-NtAlertThreadByThreadId/0011-ntdll-Reimplement-the-critical-section-fast-path-on-.patch new file mode 100644 index 00000000..f95a0296 --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0011-ntdll-Reimplement-the-critical-section-fast-path-on-.patch @@ -0,0 +1,258 @@ +From 8b1502d1fc5a6ccb748a8ada38784e79f5612811 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 31 Aug 2020 23:38:09 -0500 +Subject: [PATCH 11/13] ntdll: Reimplement the critical section fast path on + top of Win32 futexes. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/sync.c | 35 +++++++---- + dlls/ntdll/unix/loader.c | 3 - + dlls/ntdll/unix/sync.c | 109 --------------------------------- + dlls/ntdll/unix/unix_private.h | 3 - + dlls/ntdll/unixlib.h | 5 +- + 5 files changed, 24 insertions(+), 131 deletions(-) + +diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c +index ea327172b86..d7976a742c6 100644 +--- a/dlls/ntdll/sync.c ++++ b/dlls/ntdll/sync.c +@@ -658,19 +658,26 @@ static inline HANDLE get_semaphore( RTL_CRITICAL_SECTION *crit ) + + static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout ) + { +- NTSTATUS ret; ++ LARGE_INTEGER time = {.QuadPart = timeout * (LONGLONG)-10000000}; + + /* debug info is cleared by MakeCriticalSectionGlobal */ +- if (!crit_section_has_debuginfo( crit ) || +- ((ret = unix_funcs->fast_RtlpWaitForCriticalSection( crit, timeout )) == STATUS_NOT_IMPLEMENTED)) ++ if (!crit_section_has_debuginfo( crit )) + { + HANDLE sem = get_semaphore( crit ); +- LARGE_INTEGER time; +- +- time.QuadPart = timeout * (LONGLONG)-10000000; +- ret = NtWaitForSingleObject( sem, FALSE, &time ); ++ return NtWaitForSingleObject( sem, FALSE, &time ); ++ } ++ else ++ { ++ int *lock = (int *)&crit->LockSemaphore; ++ while (!InterlockedCompareExchange( lock, 0, 1 )) ++ { ++ static const int zero; ++ /* this may wait longer than specified in case of multiple wake-ups */ ++ if (RtlWaitOnAddress( (int *)&crit->LockSemaphore, &zero, sizeof(int), &time ) == STATUS_TIMEOUT) ++ return STATUS_TIMEOUT; ++ } ++ return STATUS_WAIT_0; + } +- return ret; + } + + /****************************************************************************** +@@ -760,8 +767,6 @@ NTSTATUS WINAPI RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit ) + RtlFreeHeap( GetProcessHeap(), 0, crit->DebugInfo ); + crit->DebugInfo = NULL; + } +- if (unix_funcs->fast_RtlDeleteCriticalSection( crit ) == STATUS_NOT_IMPLEMENTED) +- NtClose( crit->LockSemaphore ); + } + else NtClose( crit->LockSemaphore ); + crit->LockSemaphore = 0; +@@ -837,12 +842,18 @@ NTSTATUS WINAPI RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit ) + NTSTATUS ret; + + /* debug info is cleared by MakeCriticalSectionGlobal */ +- if (!crit_section_has_debuginfo( crit ) || +- ((ret = unix_funcs->fast_RtlpUnWaitCriticalSection( crit )) == STATUS_NOT_IMPLEMENTED)) ++ if (!crit_section_has_debuginfo( crit )) + { + HANDLE sem = get_semaphore( crit ); + ret = NtReleaseSemaphore( sem, 1, NULL ); + } ++ else ++ { ++ int *lock = (int *)&crit->LockSemaphore; ++ *lock = 1; ++ RtlWakeAddressSingle( lock ); ++ ret = STATUS_SUCCESS; ++ } + if (ret) RtlRaiseStatus( ret ); + return ret; + } +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 5ab3121dd2c..ac4efc74e5e 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1511,9 +1511,6 @@ static struct unix_funcs unix_funcs = + NtCurrentTeb, + DbgUiIssueRemoteBreakin, + RtlGetSystemTimePrecise, +- fast_RtlpWaitForCriticalSection, +- fast_RtlpUnWaitCriticalSection, +- fast_RtlDeleteCriticalSection, + fast_RtlTryAcquireSRWLockExclusive, + fast_RtlAcquireSRWLockExclusive, + fast_RtlTryAcquireSRWLockShared, +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 2bdd3a196bc..0f45fdecdca 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -2251,115 +2251,6 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG + } + + +-#ifdef __linux__ +- +-NTSTATUS CDECL fast_RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit, int timeout ) +-{ +- int val; +- struct timespec timespec; +- +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- +- timespec.tv_sec = timeout; +- timespec.tv_nsec = 0; +- while ((val = InterlockedCompareExchange( (int *)&crit->LockSemaphore, 0, 1 )) != 1) +- { +- /* note: this may wait longer than specified in case of signals or */ +- /* multiple wake-ups, but that shouldn't be a problem */ +- if (futex_wait( (int *)&crit->LockSemaphore, val, ×pec ) == -1 && errno == ETIMEDOUT) +- return STATUS_TIMEOUT; +- } +- return STATUS_WAIT_0; +-} +- +-NTSTATUS CDECL fast_RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- +- *(int *)&crit->LockSemaphore = 1; +- futex_wake( (int *)&crit->LockSemaphore, 1 ); +- return STATUS_SUCCESS; +-} +- +-NTSTATUS CDECL fast_RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- return STATUS_SUCCESS; +-} +- +-#elif defined(__APPLE__) +- +-static inline semaphore_t get_mach_semaphore( RTL_CRITICAL_SECTION *crit ) +-{ +- semaphore_t ret = *(int *)&crit->LockSemaphore; +- if (!ret) +- { +- semaphore_t sem; +- if (semaphore_create( mach_task_self(), &sem, SYNC_POLICY_FIFO, 0 )) return 0; +- if (!(ret = InterlockedCompareExchange( (int *)&crit->LockSemaphore, sem, 0 ))) +- ret = sem; +- else +- semaphore_destroy( mach_task_self(), sem ); /* somebody beat us to it */ +- } +- return ret; +-} +- +-NTSTATUS CDECL fast_RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit, int timeout ) +-{ +- mach_timespec_t timespec; +- semaphore_t sem = get_mach_semaphore( crit ); +- +- timespec.tv_sec = timeout; +- timespec.tv_nsec = 0; +- for (;;) +- { +- switch( semaphore_timedwait( sem, timespec )) +- { +- case KERN_SUCCESS: +- return STATUS_WAIT_0; +- case KERN_ABORTED: +- continue; /* got a signal, restart */ +- case KERN_OPERATION_TIMED_OUT: +- return STATUS_TIMEOUT; +- default: +- return STATUS_INVALID_HANDLE; +- } +- } +-} +- +-NTSTATUS CDECL fast_RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- semaphore_t sem = get_mach_semaphore( crit ); +- semaphore_signal( sem ); +- return STATUS_SUCCESS; +-} +- +-NTSTATUS CDECL fast_RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- semaphore_destroy( mach_task_self(), *(int *)&crit->LockSemaphore ); +- return STATUS_SUCCESS; +-} +- +-#else /* __APPLE__ */ +- +-NTSTATUS CDECL fast_RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit, int timeout ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-NTSTATUS CDECL fast_RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-NTSTATUS CDECL fast_RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-#endif +- +- + #ifdef __linux__ + + /* Futex-based SRW lock implementation: +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 9e94c0ec13c..0f833b5a51b 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -93,9 +93,6 @@ extern void (WINAPI *pKiUserApcDispatcher)(CONTEXT*,ULONG_PTR,ULONG_PTR,ULON + extern NTSTATUS (WINAPI *pKiUserExceptionDispatcher)(EXCEPTION_RECORD*,CONTEXT*) DECLSPEC_HIDDEN; + extern void (WINAPI *pLdrInitializeThunk)(CONTEXT*,void**,ULONG_PTR,ULONG_PTR) DECLSPEC_HIDDEN; + extern void (WINAPI *pRtlUserThreadStart)( PRTL_THREAD_START_ROUTINE entry, void *arg ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlpWaitForCriticalSection( RTL_CRITICAL_SECTION *crit, int timeout ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlpUnWaitCriticalSection( RTL_CRITICAL_SECTION *crit ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlDeleteCriticalSection( RTL_CRITICAL_SECTION *crit ) DECLSPEC_HIDDEN; + extern NTSTATUS CDECL fast_RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; + extern NTSTATUS CDECL fast_RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; + extern NTSTATUS CDECL fast_RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index a9c05cbd09b..a8dad0d0fcf 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -27,7 +27,7 @@ + struct _DISPATCHER_CONTEXT; + + /* increment this when you change the function table */ +-#define NTDLL_UNIXLIB_VERSION 107 ++#define NTDLL_UNIXLIB_VERSION 108 + + struct unix_funcs + { +@@ -39,9 +39,6 @@ struct unix_funcs + LONGLONG (WINAPI *RtlGetSystemTimePrecise)(void); + + /* fast locks */ +- NTSTATUS (CDECL *fast_RtlpWaitForCriticalSection)( RTL_CRITICAL_SECTION *crit, int timeout ); +- NTSTATUS (CDECL *fast_RtlpUnWaitCriticalSection)( RTL_CRITICAL_SECTION *crit ); +- NTSTATUS (CDECL *fast_RtlDeleteCriticalSection)( RTL_CRITICAL_SECTION *crit ); + NTSTATUS (CDECL *fast_RtlTryAcquireSRWLockExclusive)( RTL_SRWLOCK *lock ); + NTSTATUS (CDECL *fast_RtlAcquireSRWLockExclusive)( RTL_SRWLOCK *lock ); + NTSTATUS (CDECL *fast_RtlTryAcquireSRWLockShared)( RTL_SRWLOCK *lock ); +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0012-ntdll-Get-rid-of-the-direct-futex-path-for-condition.patch b/patches/ntdll-NtAlertThreadByThreadId/0012-ntdll-Get-rid-of-the-direct-futex-path-for-condition.patch new file mode 100644 index 00000000..a72f1cbb --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0012-ntdll-Get-rid-of-the-direct-futex-path-for-condition.patch @@ -0,0 +1,229 @@ +From 10eabed1355cf47d4eae042e775c8005a67b88d7 Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Mon, 31 Aug 2020 23:55:29 -0500 +Subject: [PATCH 12/13] ntdll: Get rid of the direct futex path for condition + variables. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/sync.c | 24 ++++-------- + dlls/ntdll/unix/loader.c | 2 - + dlls/ntdll/unix/sync.c | 71 ---------------------------------- + dlls/ntdll/unix/unix_private.h | 3 -- + dlls/ntdll/unixlib.h | 5 +-- + 5 files changed, 9 insertions(+), 96 deletions(-) + +diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c +index d7976a742c6..133ae6d4ead 100644 +--- a/dlls/ntdll/sync.c ++++ b/dlls/ntdll/sync.c +@@ -450,11 +450,8 @@ void WINAPI RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE *variable ) + */ + void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable ) + { +- if (unix_funcs->fast_RtlWakeConditionVariable( variable, 1 ) == STATUS_NOT_IMPLEMENTED) +- { +- InterlockedIncrement( (int *)&variable->Ptr ); +- RtlWakeAddressSingle( variable ); +- } ++ InterlockedIncrement( (int *)&variable->Ptr ); ++ RtlWakeAddressSingle( variable ); + } + + /*********************************************************************** +@@ -464,11 +461,8 @@ void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable ) + */ + void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable ) + { +- if (unix_funcs->fast_RtlWakeConditionVariable( variable, INT_MAX ) == STATUS_NOT_IMPLEMENTED) +- { +- InterlockedIncrement( (int *)&variable->Ptr ); +- RtlWakeAddressAll( variable ); +- } ++ InterlockedIncrement( (int *)&variable->Ptr ); ++ RtlWakeAddressAll( variable ); + } + + /*********************************************************************** +@@ -489,12 +483,11 @@ void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable ) + NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, RTL_CRITICAL_SECTION *crit, + const LARGE_INTEGER *timeout ) + { +- const void *value = variable->Ptr; ++ int value = *(int *)&variable->Ptr; + NTSTATUS status; + + RtlLeaveCriticalSection( crit ); +- if ((status = unix_funcs->fast_wait_cv( variable, value, timeout )) == STATUS_NOT_IMPLEMENTED) +- status = RtlWaitOnAddress( &variable->Ptr, &value, sizeof(value), timeout ); ++ status = RtlWaitOnAddress( &variable->Ptr, &value, sizeof(value), timeout ); + RtlEnterCriticalSection( crit ); + return status; + } +@@ -521,7 +514,7 @@ NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, R + NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, RTL_SRWLOCK *lock, + const LARGE_INTEGER *timeout, ULONG flags ) + { +- const void *value = variable->Ptr; ++ int value = *(int *)&variable->Ptr; + NTSTATUS status; + + if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED) +@@ -529,8 +522,7 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, + else + RtlReleaseSRWLockExclusive( lock ); + +- if ((status = unix_funcs->fast_wait_cv( variable, value, timeout )) == STATUS_NOT_IMPLEMENTED) +- status = RtlWaitOnAddress( variable, &value, sizeof(value), timeout ); ++ status = RtlWaitOnAddress( &variable->Ptr, &value, sizeof(value), timeout ); + + if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED) + RtlAcquireSRWLockShared( lock ); +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index ac4efc74e5e..1633aeb97eb 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1517,8 +1517,6 @@ static struct unix_funcs unix_funcs = + fast_RtlAcquireSRWLockShared, + fast_RtlReleaseSRWLockExclusive, + fast_RtlReleaseSRWLockShared, +- fast_RtlWakeConditionVariable, +- fast_wait_cv, + ntdll_atan, + ntdll_ceil, + ntdll_cos, +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 0f45fdecdca..3645c9f9549 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -166,23 +166,6 @@ static int *get_futex(void **ptr) + return NULL; + } + +-static void timespec_from_timeout( struct timespec *timespec, const LARGE_INTEGER *timeout ) +-{ +- LARGE_INTEGER now; +- timeout_t diff; +- +- if (timeout->QuadPart > 0) +- { +- NtQuerySystemTime( &now ); +- diff = timeout->QuadPart - now.QuadPart; +- } +- else +- diff = -timeout->QuadPart; +- +- timespec->tv_sec = diff / TICKSPERSEC; +- timespec->tv_nsec = (diff % TICKSPERSEC) * 100; +-} +- + #endif + + +@@ -2503,50 +2486,6 @@ NTSTATUS CDECL fast_RtlReleaseSRWLockShared( RTL_SRWLOCK *lock ) + return STATUS_SUCCESS; + } + +-NTSTATUS CDECL fast_wait_cv( RTL_CONDITION_VARIABLE *variable, const void *value, const LARGE_INTEGER *timeout ) +-{ +- const char *value_ptr; +- int aligned_value, *futex; +- struct timespec timespec; +- int ret; +- +- if (!use_futexes()) +- return STATUS_NOT_IMPLEMENTED; +- +- if (!(futex = get_futex( &variable->Ptr ))) +- return STATUS_NOT_IMPLEMENTED; +- +- value_ptr = (const char *)&value; +- value_ptr += ((ULONG_PTR)futex) - ((ULONG_PTR)&variable->Ptr); +- aligned_value = *(int *)value_ptr; +- +- if (timeout && timeout->QuadPart != TIMEOUT_INFINITE) +- { +- timespec_from_timeout( ×pec, timeout ); +- ret = futex_wait( futex, aligned_value, ×pec ); +- } +- else +- ret = futex_wait( futex, aligned_value, NULL ); +- +- if (ret == -1 && errno == ETIMEDOUT) +- return STATUS_TIMEOUT; +- return STATUS_WAIT_0; +-} +- +-NTSTATUS CDECL fast_RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable, int count ) +-{ +- int *futex; +- +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- +- if (!(futex = get_futex( &variable->Ptr ))) +- return STATUS_NOT_IMPLEMENTED; +- +- InterlockedIncrement( futex ); +- futex_wake( futex, count ); +- return STATUS_SUCCESS; +-} +- + #else + + NTSTATUS CDECL fast_RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) +@@ -2579,14 +2518,4 @@ NTSTATUS CDECL fast_RtlReleaseSRWLockShared( RTL_SRWLOCK *lock ) + return STATUS_NOT_IMPLEMENTED; + } + +-NTSTATUS CDECL fast_RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable, int count ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-NTSTATUS CDECL fast_wait_cv( RTL_CONDITION_VARIABLE *variable, const void *value, const LARGE_INTEGER *timeout ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- + #endif +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 0f833b5a51b..4462c3ad218 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -99,10 +99,7 @@ extern NTSTATUS CDECL fast_RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock ) DECLS + extern NTSTATUS CDECL fast_RtlAcquireSRWLockShared( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; + extern NTSTATUS CDECL fast_RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; + extern NTSTATUS CDECL fast_RtlReleaseSRWLockShared( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable, int count ) DECLSPEC_HIDDEN; + extern LONGLONG CDECL fast_RtlGetSystemTimePrecise(void) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_wait_cv( RTL_CONDITION_VARIABLE *variable, const void *value, +- const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; + + extern NTSTATUS CDECL get_initial_environment( WCHAR **wargv[], WCHAR *env, SIZE_T *size ) DECLSPEC_HIDDEN; + extern NTSTATUS CDECL get_startup_info( startup_info_t *info, SIZE_T *total_size, SIZE_T *info_size ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index a8dad0d0fcf..6116f408572 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -27,7 +27,7 @@ + struct _DISPATCHER_CONTEXT; + + /* increment this when you change the function table */ +-#define NTDLL_UNIXLIB_VERSION 108 ++#define NTDLL_UNIXLIB_VERSION 109 + + struct unix_funcs + { +@@ -45,9 +45,6 @@ struct unix_funcs + NTSTATUS (CDECL *fast_RtlAcquireSRWLockShared)( RTL_SRWLOCK *lock ); + NTSTATUS (CDECL *fast_RtlReleaseSRWLockExclusive)( RTL_SRWLOCK *lock ); + NTSTATUS (CDECL *fast_RtlReleaseSRWLockShared)( RTL_SRWLOCK *lock ); +- NTSTATUS (CDECL *fast_RtlWakeConditionVariable)( RTL_CONDITION_VARIABLE *variable, int count ); +- NTSTATUS (CDECL *fast_wait_cv)( RTL_CONDITION_VARIABLE *variable, const void *value, +- const LARGE_INTEGER *timeout ); + + /* math functions */ + double (CDECL *atan)( double d ); +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/0013-ntdll-Reimplement-SRW-locks-on-top-of-Win32-futexes.patch b/patches/ntdll-NtAlertThreadByThreadId/0013-ntdll-Reimplement-SRW-locks-on-top-of-Win32-futexes.patch new file mode 100644 index 00000000..02fb66fc --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/0013-ntdll-Reimplement-SRW-locks-on-top-of-Win32-futexes.patch @@ -0,0 +1,856 @@ +From 4450fe4cd6c48fcd3293c7ae0c2836640d383eaa Mon Sep 17 00:00:00 2001 +From: Zebediah Figura +Date: Sun, 22 Nov 2020 20:51:10 -0600 +Subject: [PATCH 13/13] ntdll: Reimplement SRW locks on top of Win32 futexes. + +Signed-off-by: Zebediah Figura +--- + dlls/ntdll/sync.c | 316 +++++++++++++++------------------ + dlls/ntdll/unix/loader.c | 6 - + dlls/ntdll/unix/sync.c | 309 -------------------------------- + dlls/ntdll/unix/unix_private.h | 6 - + dlls/ntdll/unixlib.h | 8 - + include/winbase.h | 24 +++ + 6 files changed, 166 insertions(+), 503 deletions(-) + +diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c +index 133ae6d4ead..f792c8a2fb0 100644 +--- a/dlls/ntdll/sync.c ++++ b/dlls/ntdll/sync.c +@@ -161,127 +161,23 @@ DWORD WINAPI RtlRunOnceExecuteOnce( RTL_RUN_ONCE *once, PRTL_RUN_ONCE_INIT_FN fu + return RtlRunOnceComplete( once, 0, context ? *context : NULL ); + } + +- +-/* SRW locks implementation +- * +- * The memory layout used by the lock is: +- * +- * 32 31 16 0 +- * ________________ ________________ +- * | X| #exclusive | #shared | +- * ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ +- * Since there is no space left for a separate counter of shared access +- * threads inside the locked section the #shared field is used for multiple +- * purposes. The following table lists all possible states the lock can be +- * in, notation: [X, #exclusive, #shared]: +- * +- * [0, 0, N] -> locked by N shared access threads, if N=0 it's unlocked +- * [0, >=1, >=1] -> threads are requesting exclusive locks, but there are +- * still shared access threads inside. #shared should not be incremented +- * anymore! +- * [1, >=1, >=0] -> lock is owned by an exclusive thread and the #shared +- * counter can be used again to count the number of threads waiting in the +- * queue for shared access. +- * +- * the following states are invalid and will never occur: +- * [0, >=1, 0], [1, 0, >=0] +- * +- * The main problem arising from the fact that we have no separate counter +- * of shared access threads inside the locked section is that in the state +- * [0, >=1, >=1] above we cannot add additional waiting threads to the +- * shared access queue - it wouldn't be possible to distinguish waiting +- * threads and those that are still inside. To solve this problem the lock +- * uses the following approach: a thread that isn't able to allocate a +- * shared lock just uses the exclusive queue instead. As soon as the thread +- * is woken up it is in the state [1, >=1, >=0]. In this state it's again +- * possible to use the shared access queue. The thread atomically moves +- * itself to the shared access queue and releases the exclusive lock, so +- * that the "real" exclusive access threads have a chance. As soon as they +- * are all ready the shared access threads are processed. +- */ +- +-#define SRWLOCK_MASK_IN_EXCLUSIVE 0x80000000 +-#define SRWLOCK_MASK_EXCLUSIVE_QUEUE 0x7fff0000 +-#define SRWLOCK_MASK_SHARED_QUEUE 0x0000ffff +-#define SRWLOCK_RES_EXCLUSIVE 0x00010000 +-#define SRWLOCK_RES_SHARED 0x00000001 +- +-#ifdef WORDS_BIGENDIAN +-#define srwlock_key_exclusive(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 1) & ~1)) +-#define srwlock_key_shared(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 3) & ~1)) +-#else +-#define srwlock_key_exclusive(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 3) & ~1)) +-#define srwlock_key_shared(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 1) & ~1)) +-#endif +- +-static inline void srwlock_check_invalid( unsigned int val ) +-{ +- /* Throw exception if it's impossible to acquire/release this lock. */ +- if ((val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) == SRWLOCK_MASK_EXCLUSIVE_QUEUE || +- (val & SRWLOCK_MASK_SHARED_QUEUE) == SRWLOCK_MASK_SHARED_QUEUE) +- RtlRaiseStatus(STATUS_RESOURCE_NOT_OWNED); +-} +- +-static inline unsigned int srwlock_lock_exclusive( unsigned int *dest, int incr ) +-{ +- unsigned int val, tmp; +- /* Atomically modifies the value of *dest by adding incr. If the shared +- * queue is empty and there are threads waiting for exclusive access, then +- * sets the mark SRWLOCK_MASK_IN_EXCLUSIVE to signal other threads that +- * they are allowed again to use the shared queue counter. */ +- for (val = *dest;; val = tmp) +- { +- tmp = val + incr; +- srwlock_check_invalid( tmp ); +- if ((tmp & SRWLOCK_MASK_EXCLUSIVE_QUEUE) && !(tmp & SRWLOCK_MASK_SHARED_QUEUE)) +- tmp |= SRWLOCK_MASK_IN_EXCLUSIVE; +- if ((tmp = InterlockedCompareExchange( (int *)dest, tmp, val )) == val) +- break; +- } +- return val; +-} +- +-static inline unsigned int srwlock_unlock_exclusive( unsigned int *dest, int incr ) +-{ +- unsigned int val, tmp; +- /* Atomically modifies the value of *dest by adding incr. If the queue of +- * threads waiting for exclusive access is empty, then remove the +- * SRWLOCK_MASK_IN_EXCLUSIVE flag (only the shared queue counter will +- * remain). */ +- for (val = *dest;; val = tmp) +- { +- tmp = val + incr; +- srwlock_check_invalid( tmp ); +- if (!(tmp & SRWLOCK_MASK_EXCLUSIVE_QUEUE)) +- tmp &= SRWLOCK_MASK_SHARED_QUEUE; +- if ((tmp = InterlockedCompareExchange( (int *)dest, tmp, val )) == val) +- break; +- } +- return val; +-} +- +-static inline void srwlock_leave_exclusive( RTL_SRWLOCK *lock, unsigned int val ) +-{ +- /* Used when a thread leaves an exclusive section. If there are other +- * exclusive access threads they are processed first, followed by +- * the shared waiters. */ +- if (val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) +- NtReleaseKeyedEvent( 0, srwlock_key_exclusive(lock), FALSE, NULL ); +- else +- { +- val &= SRWLOCK_MASK_SHARED_QUEUE; /* remove SRWLOCK_MASK_IN_EXCLUSIVE */ +- while (val--) +- NtReleaseKeyedEvent( 0, srwlock_key_shared(lock), FALSE, NULL ); +- } +-} +- +-static inline void srwlock_leave_shared( RTL_SRWLOCK *lock, unsigned int val ) +-{ +- /* Wake up one exclusive thread as soon as the last shared access thread +- * has left. */ +- if ((val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) && !(val & SRWLOCK_MASK_SHARED_QUEUE)) +- NtReleaseKeyedEvent( 0, srwlock_key_exclusive(lock), FALSE, NULL ); +-} ++struct srw_lock ++{ ++ short exclusive_waiters; ++ ++ /* Number of shared owners, or -1 if owned exclusive. ++ * ++ * Sadly Windows has no equivalent to FUTEX_WAIT_BITSET, so in order to wake ++ * up *only* exclusive or *only* shared waiters (and thus avoid spurious ++ * wakeups), we need to wait on two different addresses. ++ * RtlAcquireSRWLockShared() needs to know the values of "exclusive_waiters" ++ * and "owners", but RtlAcquireSRWLockExclusive() only needs to know the ++ * value of "owners", so the former can wait on the entire structure, and ++ * the latterwaits only on the "owners" member. Note then that "owners" must be ++ * not be the first element in the structure. */ ++ short owners; ++}; ++C_ASSERT( sizeof(struct srw_lock) == 4 ); + + /*********************************************************************** + * RtlInitializeSRWLock (NTDLL.@) +@@ -308,11 +204,36 @@ void WINAPI RtlInitializeSRWLock( RTL_SRWLOCK *lock ) + */ + void WINAPI RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) + { +- if (unix_funcs->fast_RtlAcquireSRWLockExclusive( lock ) != STATUS_NOT_IMPLEMENTED) +- return; ++ union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock }; ++ ++ InterlockedIncrement16( &u.s->exclusive_waiters ); + +- if (srwlock_lock_exclusive( (unsigned int *)&lock->Ptr, SRWLOCK_RES_EXCLUSIVE )) +- NtWaitForKeyedEvent( 0, srwlock_key_exclusive(lock), FALSE, NULL ); ++ for (;;) ++ { ++ union { struct srw_lock s; LONG l; } old, new; ++ BOOL wait; ++ ++ do ++ { ++ old.s = *u.s; ++ new.s = old.s; ++ ++ if (!old.s.owners) ++ { ++ /* Not locked exclusive or shared. We can try to grab it. */ ++ new.s.owners = -1; ++ --new.s.exclusive_waiters; ++ wait = FALSE; ++ } ++ else ++ { ++ wait = TRUE; ++ } ++ } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l); ++ ++ if (!wait) return; ++ RtlWaitOnAddress( &u.s->owners, &new.s.owners, sizeof(short), NULL ); ++ } + } + + /*********************************************************************** +@@ -324,34 +245,34 @@ void WINAPI RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) + */ + void WINAPI RtlAcquireSRWLockShared( RTL_SRWLOCK *lock ) + { +- unsigned int val, tmp; ++ union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock }; + +- if (unix_funcs->fast_RtlAcquireSRWLockShared( lock ) != STATUS_NOT_IMPLEMENTED) +- return; +- +- /* Acquires a shared lock. If it's currently not possible to add elements to +- * the shared queue, then request exclusive access instead. */ +- for (val = *(unsigned int *)&lock->Ptr;; val = tmp) ++ for (;;) + { +- if ((val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) && !(val & SRWLOCK_MASK_IN_EXCLUSIVE)) +- tmp = val + SRWLOCK_RES_EXCLUSIVE; +- else +- tmp = val + SRWLOCK_RES_SHARED; +- if ((tmp = InterlockedCompareExchange( (int *)&lock->Ptr, tmp, val )) == val) +- break; +- } ++ union { struct srw_lock s; LONG l; } old, new; ++ BOOL wait; + +- /* Drop exclusive access again and instead requeue for shared access. */ +- if ((val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) && !(val & SRWLOCK_MASK_IN_EXCLUSIVE)) +- { +- NtWaitForKeyedEvent( 0, srwlock_key_exclusive(lock), FALSE, NULL ); +- val = srwlock_unlock_exclusive( (unsigned int *)&lock->Ptr, (SRWLOCK_RES_SHARED +- - SRWLOCK_RES_EXCLUSIVE) ) - SRWLOCK_RES_EXCLUSIVE; +- srwlock_leave_exclusive( lock, val ); +- } ++ do ++ { ++ old.s = *u.s; ++ new = old; ++ ++ if (old.s.owners != -1 && !old.s.exclusive_waiters) ++ { ++ /* Not locked exclusive, and no exclusive waiters. ++ * We can try to grab it. */ ++ ++new.s.owners; ++ wait = FALSE; ++ } ++ else ++ { ++ wait = TRUE; ++ } ++ } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l); + +- if (val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) +- NtWaitForKeyedEvent( 0, srwlock_key_shared(lock), FALSE, NULL ); ++ if (!wait) return; ++ RtlWaitOnAddress( u.s, &new.s, sizeof(struct srw_lock), NULL ); ++ } + } + + /*********************************************************************** +@@ -359,11 +280,23 @@ void WINAPI RtlAcquireSRWLockShared( RTL_SRWLOCK *lock ) + */ + void WINAPI RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock ) + { +- if (unix_funcs->fast_RtlReleaseSRWLockExclusive( lock ) != STATUS_NOT_IMPLEMENTED) +- return; ++ union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock }; ++ union { struct srw_lock s; LONG l; } old, new; ++ ++ do ++ { ++ old.s = *u.s; ++ new = old; ++ ++ if (old.s.owners != -1) ERR("Lock %p is not owned exclusive!\n", lock); + +- srwlock_leave_exclusive( lock, srwlock_unlock_exclusive( (unsigned int *)&lock->Ptr, +- - SRWLOCK_RES_EXCLUSIVE ) - SRWLOCK_RES_EXCLUSIVE ); ++ new.s.owners = 0; ++ } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l); ++ ++ if (new.s.exclusive_waiters) ++ RtlWakeAddressSingle( &u.s->owners ); ++ else ++ RtlWakeAddressAll( u.s ); + } + + /*********************************************************************** +@@ -371,11 +304,22 @@ void WINAPI RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock ) + */ + void WINAPI RtlReleaseSRWLockShared( RTL_SRWLOCK *lock ) + { +- if (unix_funcs->fast_RtlReleaseSRWLockShared( lock ) != STATUS_NOT_IMPLEMENTED) +- return; ++ union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock }; ++ union { struct srw_lock s; LONG l; } old, new; ++ ++ do ++ { ++ old.s = *u.s; ++ new = old; ++ ++ if (old.s.owners == -1) ERR("Lock %p is owned exclusive!\n", lock); ++ else if (!old.s.owners) ERR("Lock %p is not owned shared!\n", lock); + +- srwlock_leave_shared( lock, srwlock_lock_exclusive( (unsigned int *)&lock->Ptr, +- - SRWLOCK_RES_SHARED ) - SRWLOCK_RES_SHARED ); ++ --new.s.owners; ++ } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l); ++ ++ if (!new.s.owners) ++ RtlWakeAddressSingle( &u.s->owners ); + } + + /*********************************************************************** +@@ -387,13 +331,28 @@ void WINAPI RtlReleaseSRWLockShared( RTL_SRWLOCK *lock ) + */ + BOOLEAN WINAPI RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) + { +- NTSTATUS ret; ++ union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock }; ++ union { struct srw_lock s; LONG l; } old, new; ++ BOOLEAN ret; + +- if ((ret = unix_funcs->fast_RtlTryAcquireSRWLockExclusive( lock )) != STATUS_NOT_IMPLEMENTED) +- return (ret == STATUS_SUCCESS); ++ do ++ { ++ old.s = *u.s; ++ new.s = old.s; + +- return InterlockedCompareExchange( (int *)&lock->Ptr, SRWLOCK_MASK_IN_EXCLUSIVE | +- SRWLOCK_RES_EXCLUSIVE, 0 ) == 0; ++ if (!old.s.owners) ++ { ++ /* Not locked exclusive or shared. We can try to grab it. */ ++ new.s.owners = -1; ++ ret = TRUE; ++ } ++ else ++ { ++ ret = FALSE; ++ } ++ } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l); ++ ++ return ret; + } + + /*********************************************************************** +@@ -401,20 +360,29 @@ BOOLEAN WINAPI RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) + */ + BOOLEAN WINAPI RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock ) + { +- unsigned int val, tmp; +- NTSTATUS ret; ++ union { RTL_SRWLOCK *rtl; struct srw_lock *s; LONG *l; } u = { lock }; ++ union { struct srw_lock s; LONG l; } old, new; ++ BOOLEAN ret; + +- if ((ret = unix_funcs->fast_RtlTryAcquireSRWLockShared( lock )) != STATUS_NOT_IMPLEMENTED) +- return (ret == STATUS_SUCCESS); +- +- for (val = *(unsigned int *)&lock->Ptr;; val = tmp) ++ do + { +- if (val & SRWLOCK_MASK_EXCLUSIVE_QUEUE) +- return FALSE; +- if ((tmp = InterlockedCompareExchange( (int *)&lock->Ptr, val + SRWLOCK_RES_SHARED, val )) == val) +- break; +- } +- return TRUE; ++ old.s = *u.s; ++ new.s = old.s; ++ ++ if (old.s.owners != -1 && !old.s.exclusive_waiters) ++ { ++ /* Not locked exclusive, and no exclusive waiters. ++ * We can try to grab it. */ ++ ++new.s.owners; ++ ret = TRUE; ++ } ++ else ++ { ++ ret = FALSE; ++ } ++ } while (InterlockedCompareExchange( u.l, new.l, old.l ) != old.l); ++ ++ return ret; + } + + /*********************************************************************** +diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c +index 1633aeb97eb..8a30e6fc749 100644 +--- a/dlls/ntdll/unix/loader.c ++++ b/dlls/ntdll/unix/loader.c +@@ -1511,12 +1511,6 @@ static struct unix_funcs unix_funcs = + NtCurrentTeb, + DbgUiIssueRemoteBreakin, + RtlGetSystemTimePrecise, +- fast_RtlTryAcquireSRWLockExclusive, +- fast_RtlAcquireSRWLockExclusive, +- fast_RtlTryAcquireSRWLockShared, +- fast_RtlAcquireSRWLockShared, +- fast_RtlReleaseSRWLockExclusive, +- fast_RtlReleaseSRWLockShared, + ntdll_atan, + ntdll_ceil, + ntdll_cos, +diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c +index 3645c9f9549..c32b62f9e0a 100644 +--- a/dlls/ntdll/unix/sync.c ++++ b/dlls/ntdll/unix/sync.c +@@ -114,8 +114,6 @@ static inline ULONGLONG monotonic_counter(void) + + #define FUTEX_WAIT 0 + #define FUTEX_WAKE 1 +-#define FUTEX_WAIT_BITSET 9 +-#define FUTEX_WAKE_BITSET 10 + + static int futex_private = 128; + +@@ -129,16 +127,6 @@ static inline int futex_wake( const int *addr, int val ) + return syscall( __NR_futex, addr, FUTEX_WAKE | futex_private, val, NULL, 0, 0 ); + } + +-static inline int futex_wait_bitset( const int *addr, int val, struct timespec *timeout, int mask ) +-{ +- return syscall( __NR_futex, addr, FUTEX_WAIT_BITSET | futex_private, val, timeout, 0, mask ); +-} +- +-static inline int futex_wake_bitset( const int *addr, int val, int mask ) +-{ +- return syscall( __NR_futex, addr, FUTEX_WAKE_BITSET | futex_private, val, NULL, 0, mask ); +-} +- + static inline int use_futexes(void) + { + static int supported = -1; +@@ -156,16 +144,6 @@ static inline int use_futexes(void) + return supported; + } + +-static int *get_futex(void **ptr) +-{ +- if (sizeof(void *) == 8) +- return (int *)((((ULONG_PTR)ptr) + 3) & ~3); +- else if (!(((ULONG_PTR)ptr) & 3)) +- return (int *)ptr; +- else +- return NULL; +-} +- + #endif + + +@@ -2232,290 +2210,3 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG + return NtWaitForSingleObject( ntdll_get_thread_data()->tid_alert_event, FALSE, timeout ); + #endif + } +- +- +-#ifdef __linux__ +- +-/* Futex-based SRW lock implementation: +- * +- * Since we can rely on the kernel to release all threads and don't need to +- * worry about NtReleaseKeyedEvent(), we can simplify the layout a bit. The +- * layout looks like this: +- * +- * 31 - Exclusive lock bit, set if the resource is owned exclusively. +- * 30-16 - Number of exclusive waiters. Unlike the fallback implementation, +- * this does not include the thread owning the lock, or shared threads +- * waiting on the lock. +- * 15 - Does this lock have any shared waiters? We use this as an +- * optimization to avoid unnecessary FUTEX_WAKE_BITSET calls when +- * releasing an exclusive lock. +- * 14-0 - Number of shared owners. Unlike the fallback implementation, this +- * does not include the number of shared threads waiting on the lock. +- * Thus the state [1, x, >=1] will never occur. +- */ +- +-#define SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT 0x80000000 +-#define SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK 0x7fff0000 +-#define SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_INC 0x00010000 +-#define SRWLOCK_FUTEX_SHARED_WAITERS_BIT 0x00008000 +-#define SRWLOCK_FUTEX_SHARED_OWNERS_MASK 0x00007fff +-#define SRWLOCK_FUTEX_SHARED_OWNERS_INC 0x00000001 +- +-/* Futex bitmasks; these are independent from the bits in the lock itself. */ +-#define SRWLOCK_FUTEX_BITSET_EXCLUSIVE 1 +-#define SRWLOCK_FUTEX_BITSET_SHARED 2 +- +-NTSTATUS CDECL fast_RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) +-{ +- int old, new, *futex; +- NTSTATUS ret; +- +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- +- if (!(futex = get_futex( &lock->Ptr ))) +- return STATUS_NOT_IMPLEMENTED; +- +- do +- { +- old = *futex; +- +- if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT) +- && !(old & SRWLOCK_FUTEX_SHARED_OWNERS_MASK)) +- { +- /* Not locked exclusive or shared. We can try to grab it. */ +- new = old | SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT; +- ret = STATUS_SUCCESS; +- } +- else +- { +- new = old; +- ret = STATUS_TIMEOUT; +- } +- } while (InterlockedCompareExchange( futex, new, old ) != old); +- +- return ret; +-} +- +-NTSTATUS CDECL fast_RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) +-{ +- int old, new, *futex; +- BOOLEAN wait; +- +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- +- if (!(futex = get_futex( &lock->Ptr ))) +- return STATUS_NOT_IMPLEMENTED; +- +- /* Atomically increment the exclusive waiter count. */ +- do +- { +- old = *futex; +- new = old + SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_INC; +- assert(new & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK); +- } while (InterlockedCompareExchange( futex, new, old ) != old); +- +- for (;;) +- { +- do +- { +- old = *futex; +- +- if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT) +- && !(old & SRWLOCK_FUTEX_SHARED_OWNERS_MASK)) +- { +- /* Not locked exclusive or shared. We can try to grab it. */ +- new = old | SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT; +- assert(old & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK); +- new -= SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_INC; +- wait = FALSE; +- } +- else +- { +- new = old; +- wait = TRUE; +- } +- } while (InterlockedCompareExchange( futex, new, old ) != old); +- +- if (!wait) +- return STATUS_SUCCESS; +- +- futex_wait_bitset( futex, new, NULL, SRWLOCK_FUTEX_BITSET_EXCLUSIVE ); +- } +- +- return STATUS_SUCCESS; +-} +- +-NTSTATUS CDECL fast_RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock ) +-{ +- int new, old, *futex; +- NTSTATUS ret; +- +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- +- if (!(futex = get_futex( &lock->Ptr ))) +- return STATUS_NOT_IMPLEMENTED; +- +- do +- { +- old = *futex; +- +- if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT) +- && !(old & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK)) +- { +- /* Not locked exclusive, and no exclusive waiters. We can try to +- * grab it. */ +- new = old + SRWLOCK_FUTEX_SHARED_OWNERS_INC; +- assert(new & SRWLOCK_FUTEX_SHARED_OWNERS_MASK); +- ret = STATUS_SUCCESS; +- } +- else +- { +- new = old; +- ret = STATUS_TIMEOUT; +- } +- } while (InterlockedCompareExchange( futex, new, old ) != old); +- +- return ret; +-} +- +-NTSTATUS CDECL fast_RtlAcquireSRWLockShared( RTL_SRWLOCK *lock ) +-{ +- int old, new, *futex; +- BOOLEAN wait; +- +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- +- if (!(futex = get_futex( &lock->Ptr ))) +- return STATUS_NOT_IMPLEMENTED; +- +- for (;;) +- { +- do +- { +- old = *futex; +- +- if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT) +- && !(old & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK)) +- { +- /* Not locked exclusive, and no exclusive waiters. We can try +- * to grab it. */ +- new = old + SRWLOCK_FUTEX_SHARED_OWNERS_INC; +- assert(new & SRWLOCK_FUTEX_SHARED_OWNERS_MASK); +- wait = FALSE; +- } +- else +- { +- new = old | SRWLOCK_FUTEX_SHARED_WAITERS_BIT; +- wait = TRUE; +- } +- } while (InterlockedCompareExchange( futex, new, old ) != old); +- +- if (!wait) +- return STATUS_SUCCESS; +- +- futex_wait_bitset( futex, new, NULL, SRWLOCK_FUTEX_BITSET_SHARED ); +- } +- +- return STATUS_SUCCESS; +-} +- +-NTSTATUS CDECL fast_RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock ) +-{ +- int old, new, *futex; +- +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- +- if (!(futex = get_futex( &lock->Ptr ))) +- return STATUS_NOT_IMPLEMENTED; +- +- do +- { +- old = *futex; +- +- if (!(old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT)) +- { +- ERR("Lock %p is not owned exclusive! (%#x)\n", lock, *futex); +- return STATUS_RESOURCE_NOT_OWNED; +- } +- +- new = old & ~SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT; +- +- if (!(new & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK)) +- new &= ~SRWLOCK_FUTEX_SHARED_WAITERS_BIT; +- } while (InterlockedCompareExchange( futex, new, old ) != old); +- +- if (new & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK) +- futex_wake_bitset( futex, 1, SRWLOCK_FUTEX_BITSET_EXCLUSIVE ); +- else if (old & SRWLOCK_FUTEX_SHARED_WAITERS_BIT) +- futex_wake_bitset( futex, INT_MAX, SRWLOCK_FUTEX_BITSET_SHARED ); +- +- return STATUS_SUCCESS; +-} +- +-NTSTATUS CDECL fast_RtlReleaseSRWLockShared( RTL_SRWLOCK *lock ) +-{ +- int old, new, *futex; +- +- if (!use_futexes()) return STATUS_NOT_IMPLEMENTED; +- +- if (!(futex = get_futex( &lock->Ptr ))) +- return STATUS_NOT_IMPLEMENTED; +- +- do +- { +- old = *futex; +- +- if (old & SRWLOCK_FUTEX_EXCLUSIVE_LOCK_BIT) +- { +- ERR("Lock %p is owned exclusive! (%#x)\n", lock, *futex); +- return STATUS_RESOURCE_NOT_OWNED; +- } +- else if (!(old & SRWLOCK_FUTEX_SHARED_OWNERS_MASK)) +- { +- ERR("Lock %p is not owned shared! (%#x)\n", lock, *futex); +- return STATUS_RESOURCE_NOT_OWNED; +- } +- +- new = old - SRWLOCK_FUTEX_SHARED_OWNERS_INC; +- } while (InterlockedCompareExchange( futex, new, old ) != old); +- +- /* Optimization: only bother waking if there are actually exclusive waiters. */ +- if (!(new & SRWLOCK_FUTEX_SHARED_OWNERS_MASK) && (new & SRWLOCK_FUTEX_EXCLUSIVE_WAITERS_MASK)) +- futex_wake_bitset( futex, 1, SRWLOCK_FUTEX_BITSET_EXCLUSIVE ); +- +- return STATUS_SUCCESS; +-} +- +-#else +- +-NTSTATUS CDECL fast_RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-NTSTATUS CDECL fast_RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-NTSTATUS CDECL fast_RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-NTSTATUS CDECL fast_RtlAcquireSRWLockShared( RTL_SRWLOCK *lock ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-NTSTATUS CDECL fast_RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-NTSTATUS CDECL fast_RtlReleaseSRWLockShared( RTL_SRWLOCK *lock ) +-{ +- return STATUS_NOT_IMPLEMENTED; +-} +- +-#endif +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 4462c3ad218..fe6813551e2 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -93,12 +93,6 @@ extern void (WINAPI *pKiUserApcDispatcher)(CONTEXT*,ULONG_PTR,ULONG_PTR,ULON + extern NTSTATUS (WINAPI *pKiUserExceptionDispatcher)(EXCEPTION_RECORD*,CONTEXT*) DECLSPEC_HIDDEN; + extern void (WINAPI *pLdrInitializeThunk)(CONTEXT*,void**,ULONG_PTR,ULONG_PTR) DECLSPEC_HIDDEN; + extern void (WINAPI *pRtlUserThreadStart)( PRTL_THREAD_START_ROUTINE entry, void *arg ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlTryAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlAcquireSRWLockExclusive( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlAcquireSRWLockShared( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlReleaseSRWLockExclusive( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; +-extern NTSTATUS CDECL fast_RtlReleaseSRWLockShared( RTL_SRWLOCK *lock ) DECLSPEC_HIDDEN; + extern LONGLONG CDECL fast_RtlGetSystemTimePrecise(void) DECLSPEC_HIDDEN; + + extern NTSTATUS CDECL get_initial_environment( WCHAR **wargv[], WCHAR *env, SIZE_T *size ) DECLSPEC_HIDDEN; +diff --git a/dlls/ntdll/unixlib.h b/dlls/ntdll/unixlib.h +index 6116f408572..5ffb09ce397 100644 +--- a/dlls/ntdll/unixlib.h ++++ b/dlls/ntdll/unixlib.h +@@ -38,14 +38,6 @@ struct unix_funcs + NTSTATUS (WINAPI *DbgUiIssueRemoteBreakin)( HANDLE process ); + LONGLONG (WINAPI *RtlGetSystemTimePrecise)(void); + +- /* fast locks */ +- NTSTATUS (CDECL *fast_RtlTryAcquireSRWLockExclusive)( RTL_SRWLOCK *lock ); +- NTSTATUS (CDECL *fast_RtlAcquireSRWLockExclusive)( RTL_SRWLOCK *lock ); +- NTSTATUS (CDECL *fast_RtlTryAcquireSRWLockShared)( RTL_SRWLOCK *lock ); +- NTSTATUS (CDECL *fast_RtlAcquireSRWLockShared)( RTL_SRWLOCK *lock ); +- NTSTATUS (CDECL *fast_RtlReleaseSRWLockExclusive)( RTL_SRWLOCK *lock ); +- NTSTATUS (CDECL *fast_RtlReleaseSRWLockShared)( RTL_SRWLOCK *lock ); +- + /* math functions */ + double (CDECL *atan)( double d ); + double (CDECL *ceil)( double d ); +diff --git a/include/winbase.h b/include/winbase.h +index dc8aa081be4..2ebcb3e735a 100644 +--- a/include/winbase.h ++++ b/include/winbase.h +@@ -2928,14 +2928,18 @@ extern WCHAR * CDECL wine_get_dos_file_name( LPCSTR str ); + #pragma intrinsic(_InterlockedExchange) + #pragma intrinsic(_InterlockedExchangeAdd) + #pragma intrinsic(_InterlockedIncrement) ++#pragma intrinsic(_InterlockedIncrement16) + #pragma intrinsic(_InterlockedDecrement) ++#pragma intrinsic(_InterlockedDecrement16) + + long _InterlockedCompareExchange(long volatile*,long,long); + long long _InterlockedCompareExchange64(long long volatile*,long long,long long); + long _InterlockedDecrement(long volatile*); ++short _InterlockedDecrement16(short volatile*); + long _InterlockedExchange(long volatile*,long); + long _InterlockedExchangeAdd(long volatile*,long); + long _InterlockedIncrement(long volatile*); ++short _InterlockedIncrement16(short volatile *); + + static FORCEINLINE LONG WINAPI InterlockedCompareExchange( LONG volatile *dest, LONG xchg, LONG compare ) + { +@@ -2962,11 +2966,21 @@ static FORCEINLINE LONG WINAPI InterlockedIncrement( LONG volatile *dest ) + return _InterlockedIncrement( (long volatile *)dest ); + } + ++static FORCEINLINE short WINAPI InterlockedIncrement16( short volatile *dest ) ++{ ++ return _InterlockedIncrement16( (short volatile *)dest ); ++} ++ + static FORCEINLINE LONG WINAPI InterlockedDecrement( LONG volatile *dest ) + { + return _InterlockedDecrement( (long volatile *)dest ); + } + ++static FORCEINLINE short WINAPI InterlockedDecrement16( short volatile *dest ) ++{ ++ return _InterlockedDecrement16( (short volatile *)dest ); ++} ++ + #ifndef __i386__ + + #pragma intrinsic(_InterlockedCompareExchangePointer) +@@ -3031,11 +3045,21 @@ static FORCEINLINE LONG WINAPI InterlockedIncrement( LONG volatile *dest ) + return __sync_add_and_fetch( dest, 1 ); + } + ++static FORCEINLINE short WINAPI InterlockedIncrement16( short volatile *dest ) ++{ ++ return __sync_add_and_fetch( dest, 1 ); ++} ++ + static FORCEINLINE LONG WINAPI InterlockedDecrement( LONG volatile *dest ) + { + return __sync_add_and_fetch( dest, -1 ); + } + ++static FORCEINLINE short WINAPI InterlockedDecrement16( short volatile *dest ) ++{ ++ return __sync_add_and_fetch( dest, -1 ); ++} ++ + static FORCEINLINE PVOID WINAPI InterlockedExchangePointer( PVOID volatile *dest, PVOID val ) + { + PVOID ret; +-- +2.29.2 + diff --git a/patches/ntdll-NtAlertThreadByThreadId/definition b/patches/ntdll-NtAlertThreadByThreadId/definition new file mode 100644 index 00000000..92b7cb18 --- /dev/null +++ b/patches/ntdll-NtAlertThreadByThreadId/definition @@ -0,0 +1,2 @@ +Fixes: [50292] Process-local synchronization objects use private interfaces into the Unix library +Depends: server-Object_Types diff --git a/patches/patchinstall.sh b/patches/patchinstall.sh index 901fda5a..9a8e65e5 100755 --- a/patches/patchinstall.sh +++ b/patches/patchinstall.sh @@ -169,6 +169,7 @@ patch_enable_all () enable_ntdll_Junction_Points="$1" enable_ntdll_Manifest_Range="$1" enable_ntdll_NtAccessCheck="$1" + enable_ntdll_NtAlertThreadByThreadId="$1" enable_ntdll_NtDevicePath="$1" enable_ntdll_NtQueryEaFile="$1" enable_ntdll_NtQuerySection="$1" @@ -589,6 +590,9 @@ patch_enable () ntdll-NtAccessCheck) enable_ntdll_NtAccessCheck="$2" ;; + ntdll-NtAlertThreadByThreadId) + enable_ntdll_NtAlertThreadByThreadId="$2" + ;; ntdll-NtDevicePath) enable_ntdll_NtDevicePath="$2" ;; @@ -1561,6 +1565,13 @@ if test "$enable_ntdll_NtDevicePath" -eq 1; then enable_ntdll_Pipe_SpecialCharacters=1 fi +if test "$enable_ntdll_Dealloc_Thread_Stack" -eq 1; then + if test "$enable_ntdll_NtAlertThreadByThreadId" -gt 1; then + abort "Patchset ntdll-NtAlertThreadByThreadId disabled, but ntdll-Dealloc_Thread_Stack depends on that." + fi + enable_ntdll_NtAlertThreadByThreadId=1 +fi + if test "$enable_ntdll_Builtin_Prot" -eq 1; then if test "$enable_ntdll_WRITECOPY" -gt 1; then abort "Patchset ntdll-WRITECOPY disabled, but ntdll-Builtin_Prot depends on that." @@ -1661,6 +1672,20 @@ if test "$enable_nvapi_Stub_DLL" -eq 1; then enable_nvcuda_CUDA_Support=1 fi +if test "$enable_Staging" -eq 1; then + if test "$enable_ntdll_NtAlertThreadByThreadId" -gt 1; then + abort "Patchset ntdll-NtAlertThreadByThreadId disabled, but Staging depends on that." + fi + enable_ntdll_NtAlertThreadByThreadId=1 +fi + +if test "$enable_ntdll_NtAlertThreadByThreadId" -eq 1; then + if test "$enable_server_Object_Types" -gt 1; then + abort "Patchset server-Object_Types disabled, but ntdll-NtAlertThreadByThreadId depends on that." + fi + enable_server_Object_Types=1 +fi + # Patchset Compiler_Warnings # | @@ -1699,14 +1724,69 @@ if test "$enable_Pipelight" -eq 1; then patch_apply Pipelight/0004-winex11.drv-Indicate-direct-rendering-through-OpenGL.patch fi -# Patchset Staging +# Patchset server-Object_Types +# | +# | This patchset fixes the following Wine bugs: +# | * [#44629] Process Hacker can't enumerate handles +# | * [#45374] Yet Another Process Monitor (.NET 2.0 app) reports System.AccessViolationException # | # | Modified files: -# | * Makefile.in, dlls/ntdll/Makefile.in, dlls/ntdll/loader.c +# | * dlls/ntdll/tests/info.c, dlls/ntdll/tests/om.c, dlls/ntdll/unix/file.c, dlls/ntdll/unix/system.c, include/winternl.h, +# | server/completion.c, server/directory.c, server/event.c, server/file.c, server/handle.c, server/mailslot.c, +# | server/main.c, server/mapping.c, server/mutex.c, server/named_pipe.c, server/object.c, server/object.h, +# | server/process.c, server/protocol.def, server/registry.c, server/semaphore.c, server/symlink.c, server/thread.c, +# | server/timer.c, server/token.c, server/winstation.c +# | +if test "$enable_server_Object_Types" -eq 1; then + patch_apply server-Object_Types/0001-ntdll-Implement-SystemExtendedHandleInformation-in-N.patch + patch_apply server-Object_Types/0002-ntdll-Implement-ObjectTypesInformation-in-NtQueryObj.patch + patch_apply server-Object_Types/0003-server-Register-types-during-startup.patch + patch_apply server-Object_Types/0004-server-Rename-ObjectType-to-Type.patch + patch_apply server-Object_Types/0008-ntdll-Set-TypeIndex-for-ObjectTypeInformation-in-NtQ.patch + patch_apply server-Object_Types/0009-ntdll-Set-object-type-for-System-Extended-HandleInfo.patch + patch_apply server-Object_Types/0010-ntdll-Mimic-object-type-behavior-for-different-windo.patch +fi + +# Patchset ntdll-NtAlertThreadByThreadId +# | +# | This patchset has the following (direct or indirect) dependencies: +# | * server-Object_Types +# | +# | This patchset fixes the following Wine bugs: +# | * [#50292] Process-local synchronization objects use private interfaces into the Unix library +# | +# | Modified files: +# | * dlls/ntdll/Makefile.in, dlls/ntdll/critsection.c, dlls/ntdll/loader.c, dlls/ntdll/ntdll.spec, dlls/ntdll/ntdll_misc.h, +# | dlls/ntdll/sync.c, dlls/ntdll/tests/Makefile.in, dlls/ntdll/tests/om.c, dlls/ntdll/tests/sync.c, +# | dlls/ntdll/unix/loader.c, dlls/ntdll/unix/sync.c, dlls/ntdll/unix/thread.c, dlls/ntdll/unix/unix_private.h, +# | dlls/ntdll/unix/virtual.c, dlls/ntdll/unixlib.h, include/winbase.h, include/winternl.h +# | +if test "$enable_ntdll_NtAlertThreadByThreadId" -eq 1; then + patch_apply ntdll-NtAlertThreadByThreadId/0001-ntdll-tests-Move-some-tests-to-a-new-sync.c-file.patch + patch_apply ntdll-NtAlertThreadByThreadId/0002-ntdll-tests-Add-some-tests-for-Rtl-resources.patch + patch_apply ntdll-NtAlertThreadByThreadId/0003-ntdll-Use-a-separate-mutex-to-lock-the-TEB-list.patch + patch_apply ntdll-NtAlertThreadByThreadId/0004-ntdll-Implement-NtAlertThreadByThreadId-and-NtWaitFo.patch + patch_apply ntdll-NtAlertThreadByThreadId/0005-ntdll-tests-Add-basic-tests-for-thread-id-alert-func.patch + patch_apply ntdll-NtAlertThreadByThreadId/0006-ntdll-Implement-thread-id-alerts-on-top-of-futexes-i.patch + patch_apply ntdll-NtAlertThreadByThreadId/0007-ntdll-Implement-thread-id-alerts-on-top-of-Mach-sema.patch + patch_apply ntdll-NtAlertThreadByThreadId/0008-ntdll-Use-a-separate-lock-to-access-the-PE-TEB-list.patch + patch_apply ntdll-NtAlertThreadByThreadId/0009-ntdll-Reimplement-Win32-futexes-on-top-of-thread-ID-.patch + patch_apply ntdll-NtAlertThreadByThreadId/0010-ntdll-Merge-critsection.c-into-sync.c.patch + patch_apply ntdll-NtAlertThreadByThreadId/0011-ntdll-Reimplement-the-critical-section-fast-path-on-.patch + patch_apply ntdll-NtAlertThreadByThreadId/0012-ntdll-Get-rid-of-the-direct-futex-path-for-condition.patch + patch_apply ntdll-NtAlertThreadByThreadId/0013-ntdll-Reimplement-SRW-locks-on-top-of-Win32-futexes.patch +fi + +# Patchset Staging +# | +# | This patchset has the following (direct or indirect) dependencies: +# | * server-Object_Types, ntdll-NtAlertThreadByThreadId +# | +# | Modified files: +# | * dlls/ntdll/loader.c # | if test "$enable_Staging" -eq 1; then patch_apply Staging/0001-kernel32-Add-winediag-message-to-show-warning-that-t.patch - patch_apply Staging/0002-winelib-Append-Staging-at-the-end-of-the-version-s.patch fi # Patchset advapi32-LsaLookupPrivilegeName @@ -3029,6 +3109,9 @@ fi # Patchset ntdll-Dealloc_Thread_Stack # | +# | This patchset has the following (direct or indirect) dependencies: +# | * server-Object_Types, ntdll-NtAlertThreadByThreadId +# | # | Modified files: # | * dlls/ntdll/unix/unix_private.h, dlls/ntdll/unix/virtual.c # | @@ -3579,29 +3662,6 @@ if test "$enable_server_Key_State" -eq 1; then patch_apply server-Key_State/0002-server-Implement-locking-and-synchronization-of-keys.patch fi -# Patchset server-Object_Types -# | -# | This patchset fixes the following Wine bugs: -# | * [#44629] Process Hacker can't enumerate handles -# | * [#45374] Yet Another Process Monitor (.NET 2.0 app) reports System.AccessViolationException -# | -# | Modified files: -# | * dlls/ntdll/tests/info.c, dlls/ntdll/tests/om.c, dlls/ntdll/unix/file.c, dlls/ntdll/unix/system.c, include/winternl.h, -# | server/completion.c, server/directory.c, server/event.c, server/file.c, server/handle.c, server/mailslot.c, -# | server/main.c, server/mapping.c, server/mutex.c, server/named_pipe.c, server/object.c, server/object.h, -# | server/process.c, server/protocol.def, server/registry.c, server/semaphore.c, server/symlink.c, server/thread.c, -# | server/timer.c, server/token.c, server/winstation.c -# | -if test "$enable_server_Object_Types" -eq 1; then - patch_apply server-Object_Types/0001-ntdll-Implement-SystemExtendedHandleInformation-in-N.patch - patch_apply server-Object_Types/0002-ntdll-Implement-ObjectTypesInformation-in-NtQueryObj.patch - patch_apply server-Object_Types/0003-server-Register-types-during-startup.patch - patch_apply server-Object_Types/0004-server-Rename-ObjectType-to-Type.patch - patch_apply server-Object_Types/0008-ntdll-Set-TypeIndex-for-ObjectTypeInformation-in-NtQ.patch - patch_apply server-Object_Types/0009-ntdll-Set-object-type-for-System-Extended-HandleInfo.patch - patch_apply server-Object_Types/0010-ntdll-Mimic-object-type-behavior-for-different-windo.patch -fi - # Patchset server-Registry_Notifications # | # | Modified files: