From: Jinoh Kang Subject: [PATCH 2/4] ntdll/unix: Add a new select type "SELECT_SIGNAL_WAIT_ASYNC". Message-Id: <416f6887-208b-3b44-ec2d-9234cfeaa1f5@gmail.com> Date: Wed, 19 Jan 2022 04:29:45 +0900 In-Reply-To: References: This select type is not used to emulate any NtWait* system call on Windows; instead, it is intended be used in place of async_wait() to set the async result and "wait" for the async object in one server call. Signed-off-by: Jinoh Kang --- dlls/ntdll/unix/server.c | 2 ++ dlls/ntdll/unix/sync.c | 14 ++++++++++++++ dlls/ntdll/unix/unix_private.h | 1 + server/async.c | 13 ++++++++++--- server/object.h | 4 ++++ server/protocol.def | 11 ++++++++++- server/thread.c | 10 ++++++++++ 7 files changed, 51 insertions(+), 4 deletions(-) diff --git a/dlls/ntdll/unix/server.c b/dlls/ntdll/unix/server.c index 9d0594d3374..100bf943292 100644 --- a/dlls/ntdll/unix/server.c +++ b/dlls/ntdll/unix/server.c @@ -632,6 +632,8 @@ unsigned int server_select( const select_op_t *select_op, data_size_t size, UINT /* don't signal multiple times */ if (size >= sizeof(select_op->signal_and_wait) && select_op->op == SELECT_SIGNAL_AND_WAIT) size = offsetof( select_op_t, signal_and_wait.signal ); + if (size >= sizeof(select_op->signal_wait_async) && select_op->op == SELECT_SIGNAL_WAIT_ASYNC ) + size = offsetof( select_op_t, signal_wait_async.signal ); } pthread_sigmask( SIG_SETMASK, &old_set, NULL ); if (signaled) break; diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 442243d8bcf..3b206a8f0f2 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2510,3 +2510,17 @@ NTSTATUS WINAPI NtWaitForAlertByThreadId( const void *address, const LARGE_INTEG } #endif + +NTSTATUS signal_wait_async( HANDLE handle, BOOL alertable, BOOL signal, NTSTATUS status, ULONG_PTR information ) +{ + select_op_t select_op; + UINT flags = SELECT_INTERRUPTIBLE; + + if (alertable) flags |= SELECT_ALERTABLE; + select_op.signal_wait_async.op = SELECT_SIGNAL_WAIT_ASYNC; + select_op.signal_wait_async.handle = wine_server_obj_handle( handle ); + select_op.signal_wait_async.signal = signal; + select_op.signal_wait_async.status = status; + select_op.signal_wait_async.total = information; + return server_wait( &select_op, sizeof(select_op.signal_wait_async), flags, NULL ); +} diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index a79edabc37c..5455521c1a0 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -274,6 +274,7 @@ extern NTSTATUS get_device_info( int fd, struct _FILE_FS_DEVICE_INFORMATION *inf extern void init_files(void) DECLSPEC_HIDDEN; extern void init_cpu_info(void) DECLSPEC_HIDDEN; extern void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async ) DECLSPEC_HIDDEN; +extern NTSTATUS signal_wait_async( HANDLE handle, BOOL alertable, BOOL signal, NTSTATUS status, ULONG_PTR information ); extern void dbg_init(void) DECLSPEC_HIDDEN; diff --git a/server/async.c b/server/async.c index 7aef28355f0..339d050b086 100644 --- a/server/async.c +++ b/server/async.c @@ -98,6 +98,11 @@ static inline void async_reselect( struct async *async ) if (async->queue && async->fd) fd_reselect_async( async->fd, async->queue ); } +struct async *get_async_obj( struct process *process, obj_handle_t handle, unsigned int access ) +{ + return (struct async *)get_handle_obj( process, handle, access, &async_ops ); +} + static void async_dump( struct object *obj, int verbose ) { struct async *async = (struct async *)obj; @@ -121,10 +126,7 @@ static void async_satisfied( struct object *obj, struct wait_queue_entry *entry assert( async->iosb ); if (async->direct_result) - { async_set_result( &async->obj, async->iosb->status, async->iosb->result ); - async->direct_result = 0; - } if (async->initial_status == STATUS_PENDING && async->blocking) set_wait_status( entry, async->iosb->status ); @@ -460,6 +462,11 @@ void async_set_result( struct object *obj, unsigned int status, apc_param_t tota assert( async->terminated ); /* it must have been woken up if we get a result */ + /* If status == STATUS_PENDING, future results will be delivered via APC. + * If status != STATUS_PENDING, this prevents async_satisfied() from + * overriding I/O result set by SELECT_SIGNAL_WAIT_ASYNC. */ + async->direct_result = 0; + if (async->alerted && status == STATUS_PENDING) /* restart it */ { async->terminated = 0; diff --git a/server/object.h b/server/object.h index f156f1d2f13..8645a00f73a 100644 --- a/server/object.h +++ b/server/object.h @@ -288,6 +288,10 @@ extern struct object *create_symlink( struct object *root, const struct unicode_ unsigned int attr, const struct unicode_str *target, const struct security_descriptor *sd ); +/* async I/O functions */ + +extern struct async *get_async_obj( struct process *process, obj_handle_t handle, unsigned int access ); + /* global variables */ /* command-line options */ diff --git a/server/protocol.def b/server/protocol.def index db73f0418a9..ae4729a9e56 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -446,7 +446,8 @@ enum select_op SELECT_WAIT_ALL, SELECT_SIGNAL_AND_WAIT, SELECT_KEYED_EVENT_WAIT, - SELECT_KEYED_EVENT_RELEASE + SELECT_KEYED_EVENT_RELEASE, + SELECT_SIGNAL_WAIT_ASYNC, }; typedef union @@ -469,6 +470,14 @@ typedef union obj_handle_t handle; client_ptr_t key; } keyed_event; + struct + { + enum select_op op; /* SELECT_SIGNAL_WAIT_ASYNC */ + obj_handle_t handle; + int signal; /* set to 0 to ignore status and total (e.g. on retries) */ + unsigned int status; /* new status of async operation */ + apc_param_t total; /* bytes transferred */ + } signal_wait_async; } select_op_t; enum apc_type diff --git a/server/thread.c b/server/thread.c index 467ccd1f0db..8eb473f2c58 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1029,6 +1029,16 @@ static int select_on( const select_op_t *select_op, data_size_t op_size, client_ current->wait->key = select_op->keyed_event.key; break; + case SELECT_SIGNAL_WAIT_ASYNC: + object = (struct object *)get_async_obj( current->process, select_op->signal_wait_async.handle, SYNCHRONIZE ); + if (!object) return 1; + if (select_op->signal_wait_async.signal) + async_set_result( object, select_op->signal_wait_async.status, select_op->signal_wait_async.total ); + ret = wait_on( select_op, 1, &object, flags, when ); + release_object( object ); + if (!ret) return 1; + break; + default: set_error( STATUS_INVALID_PARAMETER ); return 1; -- 2.31.1