From: Jinoh Kang Subject: [RFC PATCH v2 05/11] server: Add helpers for synchronous APC delivery via ordinary server call reply. Message-Id: <129a9a10-46b2-7ccf-3826-b0ca683a9c34@gmail.com> Date: Sat, 22 Jan 2022 23:36:37 +0900 In-Reply-To: <1e0d520b-0f60-1323-3305-4f884fb121a1@gmail.com> References: <1e0d520b-0f60-1323-3305-4f884fb121a1@gmail.com> Signed-off-by: Jinoh Kang --- Notes: v1 -> v2: - server/request.h - delete autogenerated change - server/thread.c - handle APC handle allocation failure Alternative approaches considered: 1. Don't use thread_queue_apc to deliver the pseudo-APC; instead, use create_apc directly. This approach requires modification or partial duplication of async_terminate() for passing upward the return value of "create_apc". This in turn necessiates changing async_handoff() to also pass over the APC, which complicates the matter. 2. Don't use APC at all (the previous patch). In this case we do not create APCs at all, and this patch becomes unnecessary. server/protocol.def | 7 +++++++ server/thread.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ server/thread.h | 3 +++ server/trace.c | 22 ++++++++++++++++++++ tools/make_requests | 1 + 5 files changed, 82 insertions(+) diff --git a/server/protocol.def b/server/protocol.def index db73f0418a9..f21f7187c4d 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -695,6 +695,13 @@ typedef union } break_process; } apc_result_t; +typedef struct +{ + apc_call_t call; /* APC call arguments */ + obj_handle_t apc_handle; /* handle to next APC */ + int __pad; +} inline_apc_t; + enum irp_type { IRP_CALL_NONE, diff --git a/server/thread.c b/server/thread.c index 467ccd1f0db..6015b256707 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1211,6 +1211,54 @@ static void clear_apc_queue( struct list *queue ) } } +void try_suspend_apc_interrupt(void) +{ + static const select_op_t select_op_none = { SELECT_NONE }; + unsigned int error = get_error(); + select_on( &select_op_none, sizeof(select_op_none), 0, + SELECT_INTERRUPTIBLE, TIMEOUT_INFINITE ); + set_error( error ); +} + +static void enable_next_apc_interrupt(void) +{ + if (current->wait && !current->wait->cookie) end_wait( current, STATUS_TIMEOUT ); +} + +void resume_apc_interrupt(void) +{ + enable_next_apc_interrupt(); + if (!list_empty( ¤t->system_apc )) + wake_thread( current ); +} + +int dequeue_synchronous_system_apc( inline_apc_t *inline_apc ) +{ + struct thread_apc *apc; + obj_handle_t handle; + int result = 0; + unsigned int error = get_error(); + + if ((apc = thread_dequeue_apc( current, 1 ))) + { + if ((handle = alloc_handle_no_access_check( current->process, &apc->obj, SYNCHRONIZE, 0 ))) + { + memset( inline_apc, 0, sizeof(*inline_apc) ); + inline_apc->call = apc->call; + inline_apc->apc_handle = handle; + result = 1; + } + else + { + apc->executed = 1; + wake_up( &apc->obj, 0 ); + } + release_object( apc ); + } + set_error( error ); + return result; +} + /* add an fd to the inflight list */ /* return list index, or -1 on error */ int thread_add_inflight_fd( struct thread *thread, int client, int server ) @@ -1654,6 +1702,7 @@ DECL_HANDLER(select) release_object( apc ); } + enable_next_apc_interrupt(); reply->signaled = select_on( &select_op, op_size, req->cookie, req->flags, req->timeout ); if (get_error() == STATUS_USER_APC) diff --git a/server/thread.h b/server/thread.h index 8dcf966a90a..d1acd3267b1 100644 --- a/server/thread.h +++ b/server/thread.h @@ -116,6 +116,9 @@ extern void kill_thread( struct thread *thread, int violent_death ); extern void wake_up( struct object *obj, int max ); extern int thread_queue_apc( struct process *process, struct thread *thread, struct object *owner, const apc_call_t *call_data ); extern void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_type type ); +extern void try_suspend_apc_interrupt(void); +extern void resume_apc_interrupt(void); +extern int dequeue_synchronous_system_apc( inline_apc_t *inline_apc ); extern int thread_add_inflight_fd( struct thread *thread, int client, int server ); extern int thread_get_inflight_fd( struct thread *thread, int client ); extern struct token *thread_get_impersonation_token( struct thread *thread ); diff --git a/server/trace.c b/server/trace.c index a48f00258fe..7a6f5e1c119 100644 --- a/server/trace.c +++ b/server/trace.c @@ -328,6 +328,13 @@ static void dump_apc_result( const char *prefix, const apc_result_t *result ) fputc( '}', stderr ); } +static void dump_inline_apc( const char *prefix, const inline_apc_t *inline_apc ) +{ + fprintf( stderr, "%s{", prefix ); + dump_apc_call( "call=", &inline_apc->call ); + fprintf( stderr, ",apc_handle=%04x}", inline_apc->apc_handle ); +} + static void dump_async_data( const char *prefix, const async_data_t *data ) { fprintf( stderr, "%s{handle=%04x,event=%04x", prefix, data->handle, data->event ); @@ -533,6 +540,21 @@ static void dump_varargs_apc_result( const char *prefix, data_size_t size ) remove_data( size ); } +#ifdef __GNUC__ +__attribute__((unused)) +#endif +static void dump_varargs_inline_apc( const char *prefix, data_size_t size ) +{ + const inline_apc_t *result = cur_data; + + if (size >= sizeof(*result)) + { + dump_inline_apc( prefix, result ); + size = sizeof(*result); + } + remove_data( size ); +} + static void dump_varargs_select_op( const char *prefix, data_size_t size ) { select_op_t data; diff --git a/tools/make_requests b/tools/make_requests index 1c4e5977c8b..e85a5c4a615 100755 --- a/tools/make_requests +++ b/tools/make_requests @@ -47,6 +47,7 @@ my %formats = "rectangle_t" => [ 16, 4, "&dump_rectangle" ], "apc_call_t" => [ 48, 8, "&dump_apc_call" ], "apc_result_t" => [ 40, 8, "&dump_apc_result" ], + "inline_apc_t" => [ 56, 8, "&dump_inline_apc" ], "async_data_t" => [ 40, 8, "&dump_async_data" ], "irp_params_t" => [ 32, 8, "&dump_irp_params" ], "luid_t" => [ 8, 4, "&dump_luid" ], -- 2.31.1