Commit 6ca1d1b0 authored by Alexandre Julliard's avatar Alexandre Julliard

server: Support queuing some APCs to a process instead of a thread.

parent fb40dc40
......@@ -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 );
......
......@@ -715,6 +715,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
*
* Implementation of NtWaitForMultipleObjects
......
......@@ -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;
......
......@@ -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 */
......@@ -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
......
......@@ -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 */
......
......@@ -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 },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment