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 <jinoh.kang.kr@gmail.com>
Signed-off-by: Zebediah Figura <zfigura@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jinoh Kang 2022-02-10 01:17:53 +09:00 committed by Alexandre Julliard
parent 6ed3af99b5
commit 15483b1a12
5 changed files with 126 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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

View File

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