Some I/O operations need a way to "queue" the async to the target object first, even if the operation itself is to be completed synchronously. After synchronous completion, it needs a way to notify the IO_STATUS_BLOCK values back to the server.
Add a new wineserver request, "notify_async_direct_result", which notifies direct (i.e. synchronous) completion of async from the same thread.
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- server/async.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ server/protocol.def | 10 ++++++++ 2 files changed, 69 insertions(+)
diff --git a/server/async.c b/server/async.c index 6373e8c1ae4..9e5503837c2 100644 --- a/server/async.c +++ b/server/async.c @@ -750,3 +750,62 @@ DECL_HANDLER(get_async_result) } set_error( iosb->status ); } + +/* Notify direct completion of async and close the wait handle */ +DECL_HANDLER(notify_async_direct_result) +{ + struct async *async = (struct async *)get_handle_obj( current->process, req->handle, 0, &async_ops ); + + if (!async) return; + + if (async->iosb && async->unknown_status && !async->pending && async->terminated) + { + /* Reactivate async. We call async_reselect() later. */ + async->terminated = 0; + + /* Set result for async_handoff(). */ + set_error( req->status ); + async->iosb->result = req->information; + + /* The async_handoff() call prior to the current server request was + * effectively a no-op since async->unknown_status is 1. Calling it + * again with async->unknown_status = 0 will do the remaining steps. + */ + async->unknown_status = 0; + async_handoff( async, NULL, 0 ); + + if (!NT_ERROR( get_error() ) && get_error() != STATUS_PENDING) + { + /* I/O completed. The client has already set the IOSB, so we can + * skip waiting on wait_handle and do async_set_result() directly. + * + * If !async->direct_result, an APC_ASYNC_IO has been fired. + * async_set_result() will be called when the APC returns. + */ + if (async->direct_result) + { + async_set_result( &async->obj, async->iosb->status, async->iosb->result ); + async->direct_result = 0; + } + + /* close wait handle here to avoid extra server round trip */ + if (async->wait_handle) + { + close_handle( async->thread->process, async->wait_handle ); + async->wait_handle = 0; + } + } + else + { + /* I/O has either failed synchronously, or switched to STATUS_PENDING. */ + async_reselect( async ); + } + + /* The wait handle is preserved only when the status is STATUS_PENDING + * and async->blocking is set (i.e. we're going to block on it). */ + reply->handle = async->wait_handle; + } + else set_error( STATUS_ACCESS_DENIED ); + + release_object( &async->obj ); +} diff --git a/server/protocol.def b/server/protocol.def index 02e73047f9b..2c3b8dbc619 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 */ +@REQ(notify_async_direct_result) + obj_handle_t handle; /* wait handle */ + unsigned int status; /* completion status */ + apc_param_t information; /* IO_STATUS_BLOCK Information */ +@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 */