server: Make signal and wait a separate select operation.

This commit is contained in:
Alexandre Julliard 2013-08-22 11:15:44 +02:00
parent 042e0046d4
commit 947e337480
9 changed files with 86 additions and 57 deletions

View File

@ -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;
}

View File

@ -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 )
{

View File

@ -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;

View File

@ -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 */
{

View File

@ -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 */

View File

@ -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

View File

@ -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 );

View File

@ -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)
{

View File

@ -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 );
}