Signed-off-by: Zebediah Figura z.figura12@gmail.com --- This is not particularly pretty, but the structure of server_select() doesn't easily lend itself to helper methods. I'm open to suggestions.
dlls/ntdll/ntdll_misc.h | 2 ++ dlls/ntdll/server.c | 4 +-- dlls/ntdll/sync.c | 86 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 70 insertions(+), 22 deletions(-)
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index fdb443b45c..1630978f0a 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -104,6 +104,8 @@ extern int server_pipe( int fd[2] ) DECLSPEC_HIDDEN; extern NTSTATUS alloc_object_attributes( const OBJECT_ATTRIBUTES *attr, struct object_attributes **ret, data_size_t *ret_len ) DECLSPEC_HIDDEN; extern NTSTATUS validate_open_object_attributes( const OBJECT_ATTRIBUTES *attr ) DECLSPEC_HIDDEN; +extern int wait_select_reply( void *cookie ) DECLSPEC_HIDDEN; +extern BOOL invoke_apc( const apc_call_t *call, apc_result_t *result ) DECLSPEC_HIDDEN;
/* module handling */ extern LIST_ENTRY tls_links DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c index ec034455aa..38356f4674 100644 --- a/dlls/ntdll/server.c +++ b/dlls/ntdll/server.c @@ -349,7 +349,7 @@ void server_leave_uninterrupted_section( RTL_CRITICAL_SECTION *cs, sigset_t *sig * * Wait for a reply on the waiting pipe of the current thread. */ -static int wait_select_reply( void *cookie ) +int wait_select_reply( void *cookie ) { int signaled; struct wake_up_reply reply; @@ -386,7 +386,7 @@ static int wait_select_reply( void *cookie ) * * Invoke a single APC. Return TRUE if a user APC has been run. */ -static BOOL invoke_apc( const apc_call_t *call, apc_result_t *result ) +BOOL invoke_apc( const apc_call_t *call, apc_result_t *result ) { BOOL user_apc = FALSE; SIZE_T size; diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 0c373a4c88..e1a48d7044 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -1961,35 +1961,81 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, return status; }
-/*********************************************************************** - * RtlWaitOnAddress (NTDLL.@) - */ -NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size, - const LARGE_INTEGER *timeout ) +static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size ) { switch (size) { case 1: - if (*(const UCHAR *)addr != *(const UCHAR *)cmp) - return STATUS_SUCCESS; - break; + return (*(const UCHAR *)addr == *(const UCHAR *)cmp); case 2: - if (*(const USHORT *)addr != *(const USHORT *)cmp) - return STATUS_SUCCESS; - break; + return (*(const USHORT *)addr == *(const USHORT *)cmp); case 4: - if (*(const ULONG *)addr != *(const ULONG *)cmp) - return STATUS_SUCCESS; - break; + return (*(const ULONG *)addr == *(const ULONG *)cmp); case 8: - if (*(const ULONG64 *)addr != *(const ULONG64 *)cmp) - return STATUS_SUCCESS; - break; - default: - return STATUS_INVALID_PARAMETER; + return (*(const ULONG64 *)addr == *(const ULONG64 *)cmp); }
- return NtWaitForKeyedEvent( 0, addr, 0, timeout ); + return FALSE; +} + +/*********************************************************************** + * 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; + int cookie; + BOOL user_apc = FALSE; + obj_handle_t apc_handle = 0; + apc_call_t call; + apc_result_t result; + timeout_t abs_timeout = timeout ? timeout->QuadPart : TIMEOUT_INFINITE; + + if (size != 1 && size != 2 && size != 4 && size != 8) + return STATUS_INVALID_PARAMETER; + + 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 ); + + memset( &result, 0, sizeof(result) ); + + for (;;) + { + if (!compare_addr( addr, cmp, size )) + return STATUS_SUCCESS; + + SERVER_START_REQ( select ) + { + req->flags = SELECT_INTERRUPTIBLE; + req->cookie = wine_server_client_ptr( &cookie ); + req->prev_apc = apc_handle; + req->timeout = abs_timeout; + wine_server_add_data( req, &result, sizeof(result) ); + wine_server_add_data( req, &select_op, sizeof(select_op.keyed_event) ); + ret = wine_server_call( req ); + abs_timeout = reply->timeout; + apc_handle = reply->apc_handle; + call = reply->call; + } + SERVER_END_REQ; + if (ret == STATUS_PENDING) ret = wait_select_reply( &cookie ); + if (ret != STATUS_USER_APC) break; + if (invoke_apc( &call, &result )) + { + /* if we ran a user apc we have to check once more if additional apcs are queued, + * but we don't want to wait */ + abs_timeout = 0; + user_apc = TRUE; + size = 0; + } + } + + if (ret == STATUS_TIMEOUT && user_apc) ret = STATUS_USER_APC; + + return ret; }
/***********************************************************************
Currently a wake may occur between calling compare_addr() and performing the select request; in that case the thread will never be woken. Prevent this by taking a CS around both operations.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46099 Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/sync.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index e1a48d7044..62cb3cd5ec 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -1961,6 +1961,15 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable, return status; }
+static RTL_CRITICAL_SECTION addr_section; +static RTL_CRITICAL_SECTION_DEBUG addr_section_debug = +{ + 0, 0, &addr_section, + { &addr_section_debug.ProcessLocksList, &addr_section_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": addr_section") } +}; +static RTL_CRITICAL_SECTION addr_section = { &addr_section_debug, -1, 0, 0, 0, 0 }; + static BOOL compare_addr( const void *addr, const void *cmp, SIZE_T size ) { switch (size) @@ -2004,8 +2013,12 @@ NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size
for (;;) { + RtlEnterCriticalSection( &addr_section ); if (!compare_addr( addr, cmp, size )) + { + RtlLeaveCriticalSection( &addr_section ); return STATUS_SUCCESS; + }
SERVER_START_REQ( select ) { @@ -2021,6 +2034,9 @@ NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size call = reply->call; } SERVER_END_REQ; + + RtlLeaveCriticalSection( &addr_section ); + if (ret == STATUS_PENDING) ret = wait_select_reply( &cookie ); if (ret != STATUS_USER_APC) break; if (invoke_apc( &call, &result )) @@ -2043,7 +2059,9 @@ NTSTATUS WINAPI RtlWaitOnAddress( const void *addr, const void *cmp, SIZE_T size */ void WINAPI RtlWakeAddressAll( const void *addr ) { + RtlEnterCriticalSection( &addr_section ); while (NtReleaseKeyedEvent( 0, addr, 0, &zero_timeout ) == STATUS_SUCCESS) {} + RtlLeaveCriticalSection( &addr_section ); }
/*********************************************************************** @@ -2051,5 +2069,7 @@ void WINAPI RtlWakeAddressAll( const void *addr ) */ void WINAPI RtlWakeAddressSingle( const void *addr ) { + RtlEnterCriticalSection( &addr_section ); NtReleaseKeyedEvent( 0, addr, 0, &zero_timeout ); + RtlLeaveCriticalSection( &addr_section ); }