From 6ca1d1b0812496e670957543294b04abfefe8d78 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 15 Jan 2007 22:26:32 +0100 Subject: [PATCH] server: Support queuing some APCs to a process instead of a thread. --- dlls/ntdll/ntdll_misc.h | 1 + dlls/ntdll/sync.c | 36 ++++++++++ dlls/ntdll/thread.c | 2 +- include/wine/server_protocol.h | 6 +- server/protocol.def | 7 +- server/thread.c | 125 +++++++++++++++++++++++++++------ server/trace.c | 12 +++- 7 files changed, 160 insertions(+), 29 deletions(-) diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 895f6def022..b06646c7c3d 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -42,6 +42,7 @@ extern void dump_ObjectAttributes (const OBJECT_ATTRIBUTES *ObjectAttributes); extern void NTDLL_get_server_abstime( abs_time_t *when, const LARGE_INTEGER *timeout ); extern void NTDLL_from_server_abstime( LARGE_INTEGER *time, const abs_time_t *when ); +extern NTSTATUS NTDLL_queue_process_apc( HANDLE process, const apc_call_t *call, apc_result_t *result ); extern NTSTATUS NTDLL_wait_for_multiple_objects( UINT count, const HANDLE *handles, UINT flags, const LARGE_INTEGER *timeout, HANDLE signal_object ); diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 607f07acfaf..2274e217145 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -714,6 +714,42 @@ static BOOL call_apcs( BOOL alertable ) } +/*********************************************************************** + * NTDLL_queue_process_apc + */ +NTSTATUS NTDLL_queue_process_apc( HANDLE process, const apc_call_t *call, apc_result_t *result ) +{ + for (;;) + { + NTSTATUS ret; + HANDLE handle = 0; + + SERVER_START_REQ( queue_apc ) + { + req->process = process; + req->call = *call; + if (!(ret = wine_server_call( req ))) handle = reply->handle; + } + SERVER_END_REQ; + + if (!handle) return ret; + + NtWaitForSingleObject( handle, FALSE, NULL ); + + SERVER_START_REQ( get_apc_result ) + { + req->handle = handle; + if (!(ret = wine_server_call( req ))) *result = reply->result; + } + SERVER_END_REQ; + + if (!ret && result->type == APC_NONE) continue; /* APC didn't run, try again */ + if (ret) NtClose( handle ); + return ret; + } +} + + /*********************************************************************** * NTDLL_wait_for_multiple_objects * diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index dda2ed22e9d..8684b362554 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -622,7 +622,7 @@ NTSTATUS WINAPI NtQueueApcThread( HANDLE handle, PNTAPCFUNC func, ULONG_PTR arg1 NTSTATUS ret; SERVER_START_REQ( queue_apc ) { - req->handle = handle; + req->thread = handle; if (func) { req->call.type = APC_USER; diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 420b4e00b9e..988b3c785de 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -582,12 +582,14 @@ struct unload_dll_reply struct queue_apc_request { struct request_header __header; - obj_handle_t handle; + obj_handle_t thread; + obj_handle_t process; apc_call_t call; }; struct queue_apc_reply { struct reply_header __header; + obj_handle_t handle; }; @@ -4497,6 +4499,6 @@ union generic_reply struct query_symlink_reply query_symlink_reply; }; -#define SERVER_PROTOCOL_VERSION 265 +#define SERVER_PROTOCOL_VERSION 266 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index 7b5d0fd5ab5..5ac0001cefb 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -504,10 +504,13 @@ typedef union @END -/* Queue an APC for a thread */ +/* Queue an APC for a thread or process */ @REQ(queue_apc) - obj_handle_t handle; /* thread handle */ + obj_handle_t thread; /* thread handle */ + obj_handle_t process; /* process handle */ apc_call_t call; /* call arguments */ +@REPLY + obj_handle_t handle; /* APC handle */ @END diff --git a/server/thread.c b/server/thread.c index 133953b0bc4..f99f76336d0 100644 --- a/server/thread.c +++ b/server/thread.c @@ -312,6 +312,21 @@ static int thread_apc_signaled( struct object *obj, struct thread *thread ) return apc->executed; } +/* queue an async procedure call */ +static struct thread_apc *create_apc( struct object *owner, const apc_call_t *call_data ) +{ + struct thread_apc *apc; + + if ((apc = alloc_object( &thread_apc_ops ))) + { + apc->call = *call_data; + apc->owner = owner; + apc->executed = 0; + apc->result.type = APC_NONE; + } + return apc; +} + /* get a thread pointer from a thread id (and increment the refcount) */ struct thread *get_thread_from_id( thread_id_t id ) { @@ -679,20 +694,49 @@ static inline struct list *get_apc_queue( struct thread *thread, enum apc_type t } } -/* queue an async procedure call */ -int thread_queue_apc( struct thread *thread, struct object *owner, const apc_call_t *call_data ) +/* queue an existing APC to a given thread */ +static int queue_apc( struct process *process, struct thread *thread, struct thread_apc *apc ) { - struct thread_apc *apc; - struct list *queue = get_apc_queue( thread, call_data->type ); + struct list *queue; - /* cancel a possible previous APC with the same owner */ - if (owner) thread_cancel_apc( thread, owner, call_data->type ); - if (thread->state == TERMINATED) return 0; + if (!thread) /* find a suitable thread inside the process */ + { + struct thread *candidate; - if (!(apc = alloc_object( &thread_apc_ops ))) return 0; - apc->call = *call_data; - apc->owner = owner; - apc->executed = 0; + /* first try to find a waiting thread */ + LIST_FOR_EACH_ENTRY( candidate, &process->thread_list, struct thread, proc_entry ) + { + if (candidate->state == TERMINATED) continue; + if (process->suspend || candidate->suspend || + (candidate->wait && (candidate->wait->flags & SELECT_INTERRUPTIBLE))) + { + thread = candidate; + break; + } + } + if (!thread) + { + /* then use the first one that accepts a signal */ + LIST_FOR_EACH_ENTRY( candidate, &process->thread_list, struct thread, proc_entry ) + { + if (send_thread_signal( candidate, SIGUSR1 )) + { + thread = candidate; + break; + } + } + } + if (!thread) return 0; /* nothing found */ + } + else + { + if (thread->state == TERMINATED) return 0; + /* cancel a possible previous APC with the same owner */ + if (apc->owner) thread_cancel_apc( thread, apc->owner, apc->call.type ); + } + + queue = get_apc_queue( thread, apc->call.type ); + grab_object( apc ); list_add_tail( queue, &apc->entry ); if (!list_prev( queue, &apc->entry )) /* first one */ wake_thread( thread ); @@ -700,6 +744,20 @@ int thread_queue_apc( struct thread *thread, struct object *owner, const apc_cal return 1; } +/* queue an async procedure call */ +int thread_queue_apc( struct thread *thread, struct object *owner, const apc_call_t *call_data ) +{ + struct thread_apc *apc; + int ret = 0; + + if ((apc = create_apc( owner, call_data ))) + { + ret = queue_apc( NULL, thread, apc ); + release_object( apc ); + } + return ret; +} + /* cancel the async procedure call owned by a specific object */ void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_type type ) { @@ -1076,24 +1134,47 @@ DECL_HANDLER(select) select_on( count, req->cookie, get_req_data(), req->flags, &req->timeout, req->signal ); } -/* queue an APC for a thread */ +/* queue an APC for a thread or process */ DECL_HANDLER(queue_apc) { struct thread *thread; - if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT ))) + struct process *process; + struct thread_apc *apc; + + if (!(apc = create_apc( NULL, &req->call ))) return; + + switch (apc->call.type) { - switch( req->call.type ) + case APC_NONE: + case APC_USER: + if ((thread = get_thread_from_handle( req->thread, THREAD_SET_CONTEXT ))) { - case APC_NONE: - case APC_USER: - thread_queue_apc( thread, NULL, &req->call ); - break; - default: - set_error( STATUS_INVALID_PARAMETER ); - break; + if (!queue_apc( NULL, thread, apc )) set_error( STATUS_THREAD_IS_TERMINATING ); + release_object( thread ); } - release_object( thread ); + break; + case APC_VIRTUAL_ALLOC: + case APC_VIRTUAL_FREE: + if ((process = get_process_from_handle( req->process, PROCESS_VM_OPERATION ))) + { + obj_handle_t handle = alloc_handle( current->process, apc, SYNCHRONIZE, 0 ); + if (handle) + { + if (queue_apc( process, NULL, apc )) reply->handle = handle; + else + { + close_handle( current->process, handle ); + set_error( STATUS_PROCESS_IS_TERMINATING ); + } + } + release_object( process ); + } + break; + default: + set_error( STATUS_INVALID_PARAMETER ); + break; } + release_object( apc ); } /* get next APC to call */ diff --git a/server/trace.c b/server/trace.c index 2724e7d6eef..6e4f7457e96 100644 --- a/server/trace.c +++ b/server/trace.c @@ -909,11 +909,17 @@ static void dump_unload_dll_request( const struct unload_dll_request *req ) static void dump_queue_apc_request( const struct queue_apc_request *req ) { - fprintf( stderr, " handle=%p,", req->handle ); + fprintf( stderr, " thread=%p,", req->thread ); + fprintf( stderr, " process=%p,", req->process ); fprintf( stderr, " call=" ); dump_apc_call( &req->call ); } +static void dump_queue_apc_reply( const struct queue_apc_reply *req ) +{ + fprintf( stderr, " handle=%p", req->handle ); +} + static void dump_get_apc_request( const struct get_apc_request *req ) { fprintf( stderr, " alertable=%d,", req->alertable ); @@ -3573,7 +3579,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_resume_thread_reply, (dump_func)0, (dump_func)0, - (dump_func)0, + (dump_func)dump_queue_apc_reply, (dump_func)dump_get_apc_reply, (dump_func)dump_get_apc_result_reply, (dump_func)0, @@ -4057,10 +4063,12 @@ static const struct { "PIPE_LISTENING", STATUS_PIPE_LISTENING }, { "PIPE_NOT_AVAILABLE", STATUS_PIPE_NOT_AVAILABLE }, { "PRIVILEGE_NOT_HELD", STATUS_PRIVILEGE_NOT_HELD }, + { "PROCESS_IS_TERMINATING", STATUS_PROCESS_IS_TERMINATING }, { "SECTION_TOO_BIG", STATUS_SECTION_TOO_BIG }, { "SEMAPHORE_LIMIT_EXCEEDED", STATUS_SEMAPHORE_LIMIT_EXCEEDED }, { "SHARING_VIOLATION", STATUS_SHARING_VIOLATION }, { "SUSPEND_COUNT_EXCEEDED", STATUS_SUSPEND_COUNT_EXCEEDED }, + { "THREAD_IS_TERMINATING", STATUS_THREAD_IS_TERMINATING }, { "TIMEOUT", STATUS_TIMEOUT }, { "TOO_MANY_OPENED_FILES", STATUS_TOO_MANY_OPENED_FILES }, { "UNSUCCESSFUL", STATUS_UNSUCCESSFUL },