[PATCH v6 1/4] server: Allow calling async_handoff() with status code STATUS_ALERTED.

Jinoh Kang jinoh.kang.kr at gmail.com
Wed Feb 9 10:17:53 CST 2022


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

Notes:
    v1 -> v2: dequeue async after failed completion
    v2 -> v3:
    - rewrite handling of pending/failed asyncs.
    - set async->alerted = 1
    v3 -> v4:
    - add assert( async->iosb );
    - make robust against abrupt transition/cancellation:
      don't return STATUS_ACCESS_DENIED on precondition failure
      (client won't know what to do with STATUS_ACCESS_DENIED anyway)
    - always return wait_handle if possible
    - clarify and rearrange comments
    v4 -> v5:
    - edit comments
    - /notify_async_direct_result/set_async_direct_result/g
    - remove assert( async->iosb );
    - simplify complicated precondition
    - fail early if precondition unmet
    - don't forward status from async
    - don't use async_handoff(); open code relevant parts instead
    - remove irrelevant word in commit message (signal_wait_async)
    - async_handoff(): don't check for async->pending in new code path
    - async_handoff(): use async_terminate() directly in new code path
    v5 -> v6:
    - merge two patches into one
    - make comment case consistent

 server/async.c      | 72 +++++++++++++++++++++++++++++++++++++++++++++
 server/protocol.def | 10 +++++++
 2 files changed, 82 insertions(+)

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 02e73047f9b..28dee7c48f7 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 */
+    unsigned int   status;        /* completion status */
+    apc_param_t    information;   /* IO_STATUS_BLOCK Information */
+ 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 */
-- 
2.34.1




More information about the wine-devel mailing list