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",