Jinoh Kang : server: Allow calling async_handoff() with status code STATUS_ALERTED.

Alexandre Julliard julliard at winehq.org
Thu Feb 10 16:10:30 CST 2022


Module: wine
Branch: master
Commit: 15483b1a126902a8c7b7b59800a376bc473e2ed3
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=15483b1a126902a8c7b7b59800a376bc473e2ed3

Author: Jinoh Kang <jinoh.kang.kr at gmail.com>
Date:   Thu Feb 10 01:17:53 2022 +0900

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 at gmail.com>
Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 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 */
+ at REQ(set_async_direct_result)
+    obj_handle_t   handle;        /* wait handle */
+    apc_param_t    information;   /* IO_STATUS_BLOCK Information */
+    unsigned int   status;        /* completion status */
+ at REPLY
+    obj_handle_t   handle;        /* wait handle, or NULL if closed */
+ at 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",




More information about the wine-cvs mailing list