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; }
/***********************************************************************