server: Support queuing some APCs to a process instead of a thread.
This commit is contained in:
parent
fb40dc4099
commit
6ca1d1b081
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
125
server/thread.c
125
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 */
|
||||
|
|
|
@ -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 },
|
||||
|
|
Loading…
Reference in New Issue