Added APC support in waitable timers.
This commit is contained in:
parent
0baa4e0b3b
commit
ea1afcef49
|
@ -308,12 +308,15 @@ struct queue_apc_request
|
|||
};
|
||||
|
||||
|
||||
/* Get list of APC to call */
|
||||
struct get_apcs_request
|
||||
/* Get next APC to call */
|
||||
struct get_apc_request
|
||||
{
|
||||
OUT int count; /* number of apcs */
|
||||
OUT void* apcs[1]; /* async procedures to call */
|
||||
OUT void* func; /* function to call */
|
||||
OUT int type; /* function type */
|
||||
OUT int nb_args; /* number of arguments */
|
||||
OUT void* args[1]; /* function arguments */
|
||||
};
|
||||
enum apc_type { APC_NONE, APC_USER, APC_TIMER };
|
||||
|
||||
|
||||
/* Close a handle for the current process */
|
||||
|
@ -1212,7 +1215,7 @@ enum request
|
|||
REQ_LOAD_DLL,
|
||||
REQ_UNLOAD_DLL,
|
||||
REQ_QUEUE_APC,
|
||||
REQ_GET_APCS,
|
||||
REQ_GET_APC,
|
||||
REQ_CLOSE_HANDLE,
|
||||
REQ_GET_HANDLE_INFO,
|
||||
REQ_SET_HANDLE_INFO,
|
||||
|
@ -1303,7 +1306,7 @@ enum request
|
|||
REQ_NB_REQUESTS
|
||||
};
|
||||
|
||||
#define SERVER_PROTOCOL_VERSION 16
|
||||
#define SERVER_PROTOCOL_VERSION 17
|
||||
|
||||
/* ### make_requests end ### */
|
||||
/* Everything above this line is generated automatically by tools/make_requests */
|
||||
|
|
|
@ -2578,6 +2578,10 @@ typedef enum tagSID_NAME_USE {
|
|||
#define MUTEX_MODIFY_STATE 0x0001
|
||||
#define MUTEX_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x1)
|
||||
|
||||
#define TIMER_QUERY_STATE 0x0001
|
||||
#define TIMER_MODIFY_STATE 0x0002
|
||||
#define TIMER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3)
|
||||
|
||||
#define PROCESS_TERMINATE 0x0001
|
||||
#define PROCESS_CREATE_THREAD 0x0002
|
||||
#define PROCESS_VM_OPERATION 0x0008
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include "heap.h"
|
||||
#include "file.h" /* for DOSFS_UnixTimeToFileTime */
|
||||
#include "thread.h"
|
||||
#include "winerror.h"
|
||||
#include "syslevel.h"
|
||||
#include "server.h"
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* call_apcs
|
||||
*
|
||||
|
@ -21,24 +23,35 @@
|
|||
*/
|
||||
static void call_apcs(void)
|
||||
{
|
||||
#define MAX_APCS 16
|
||||
int i;
|
||||
void *buffer[MAX_APCS * 2];
|
||||
int count;
|
||||
struct get_apcs_request *req = get_req_buffer();
|
||||
FARPROC proc;
|
||||
struct get_apc_request *req = get_req_buffer();
|
||||
|
||||
if (server_call( REQ_GET_APCS ) || !req->count) return;
|
||||
assert( req->count <= MAX_APCS );
|
||||
|
||||
/* make a copy of the request buffer... it may get trashed *
|
||||
* when the apcs are called */
|
||||
memcpy( buffer, req->apcs, req->count * 2 * sizeof(req->apcs[0]) );
|
||||
count = req->count;
|
||||
|
||||
for (i = 0; i < count * 2; i += 2)
|
||||
for (;;)
|
||||
{
|
||||
PAPCFUNC func = (PAPCFUNC)buffer[i];
|
||||
if (func) func( (ULONG_PTR)buffer[i+1] );
|
||||
if (server_call( REQ_GET_APC )) return;
|
||||
switch(req->type)
|
||||
{
|
||||
case APC_NONE:
|
||||
return; /* no more APCs */
|
||||
case APC_USER:
|
||||
if ((proc = req->func))
|
||||
{
|
||||
proc( req->args[0] );
|
||||
}
|
||||
break;
|
||||
case APC_TIMER:
|
||||
if ((proc = req->func))
|
||||
{
|
||||
FILETIME ft;
|
||||
/* convert sec/usec to NT time */
|
||||
DOSFS_UnixTimeToFileTime( (time_t)req->args[0], &ft, (DWORD)req->args[1] * 10 );
|
||||
proc( req->args[2], ft.dwLowDateTime, ft.dwHighDateTime );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
server_protocol_error( "get_apc_request: bad type %d\n", req->type );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ DECL_HANDLER(resume_thread);
|
|||
DECL_HANDLER(load_dll);
|
||||
DECL_HANDLER(unload_dll);
|
||||
DECL_HANDLER(queue_apc);
|
||||
DECL_HANDLER(get_apcs);
|
||||
DECL_HANDLER(get_apc);
|
||||
DECL_HANDLER(close_handle);
|
||||
DECL_HANDLER(get_handle_info);
|
||||
DECL_HANDLER(set_handle_info);
|
||||
|
@ -202,7 +202,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
|
|||
(req_handler)req_load_dll,
|
||||
(req_handler)req_unload_dll,
|
||||
(req_handler)req_queue_apc,
|
||||
(req_handler)req_get_apcs,
|
||||
(req_handler)req_get_apc,
|
||||
(req_handler)req_close_handle,
|
||||
(req_handler)req_get_handle_info,
|
||||
(req_handler)req_set_handle_info,
|
||||
|
|
110
server/thread.c
110
server/thread.c
|
@ -9,6 +9,7 @@
|
|||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -23,7 +24,6 @@
|
|||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
#include "winbase.h"
|
||||
|
||||
#include "handle.h"
|
||||
|
@ -48,10 +48,14 @@ struct thread_wait
|
|||
|
||||
struct thread_apc
|
||||
{
|
||||
void *func; /* function to call in client */
|
||||
void *param; /* function param */
|
||||
struct thread_apc *next; /* queue linked list */
|
||||
struct thread_apc *prev;
|
||||
struct object *owner; /* object that queued this apc */
|
||||
void *func; /* function to call in client */
|
||||
enum apc_type type; /* type of apc function */
|
||||
int nb_args; /* number of arguments */
|
||||
void *args[1]; /* function arguments */
|
||||
};
|
||||
#define MAX_THREAD_APC 16 /* Max outstanding APCs for a thread */
|
||||
|
||||
|
||||
/* thread operations */
|
||||
|
@ -60,6 +64,7 @@ static void dump_thread( struct object *obj, int verbose );
|
|||
static int thread_signaled( struct object *obj, struct thread *thread );
|
||||
extern void thread_poll_event( struct object *obj, int event );
|
||||
static void destroy_thread( struct object *obj );
|
||||
static struct thread_apc *thread_dequeue_apc( struct thread *thread );
|
||||
|
||||
static const struct object_ops thread_ops =
|
||||
{
|
||||
|
@ -126,8 +131,8 @@ struct thread *create_thread( int fd, struct process *process )
|
|||
thread->queue = NULL;
|
||||
thread->info = NULL;
|
||||
thread->wait = NULL;
|
||||
thread->apc = NULL;
|
||||
thread->apc_count = 0;
|
||||
thread->apc_head = NULL;
|
||||
thread->apc_tail = NULL;
|
||||
thread->error = 0;
|
||||
thread->pass_fd = -1;
|
||||
thread->state = RUNNING;
|
||||
|
@ -179,6 +184,7 @@ void thread_poll_event( struct object *obj, int event )
|
|||
/* destroy a thread when its refcount is 0 */
|
||||
static void destroy_thread( struct object *obj )
|
||||
{
|
||||
struct thread_apc *apc;
|
||||
struct thread *thread = (struct thread *)obj;
|
||||
assert( obj->ops == &thread_ops );
|
||||
|
||||
|
@ -187,7 +193,7 @@ static void destroy_thread( struct object *obj )
|
|||
if (thread->next) thread->next->prev = thread->prev;
|
||||
if (thread->prev) thread->prev->next = thread->next;
|
||||
else first_thread = thread->next;
|
||||
if (thread->apc) free( thread->apc );
|
||||
while ((apc = thread_dequeue_apc( thread ))) free( apc );
|
||||
if (thread->info) release_object( thread->info );
|
||||
if (thread->queue) release_object( thread->queue );
|
||||
if (thread->buffer != (void *)-1) munmap( thread->buffer, MAX_REQUEST_LENGTH );
|
||||
|
@ -399,7 +405,7 @@ static int check_wait( struct thread *thread, struct object **object )
|
|||
}
|
||||
|
||||
other_checks:
|
||||
if ((wait->flags & SELECT_ALERTABLE) && thread->apc) return STATUS_USER_APC;
|
||||
if ((wait->flags & SELECT_ALERTABLE) && thread->apc_head) return STATUS_USER_APC;
|
||||
if (wait->flags & SELECT_TIMEOUT)
|
||||
{
|
||||
struct timeval now;
|
||||
|
@ -501,26 +507,67 @@ void wake_up( struct object *obj, int max )
|
|||
}
|
||||
|
||||
/* queue an async procedure call */
|
||||
static int thread_queue_apc( struct thread *thread, void *func, void *param )
|
||||
int thread_queue_apc( struct thread *thread, struct object *owner, void *func,
|
||||
enum apc_type type, int nb_args, ... )
|
||||
{
|
||||
struct thread_apc *apc;
|
||||
if (!thread->apc)
|
||||
|
||||
/* cancel a possible previous APC with the same owner */
|
||||
if (owner) thread_cancel_apc( thread, owner );
|
||||
|
||||
if (!(apc = mem_alloc( sizeof(*apc) + (nb_args-1)*sizeof(apc->args[0]) ))) return 0;
|
||||
apc->prev = thread->apc_tail;
|
||||
apc->next = NULL;
|
||||
apc->owner = owner;
|
||||
apc->func = func;
|
||||
apc->type = type;
|
||||
apc->nb_args = nb_args;
|
||||
if (nb_args)
|
||||
{
|
||||
if (!(thread->apc = mem_alloc( MAX_THREAD_APC * sizeof(*apc) )))
|
||||
return 0;
|
||||
thread->apc_count = 0;
|
||||
int i;
|
||||
va_list args;
|
||||
va_start( args, nb_args );
|
||||
for (i = 0; i < nb_args; i++) apc->args[i] = va_arg( args, void * );
|
||||
va_end( args );
|
||||
}
|
||||
else if (thread->apc_count >= MAX_THREAD_APC) return 0;
|
||||
thread->apc[thread->apc_count].func = func;
|
||||
thread->apc[thread->apc_count].param = param;
|
||||
thread->apc_count++;
|
||||
if (thread->wait)
|
||||
thread->apc_tail = apc;
|
||||
if (!apc->prev) /* first one */
|
||||
{
|
||||
if (wake_thread( thread )) send_reply( thread );
|
||||
thread->apc_head = apc;
|
||||
if (thread->wait && wake_thread( thread )) send_reply( thread );
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cancel the async procedure call owned by a specific object */
|
||||
void thread_cancel_apc( struct thread *thread, struct object *owner )
|
||||
{
|
||||
struct thread_apc *apc;
|
||||
for (apc = thread->apc_head; apc; apc = apc->next)
|
||||
{
|
||||
if (apc->owner != owner) continue;
|
||||
if (apc->next) apc->next->prev = apc->prev;
|
||||
else thread->apc_tail = apc->prev;
|
||||
if (apc->prev) apc->prev->next = apc->next;
|
||||
else thread->apc_head = apc->next;
|
||||
free( apc );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove the head apc from the queue; the returned pointer must be freed by the caller */
|
||||
static struct thread_apc *thread_dequeue_apc( struct thread *thread )
|
||||
{
|
||||
struct thread_apc *apc = thread->apc_head;
|
||||
if (apc)
|
||||
{
|
||||
if (apc->next) apc->next->prev = NULL;
|
||||
else thread->apc_tail = NULL;
|
||||
thread->apc_head = apc->next;
|
||||
}
|
||||
return apc;
|
||||
}
|
||||
|
||||
/* retrieve an LDT selector entry */
|
||||
static void get_selector_entry( struct thread *thread, int entry,
|
||||
unsigned int *base, unsigned int *limit,
|
||||
|
@ -749,20 +796,29 @@ DECL_HANDLER(queue_apc)
|
|||
struct thread *thread;
|
||||
if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT )))
|
||||
{
|
||||
thread_queue_apc( thread, req->func, req->param );
|
||||
thread_queue_apc( thread, NULL, req->func, APC_USER, 1, req->param );
|
||||
release_object( thread );
|
||||
}
|
||||
}
|
||||
|
||||
/* get list of APC to call */
|
||||
DECL_HANDLER(get_apcs)
|
||||
/* get next APC to call */
|
||||
DECL_HANDLER(get_apc)
|
||||
{
|
||||
if ((req->count = current->apc_count))
|
||||
struct thread_apc *apc;
|
||||
|
||||
if ((apc = thread_dequeue_apc( current )))
|
||||
{
|
||||
memcpy( req->apcs, current->apc, current->apc_count * sizeof(*current->apc) );
|
||||
free( current->apc );
|
||||
current->apc = NULL;
|
||||
current->apc_count = 0;
|
||||
req->func = apc->func;
|
||||
req->type = apc->type;
|
||||
req->nb_args = apc->nb_args;
|
||||
memcpy( req->args, apc->args, apc->nb_args * sizeof(req->args[0]) );
|
||||
free( apc );
|
||||
}
|
||||
else
|
||||
{
|
||||
req->func = NULL;
|
||||
req->type = APC_NONE;
|
||||
req->nb_args = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,10 +43,10 @@ struct thread
|
|||
struct debug_ctx *debug_ctx; /* debugger context if this thread is a debugger */
|
||||
struct debug_event *debug_event; /* debug event being sent to debugger */
|
||||
struct msg_queue *queue; /* message queue */
|
||||
struct startup_info*info; /* startup info for child process */
|
||||
struct thread_wait *wait; /* current wait condition if sleeping */
|
||||
struct thread_apc *apc; /* list of async procedure calls */
|
||||
int apc_count; /* number of outstanding APCs */
|
||||
struct startup_info*info; /* startup info for child process */
|
||||
struct thread_wait *wait; /* current wait condition if sleeping */
|
||||
struct thread_apc *apc_head; /* queue of async procedure calls */
|
||||
struct thread_apc *apc_tail; /* queue of async procedure calls */
|
||||
int error; /* current error code */
|
||||
int pass_fd; /* fd to pass to the client */
|
||||
enum run_state state; /* running state */
|
||||
|
@ -90,6 +90,9 @@ extern void kill_thread( struct thread *thread, int violent_death );
|
|||
extern void wake_up( struct object *obj, int max );
|
||||
extern int sleep_on( int count, struct object *objects[], int flags,
|
||||
int timeout, sleep_reply func );
|
||||
extern int thread_queue_apc( struct thread *thread, struct object *owner, void *func,
|
||||
enum apc_type type, int nb_args, ... );
|
||||
extern void thread_cancel_apc( struct thread *thread, struct object *owner );
|
||||
extern struct thread_snapshot *thread_snap( int *count );
|
||||
|
||||
/* ptrace functions */
|
||||
|
|
|
@ -10,14 +10,10 @@
|
|||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "winnt.h"
|
||||
#include "handle.h"
|
||||
#include "request.h"
|
||||
|
||||
/* FIXME: check values and move to standard header */
|
||||
#define TIMER_MODIFY_STATE 0x0001
|
||||
#define TIMER_QUERY_STATE 0x0002
|
||||
#define TIMER_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x3)
|
||||
|
||||
struct timer
|
||||
{
|
||||
struct object obj; /* object header */
|
||||
|
@ -26,6 +22,7 @@ struct timer
|
|||
int period; /* timer period in ms */
|
||||
struct timeval when; /* next expiration */
|
||||
struct timeout_user *timeout; /* timeout user */
|
||||
struct thread *thread; /* thread that set the APC function */
|
||||
void *callback; /* callback APC function */
|
||||
void *arg; /* callback argument */
|
||||
};
|
||||
|
@ -69,6 +66,7 @@ static struct timer *create_timer( const WCHAR *name, size_t len, int manual )
|
|||
timer->when.tv_usec = 0;
|
||||
timer->period = 0;
|
||||
timer->timeout = NULL;
|
||||
timer->thread = NULL;
|
||||
}
|
||||
}
|
||||
return timer;
|
||||
|
@ -79,6 +77,11 @@ static void timer_callback( void *private )
|
|||
{
|
||||
struct timer *timer = (struct timer *)private;
|
||||
|
||||
/* queue an APC */
|
||||
if (timer->thread)
|
||||
thread_queue_apc( timer->thread, &timer->obj, timer->callback, APC_TIMER, 3,
|
||||
(void *)timer->when.tv_sec, (void *)timer->when.tv_usec, timer->arg );
|
||||
|
||||
if (timer->period) /* schedule the next expiration */
|
||||
{
|
||||
add_timeout( &timer->when, timer->period );
|
||||
|
@ -91,16 +94,31 @@ static void timer_callback( void *private )
|
|||
wake_up( &timer->obj, 0 );
|
||||
}
|
||||
|
||||
/* cancel a running timer */
|
||||
static void cancel_timer( struct timer *timer )
|
||||
{
|
||||
if (timer->timeout)
|
||||
{
|
||||
remove_timeout_user( timer->timeout );
|
||||
timer->timeout = NULL;
|
||||
}
|
||||
if (timer->thread)
|
||||
{
|
||||
thread_cancel_apc( timer->thread, &timer->obj );
|
||||
timer->thread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* set the timer expiration and period */
|
||||
static void set_timer( struct timer *timer, int sec, int usec, int period,
|
||||
void *callback, void *arg )
|
||||
{
|
||||
cancel_timer( timer );
|
||||
if (timer->manual)
|
||||
{
|
||||
period = 0; /* period doesn't make any sense for a manual timer */
|
||||
timer->signaled = 0;
|
||||
}
|
||||
if (timer->timeout) remove_timeout_user( timer->timeout );
|
||||
if (!sec && !usec)
|
||||
{
|
||||
/* special case: use now + period as first expiration */
|
||||
|
@ -115,19 +133,10 @@ static void set_timer( struct timer *timer, int sec, int usec, int period,
|
|||
timer->period = period;
|
||||
timer->callback = callback;
|
||||
timer->arg = arg;
|
||||
if (callback) timer->thread = current;
|
||||
timer->timeout = add_timeout_user( &timer->when, timer_callback, timer );
|
||||
}
|
||||
|
||||
/* cancel a running timer */
|
||||
static void cancel_timer( struct timer *timer )
|
||||
{
|
||||
if (timer->timeout)
|
||||
{
|
||||
remove_timeout_user( timer->timeout );
|
||||
timer->timeout = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_dump( struct object *obj, int verbose )
|
||||
{
|
||||
struct timer *timer = (struct timer *)obj;
|
||||
|
|
|
@ -165,11 +165,11 @@ static void dump_varargs_select_request( const struct select_request *req )
|
|||
dump_ints( req->handles, count );
|
||||
}
|
||||
|
||||
static void dump_varargs_get_apcs_reply( const struct get_apcs_request *req )
|
||||
static void dump_varargs_get_apc_reply( const struct get_apc_request *req )
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 2 * req->count; i++)
|
||||
fprintf( stderr, "%c%p", i ? ',' : '{', req->apcs[i] );
|
||||
for (i = 0; i < req->nb_args; i++)
|
||||
fprintf( stderr, "%c%p", i ? ',' : '{', req->args[i] );
|
||||
fprintf( stderr, "}" );
|
||||
}
|
||||
|
||||
|
@ -422,15 +422,17 @@ static void dump_queue_apc_request( const struct queue_apc_request *req )
|
|||
fprintf( stderr, " param=%p", req->param );
|
||||
}
|
||||
|
||||
static void dump_get_apcs_request( const struct get_apcs_request *req )
|
||||
static void dump_get_apc_request( const struct get_apc_request *req )
|
||||
{
|
||||
}
|
||||
|
||||
static void dump_get_apcs_reply( const struct get_apcs_request *req )
|
||||
static void dump_get_apc_reply( const struct get_apc_request *req )
|
||||
{
|
||||
fprintf( stderr, " count=%d,", req->count );
|
||||
fprintf( stderr, " apcs=" );
|
||||
dump_varargs_get_apcs_reply( req );
|
||||
fprintf( stderr, " func=%p,", req->func );
|
||||
fprintf( stderr, " type=%d,", req->type );
|
||||
fprintf( stderr, " nb_args=%d,", req->nb_args );
|
||||
fprintf( stderr, " args=" );
|
||||
dump_varargs_get_apc_reply( req );
|
||||
}
|
||||
|
||||
static void dump_close_handle_request( const struct close_handle_request *req )
|
||||
|
@ -1394,7 +1396,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
|
|||
(dump_func)dump_load_dll_request,
|
||||
(dump_func)dump_unload_dll_request,
|
||||
(dump_func)dump_queue_apc_request,
|
||||
(dump_func)dump_get_apcs_request,
|
||||
(dump_func)dump_get_apc_request,
|
||||
(dump_func)dump_close_handle_request,
|
||||
(dump_func)dump_get_handle_info_request,
|
||||
(dump_func)dump_set_handle_info_request,
|
||||
|
@ -1504,7 +1506,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
|
|||
(dump_func)0,
|
||||
(dump_func)0,
|
||||
(dump_func)0,
|
||||
(dump_func)dump_get_apcs_reply,
|
||||
(dump_func)dump_get_apc_reply,
|
||||
(dump_func)0,
|
||||
(dump_func)dump_get_handle_info_reply,
|
||||
(dump_func)0,
|
||||
|
@ -1614,7 +1616,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
|
|||
"load_dll",
|
||||
"unload_dll",
|
||||
"queue_apc",
|
||||
"get_apcs",
|
||||
"get_apc",
|
||||
"close_handle",
|
||||
"get_handle_info",
|
||||
"set_handle_info",
|
||||
|
|
Loading…
Reference in New Issue