diff --git a/dlls/ntdll/critsection.c b/dlls/ntdll/critsection.c index 6f0b5d5f1ef..40327452760 100644 --- a/dlls/ntdll/critsection.c +++ b/dlls/ntdll/critsection.c @@ -235,7 +235,7 @@ static inline NTSTATUS wait_semaphore( RTL_CRITICAL_SECTION *crit, int timeout ) time.QuadPart = timeout * (LONGLONG)-10000000; select_op.wait.op = SELECT_WAIT; select_op.wait.handles[0] = wine_server_obj_handle( sem ); - ret = NTDLL_wait_for_multiple_objects( &select_op, offsetof( select_op_t, wait.handles[1] ), 0, &time, 0 ); + ret = NTDLL_wait_for_multiple_objects( &select_op, offsetof( select_op_t, wait.handles[1] ), 0, &time ); } return ret; } diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c index 99fac411229..319257169d4 100644 --- a/dlls/ntdll/exception.c +++ b/dlls/ntdll/exception.c @@ -81,7 +81,7 @@ void wait_suspend( CONTEXT *context ) /* wait with 0 timeout, will only return once the thread is no longer suspended */ timeout.QuadPart = 0; - NTDLL_wait_for_multiple_objects( NULL, 0, SELECT_INTERRUPTIBLE, &timeout, 0 ); + NTDLL_wait_for_multiple_objects( NULL, 0, SELECT_INTERRUPTIBLE, &timeout ); /* retrieve the new context */ SERVER_START_REQ( get_suspend_context ) @@ -134,7 +134,7 @@ NTSTATUS send_debug_event( EXCEPTION_RECORD *rec, int first_chance, CONTEXT *con select_op.wait.op = SELECT_WAIT; select_op.wait.handles[0] = handle; - NTDLL_wait_for_multiple_objects( &select_op, offsetof( select_op_t, wait.handles[1] ), SELECT_INTERRUPTIBLE, NULL, 0 ); + NTDLL_wait_for_multiple_objects( &select_op, offsetof( select_op_t, wait.handles[1] ), SELECT_INTERRUPTIBLE, NULL ); SERVER_START_REQ( get_exception_status ) { diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 225121f2b3b..20d4d301e67 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -63,8 +63,8 @@ extern LPCSTR debugstr_us( const UNICODE_STRING *str ) DECLSPEC_HIDDEN; extern LPCSTR debugstr_ObjectAttributes(const OBJECT_ATTRIBUTES *oa) DECLSPEC_HIDDEN; extern NTSTATUS NTDLL_queue_process_apc( HANDLE process, const apc_call_t *call, apc_result_t *result ) DECLSPEC_HIDDEN; -extern NTSTATUS NTDLL_wait_for_multiple_objects( const select_op_t *select_op, data_size_t size, UINT flags, - const LARGE_INTEGER *timeout, HANDLE signal_object ) DECLSPEC_HIDDEN; +extern NTSTATUS NTDLL_wait_for_multiple_objects( const select_op_t *select_op, data_size_t size, + UINT flags, const LARGE_INTEGER *timeout ) DECLSPEC_HIDDEN; /* init routines */ extern NTSTATUS signal_alloc_thread( TEB **teb ) DECLSPEC_HIDDEN; diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index df6e48e4430..15652ce5abf 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -1107,7 +1107,7 @@ NTSTATUS NTDLL_queue_process_apc( HANDLE process, const apc_call_t *call, apc_re * Implementation of NtWaitForMultipleObjects */ NTSTATUS NTDLL_wait_for_multiple_objects( const select_op_t *select_op, data_size_t size, UINT flags, - const LARGE_INTEGER *timeout, HANDLE signal_object ) + const LARGE_INTEGER *timeout ) { NTSTATUS ret; int cookie; @@ -1125,7 +1125,6 @@ NTSTATUS NTDLL_wait_for_multiple_objects( const select_op_t *select_op, data_siz { req->flags = flags; req->cookie = wine_server_client_ptr( &cookie ); - req->signal = wine_server_obj_handle( signal_object ); req->prev_apc = apc_handle; req->timeout = abs_timeout; wine_server_add_data( req, &result, sizeof(result) ); @@ -1145,7 +1144,10 @@ NTSTATUS NTDLL_wait_for_multiple_objects( const select_op_t *select_op, data_siz abs_timeout = 0; user_apc = TRUE; } - signal_object = 0; /* don't signal it multiple times */ + + /* don't signal multiple times */ + if (size >= sizeof(select_op->signal_and_wait) && select_op->op == SELECT_SIGNAL_AND_WAIT) + size = offsetof( select_op_t, signal_and_wait.signal ); } if (ret == STATUS_TIMEOUT && user_apc) ret = STATUS_USER_APC; @@ -1176,7 +1178,7 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, if (alertable) flags |= SELECT_ALERTABLE; select_op.wait.op = wait_all ? SELECT_WAIT_ALL : SELECT_WAIT; for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); - return NTDLL_wait_for_multiple_objects( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout, 0 ); + return NTDLL_wait_for_multiple_objects( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); } @@ -1199,10 +1201,12 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE hSignalObject, HANDLE hWa UINT flags = SELECT_INTERRUPTIBLE; if (!hSignalObject) return STATUS_INVALID_HANDLE; + if (alertable) flags |= SELECT_ALERTABLE; - select_op.wait.op = SELECT_WAIT; - select_op.wait.handles[0] = wine_server_obj_handle( hWaitObject ); - return NTDLL_wait_for_multiple_objects( &select_op, offsetof( select_op_t, wait.handles[1] ), flags, timeout, hSignalObject ); + select_op.signal_and_wait.op = SELECT_SIGNAL_AND_WAIT; + select_op.signal_and_wait.wait = wine_server_obj_handle( hWaitObject ); + select_op.signal_and_wait.signal = wine_server_obj_handle( hSignalObject ); + return NTDLL_wait_for_multiple_objects( &select_op, sizeof(select_op.signal_and_wait), flags, timeout ); } @@ -1228,7 +1232,7 @@ NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeou /* if alertable, we need to query the server */ if (alertable) return NTDLL_wait_for_multiple_objects( NULL, 0, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, - timeout, 0 ); + timeout ); if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) /* sleep forever */ { diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 890c8a31732..58d8e11ef5f 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -408,7 +408,8 @@ enum select_op { SELECT_NONE, SELECT_WAIT, - SELECT_WAIT_ALL + SELECT_WAIT_ALL, + SELECT_SIGNAL_AND_WAIT }; typedef union @@ -419,6 +420,12 @@ typedef union enum select_op op; obj_handle_t handles[MAXIMUM_WAIT_OBJECTS]; } wait; + struct + { + enum select_op op; + obj_handle_t wait; + obj_handle_t signal; + } signal_and_wait; } select_op_t; enum apc_type @@ -1069,11 +1076,11 @@ struct select_request struct request_header __header; int flags; client_ptr_t cookie; - obj_handle_t signal; - obj_handle_t prev_apc; timeout_t timeout; + obj_handle_t prev_apc; /* VARARG(result,apc_result); */ /* VARARG(data,select_op); */ + char __pad_36[4]; }; struct select_reply { @@ -5787,6 +5794,6 @@ union generic_reply struct set_suspend_context_reply set_suspend_context_reply; }; -#define SERVER_PROTOCOL_VERSION 445 +#define SERVER_PROTOCOL_VERSION 446 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index 0b50766fa2b..0be065e896c 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -424,7 +424,8 @@ enum select_op { SELECT_NONE, SELECT_WAIT, - SELECT_WAIT_ALL + SELECT_WAIT_ALL, + SELECT_SIGNAL_AND_WAIT }; typedef union @@ -435,6 +436,12 @@ typedef union enum select_op op; /* SELECT_WAIT or SELECT_WAIT_ALL */ obj_handle_t handles[MAXIMUM_WAIT_OBJECTS]; } wait; + struct + { + enum select_op op; /* SELECT_SIGNAL_AND_WAIT */ + obj_handle_t wait; + obj_handle_t signal; /* must be last in the structure so we can remove it on retries */ + } signal_and_wait; } select_op_t; enum apc_type @@ -937,9 +944,8 @@ struct rawinput_device @REQ(select) int flags; /* wait flags (see below) */ client_ptr_t cookie; /* magic cookie to return to client */ - obj_handle_t signal; /* object to signal (0 if none) */ - obj_handle_t prev_apc; /* handle to previous APC */ timeout_t timeout; /* timeout */ + obj_handle_t prev_apc; /* handle to previous APC */ VARARG(result,apc_result); /* result of previous APC */ VARARG(data,select_op); /* operation-specific data */ @REPLY diff --git a/server/request.h b/server/request.h index e831f8d801a..07771616762 100644 --- a/server/request.h +++ b/server/request.h @@ -816,9 +816,8 @@ C_ASSERT( FIELD_OFFSET(struct open_thread_reply, handle) == 8 ); C_ASSERT( sizeof(struct open_thread_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct select_request, flags) == 12 ); C_ASSERT( FIELD_OFFSET(struct select_request, cookie) == 16 ); -C_ASSERT( FIELD_OFFSET(struct select_request, signal) == 24 ); -C_ASSERT( FIELD_OFFSET(struct select_request, prev_apc) == 28 ); -C_ASSERT( FIELD_OFFSET(struct select_request, timeout) == 32 ); +C_ASSERT( FIELD_OFFSET(struct select_request, timeout) == 24 ); +C_ASSERT( FIELD_OFFSET(struct select_request, prev_apc) == 32 ); C_ASSERT( sizeof(struct select_request) == 40 ); C_ASSERT( FIELD_OFFSET(struct select_reply, timeout) == 8 ); C_ASSERT( FIELD_OFFSET(struct select_reply, call) == 16 ); diff --git a/server/thread.c b/server/thread.c index 6858fd1a023..b5698b3fd57 100644 --- a/server/thread.c +++ b/server/thread.c @@ -590,6 +590,25 @@ static int wait_on( const select_op_t *select_op, unsigned int count, struct obj return 1; } +static int wait_on_handles( const select_op_t *select_op, unsigned int count, const obj_handle_t *handles, + int flags, timeout_t timeout ) +{ + struct object *objects[MAXIMUM_WAIT_OBJECTS]; + unsigned int i; + int ret = 0; + + assert( count <= MAXIMUM_WAIT_OBJECTS ); + + for (i = 0; i < count; i++) + if (!(objects[i] = get_handle_obj( current->process, handles[i], SYNCHRONIZE, NULL ))) + break; + + if (i == count) ret = wait_on( select_op, count, objects, flags, timeout ); + + while (i > 0) release_object( objects[--i] ); + return ret; +} + /* check if the thread waiting condition is satisfied */ static int check_wait( struct thread *thread ) { @@ -714,18 +733,17 @@ static int signal_object( obj_handle_t handle ) /* select on a list of handles */ static timeout_t select_on( const select_op_t *select_op, data_size_t op_size, client_ptr_t cookie, - int flags, timeout_t timeout, obj_handle_t signal_obj ) + int flags, timeout_t timeout ) { int ret; - unsigned int i, count; - struct object *objects[MAXIMUM_WAIT_OBJECTS]; + unsigned int count; if (timeout <= 0) timeout = current_time - timeout; switch (select_op->op) { case SELECT_NONE: - count = 0; + if (!wait_on( select_op, 0, NULL, flags, timeout )) return timeout; break; case SELECT_WAIT: @@ -736,6 +754,23 @@ static timeout_t select_on( const select_op_t *select_op, data_size_t op_size, c set_error( STATUS_INVALID_PARAMETER ); return 0; } + if (!wait_on_handles( select_op, count, select_op->wait.handles, flags, timeout )) + return timeout; + break; + + case SELECT_SIGNAL_AND_WAIT: + if (!wait_on_handles( select_op, 1, &select_op->signal_and_wait.wait, flags, timeout )) + return timeout; + if (select_op->signal_and_wait.signal) + { + if (!signal_object( select_op->signal_and_wait.signal )) + { + end_wait( current ); + return timeout; + } + /* check if we woke ourselves up */ + if (!current->wait) return timeout; + } break; default: @@ -743,34 +778,12 @@ static timeout_t select_on( const select_op_t *select_op, data_size_t op_size, c return 0; } - for (i = 0; i < count; i++) - { - if (!(objects[i] = get_handle_obj( current->process, select_op->wait.handles[i], - SYNCHRONIZE, NULL ))) - break; - } - - if (i < count) goto done; - if (!wait_on( select_op, count, objects, flags, timeout )) goto done; - - /* signal the object */ - if (signal_obj) - { - if (!signal_object( signal_obj )) - { - end_wait( current ); - goto done; - } - /* check if we woke ourselves up */ - if (!current->wait) goto done; - } - if ((ret = check_wait( current )) != -1) { /* condition is already satisfied */ end_wait( current ); set_error( ret ); - goto done; + return timeout; } /* now we need to wait */ @@ -780,14 +793,11 @@ static timeout_t select_on( const select_op_t *select_op, data_size_t op_size, c thread_timeout, current->wait ))) { end_wait( current ); - goto done; + return timeout; } } current->wait->cookie = cookie; set_error( STATUS_PENDING ); - -done: - while (i > 0) release_object( objects[--i] ); return timeout; } @@ -1370,7 +1380,7 @@ DECL_HANDLER(select) release_object( apc ); } - reply->timeout = select_on( &select_op, op_size, req->cookie, req->flags, req->timeout, req->signal ); + reply->timeout = select_on( &select_op, op_size, req->cookie, req->flags, req->timeout ); if (get_error() == STATUS_USER_APC) { diff --git a/server/trace.c b/server/trace.c index cf587945bb6..7dc7d12448b 100644 --- a/server/trace.c +++ b/server/trace.c @@ -405,6 +405,10 @@ static void dump_varargs_select_op( const char *prefix, data_size_t size ) dump_handles( ",handles=", data.wait.handles, min( size, sizeof(data.wait) ) - offsetof( select_op_t, wait.handles )); break; + case SELECT_SIGNAL_AND_WAIT: + fprintf( stderr, "SIGNAL_AND_WAIT,signal=%04x,wait=%04x", + data.signal_and_wait.signal, data.signal_and_wait.wait ); + break; default: fprintf( stderr, "op=%u", data.op ); break; @@ -1388,9 +1392,8 @@ static void dump_select_request( const struct select_request *req ) { fprintf( stderr, " flags=%d", req->flags ); dump_uint64( ", cookie=", &req->cookie ); - fprintf( stderr, ", signal=%04x", req->signal ); - fprintf( stderr, ", prev_apc=%04x", req->prev_apc ); dump_timeout( ", timeout=", &req->timeout ); + fprintf( stderr, ", prev_apc=%04x", req->prev_apc ); dump_varargs_apc_result( ", result=", cur_size ); dump_varargs_select_op( ", data=", cur_size ); }