From 15483b1a126902a8c7b7b59800a376bc473e2ed3 Mon Sep 17 00:00:00 2001 From: Jinoh Kang Date: Thu, 10 Feb 2022 01:17:53 +0900 Subject: [PATCH] server: Allow calling async_handoff() with status code STATUS_ALERTED. If the server detects that an I/O request could be completed immediately (e.g. the socket to read from already has incoming data), it can now return STATUS_ALERTED to allow opportunistic synchronous I/O. The Unix side will then attempt to perform I/O in nonblocking mode and report back the I/O status to the server via the new server request "set_async_direct_result". If the operation returns e.g. EAGAIN or EWOULDBLOCK, the client can opt to either abandon the request (by specifying an error status) or poll for it in the server as usual (by waiting on the wait handle). Without such mechanism in place, the client cannot safely perform immediately satiable I/O operations synchronously, since it can potentially conflict with other pending I/O operations that have already been queued. Signed-off-by: Jinoh Kang Signed-off-by: Zebediah Figura Signed-off-by: Alexandre Julliard --- include/wine/server_protocol.h | 22 ++++++++++- server/async.c | 72 ++++++++++++++++++++++++++++++++++ server/protocol.def | 10 +++++ server/request.h | 8 ++++ server/trace.c | 15 +++++++ 5 files changed, 126 insertions(+), 1 deletion(-) diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 97ab4e25834..5ed95a41c19 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2905,6 +2905,23 @@ struct get_async_result_reply +struct set_async_direct_result_request +{ + struct request_header __header; + obj_handle_t handle; + apc_param_t information; + unsigned int status; + char __pad_28[4]; +}; +struct set_async_direct_result_reply +{ + struct reply_header __header; + obj_handle_t handle; + char __pad_12[4]; +}; + + + struct read_request { struct request_header __header; @@ -5547,6 +5564,7 @@ enum request REQ_register_async, REQ_cancel_async, REQ_get_async_result, + REQ_set_async_direct_result, REQ_read, REQ_write, REQ_ioctl, @@ -5828,6 +5846,7 @@ union generic_request struct register_async_request register_async_request; struct cancel_async_request cancel_async_request; struct get_async_result_request get_async_result_request; + struct set_async_direct_result_request set_async_direct_result_request; struct read_request read_request; struct write_request write_request; struct ioctl_request ioctl_request; @@ -6107,6 +6126,7 @@ union generic_reply struct register_async_reply register_async_reply; struct cancel_async_reply cancel_async_reply; struct get_async_result_reply get_async_result_reply; + struct set_async_direct_result_reply set_async_direct_result_reply; struct read_reply read_reply; struct write_reply write_reply; struct ioctl_reply ioctl_reply; @@ -6262,7 +6282,7 @@ union generic_reply /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 743 +#define SERVER_PROTOCOL_VERSION 744 /* ### protocol_version end ### */ diff --git a/server/async.c b/server/async.c index 7aef28355f0..7d0ff10f7a4 100644 --- a/server/async.c +++ b/server/async.c @@ -338,6 +338,31 @@ obj_handle_t async_handoff( struct async *async, data_size_t *result, int force_ return async->wait_handle; } + if (get_error() == STATUS_ALERTED) + { + /* give the client opportunity to complete synchronously. after the + * client performs the I/O, it reports the result back to the server + * via the set_async_direct_result request. if it turns out that the + * I/O request is not actually immediately satiable, the client may + * then choose to re-queue the async by reporting STATUS_PENDING + * instead. + * + * since we're deferring the initial I/O (to the client), we mark the + * async as having unknown initial status (unknown_status = 1). note + * that we don't reuse async_set_unknown_status() here. this is because + * the one responsible for performing the I/O is not the device driver, + * but instead the client that requested the I/O in the first place. + * + * also, async_set_unknown_status() would set direct_result to zero + * forcing APC_ASYNC_IO to fire in async_terminate(), which is not + * useful due to subtle semantic differences between synchronous and + * asynchronous completion. + */ + async->unknown_status = 1; + async_terminate( async, STATUS_ALERTED ); + return async->wait_handle; + } + async->initial_status = get_error(); if (!async->pending && NT_ERROR( get_error() )) @@ -728,3 +753,50 @@ DECL_HANDLER(get_async_result) } set_error( iosb->status ); } + +/* notify direct completion of async and close the wait handle if not blocking */ +DECL_HANDLER(set_async_direct_result) +{ + struct async *async = (struct async *)get_handle_obj( current->process, req->handle, 0, &async_ops ); + unsigned int status = req->status; + + if (!async) return; + + if (!async->unknown_status || !async->terminated || !async->alerted) + { + set_error( STATUS_INVALID_PARAMETER ); + release_object( &async->obj ); + return; + } + + async_set_initial_status( async, status ); + + if (status == STATUS_PENDING) + { + async->direct_result = 0; + async->pending = 1; + } + + /* if the I/O has completed successfully, the client would have already + * set the IOSB. therefore, we can skip waiting on wait_handle and do + * async_set_result() directly. + */ + async_set_result( &async->obj, status, req->information ); + + /* close wait handle here to avoid extra server round trip, if the I/O + * either has completed, or is pending and not blocking. + */ + if (status != STATUS_PENDING || !async->blocking) + { + close_handle( async->thread->process, async->wait_handle ); + async->wait_handle = 0; + } + + /* report back to the client whether the wait handle has been closed. + * handle will be 0 if closed by us; otherwise the original value is + * retained + */ + reply->handle = async->wait_handle; + + release_object( &async->obj ); +} diff --git a/server/protocol.def b/server/protocol.def index 21b17c11551..ce05e9a38c6 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2163,6 +2163,16 @@ enum message_type @END +/* Notify direct completion of async and close the wait handle if not blocking */ +@REQ(set_async_direct_result) + obj_handle_t handle; /* wait handle */ + apc_param_t information; /* IO_STATUS_BLOCK Information */ + unsigned int status; /* completion status */ +@REPLY + obj_handle_t handle; /* wait handle, or NULL if closed */ +@END + + /* Perform a read on a file object */ @REQ(read) async_data_t async; /* async I/O parameters */ diff --git a/server/request.h b/server/request.h index f2d0e827d94..128b70fe3cf 100644 --- a/server/request.h +++ b/server/request.h @@ -242,6 +242,7 @@ DECL_HANDLER(set_serial_info); DECL_HANDLER(register_async); DECL_HANDLER(cancel_async); DECL_HANDLER(get_async_result); +DECL_HANDLER(set_async_direct_result); DECL_HANDLER(read); DECL_HANDLER(write); DECL_HANDLER(ioctl); @@ -522,6 +523,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_register_async, (req_handler)req_cancel_async, (req_handler)req_get_async_result, + (req_handler)req_set_async_direct_result, (req_handler)req_read, (req_handler)req_write, (req_handler)req_ioctl, @@ -1396,6 +1398,12 @@ C_ASSERT( sizeof(struct cancel_async_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct get_async_result_request, user_arg) == 16 ); C_ASSERT( sizeof(struct get_async_result_request) == 24 ); C_ASSERT( sizeof(struct get_async_result_reply) == 8 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_request, handle) == 12 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_request, information) == 16 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_request, status) == 24 ); +C_ASSERT( sizeof(struct set_async_direct_result_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_reply, handle) == 8 ); +C_ASSERT( sizeof(struct set_async_direct_result_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct read_request, async) == 16 ); C_ASSERT( FIELD_OFFSET(struct read_request, pos) == 56 ); C_ASSERT( sizeof(struct read_request) == 64 ); diff --git a/server/trace.c b/server/trace.c index 32606d540f7..6b50b9d0d18 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2758,6 +2758,18 @@ static void dump_get_async_result_reply( const struct get_async_result_reply *re dump_varargs_bytes( " out_data=", cur_size ); } +static void dump_set_async_direct_result_request( const struct set_async_direct_result_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + dump_uint64( ", information=", &req->information ); + fprintf( stderr, ", status=%08x", req->status ); +} + +static void dump_set_async_direct_result_reply( const struct set_async_direct_result_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + static void dump_read_request( const struct read_request *req ) { dump_async_data( " async=", &req->async ); @@ -4595,6 +4607,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_register_async_request, (dump_func)dump_cancel_async_request, (dump_func)dump_get_async_result_request, + (dump_func)dump_set_async_direct_result_request, (dump_func)dump_read_request, (dump_func)dump_write_request, (dump_func)dump_ioctl_request, @@ -4872,6 +4885,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { NULL, NULL, (dump_func)dump_get_async_result_reply, + (dump_func)dump_set_async_direct_result_reply, (dump_func)dump_read_reply, (dump_func)dump_write_reply, (dump_func)dump_ioctl_reply, @@ -5149,6 +5163,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "register_async", "cancel_async", "get_async_result", + "set_async_direct_result", "read", "write", "ioctl",