From ea1afcef499ac29f3142f66a853e1a5a205ac366 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 22 Aug 2000 20:08:37 +0000 Subject: [PATCH] Added APC support in waitable timers. --- include/server.h | 15 +++--- include/winnt.h | 4 ++ scheduler/synchro.c | 45 +++++++++++------- server/request.h | 4 +- server/thread.c | 110 +++++++++++++++++++++++++++++++++----------- server/thread.h | 11 +++-- server/timer.c | 41 ++++++++++------- server/trace.c | 24 +++++----- 8 files changed, 172 insertions(+), 82 deletions(-) diff --git a/include/server.h b/include/server.h index 4ce1a19f146..8c18e3f071f 100644 --- a/include/server.h +++ b/include/server.h @@ -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 */ diff --git a/include/winnt.h b/include/winnt.h index c73531678e2..33fccd5a83e 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -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 diff --git a/scheduler/synchro.c b/scheduler/synchro.c index 0f0f2faf491..9bcf55f206f 100644 --- a/scheduler/synchro.c +++ b/scheduler/synchro.c @@ -9,11 +9,13 @@ #include #include #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; + } } } diff --git a/server/request.h b/server/request.h index 7876540f18a..1615f84ef74 100644 --- a/server/request.h +++ b/server/request.h @@ -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, diff --git a/server/thread.c b/server/thread.c index 18991324f4c..c00e61efed6 100644 --- a/server/thread.c +++ b/server/thread.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -23,7 +24,6 @@ #include #include - #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; } } diff --git a/server/thread.h b/server/thread.h index 910c330f30d..8a59ca2eb0b 100644 --- a/server/thread.h +++ b/server/thread.h @@ -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 */ diff --git a/server/timer.c b/server/timer.c index ec47917a005..c914d93a56a 100644 --- a/server/timer.c +++ b/server/timer.c @@ -10,14 +10,10 @@ #include #include +#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; diff --git a/server/trace.c b/server/trace.c index 79a95d8d7f5..8774c81b7c2 100644 --- a/server/trace.c +++ b/server/trace.c @@ -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",