diff --git a/include/server.h b/include/server.h index e6ca2e597b8..762e2724a81 100644 --- a/include/server.h +++ b/include/server.h @@ -340,6 +340,7 @@ struct queue_apc_request { REQUEST_HEADER; /* request header */ IN handle_t handle; /* thread handle */ + IN int user; /* user or system apc? */ IN void* func; /* function to call */ IN void* param; /* param for function to call */ }; @@ -349,6 +350,7 @@ struct queue_apc_request struct get_apc_request { REQUEST_HEADER; /* request header */ + IN int alertable; /* is thread alertable? */ OUT void* func; /* function to call */ OUT int type; /* function type */ OUT VARARG(args,ptrs); /* function arguments */ @@ -412,13 +414,15 @@ struct select_request { REQUEST_HEADER; /* request header */ IN int flags; /* wait flags (see below) */ - IN int timeout; /* timeout in ms */ + IN int sec; /* absolute timeout */ + IN int usec; /* absolute timeout */ OUT int signaled; /* signaled handle */ IN VARARG(handles,handles); /* handles to select on */ }; -#define SELECT_ALL 1 -#define SELECT_ALERTABLE 2 -#define SELECT_TIMEOUT 4 +#define SELECT_ALL 1 +#define SELECT_ALERTABLE 2 +#define SELECT_INTERRUPTIBLE 4 +#define SELECT_TIMEOUT 8 /* Create an event */ @@ -1576,7 +1580,7 @@ union generic_request struct async_result_request async_result; }; -#define SERVER_PROTOCOL_VERSION 33 +#define SERVER_PROTOCOL_VERSION 34 /* ### make_requests end ### */ /* Everything above this line is generated automatically by tools/make_requests */ diff --git a/scheduler/synchro.c b/scheduler/synchro.c index 5718b1afa33..6d42029a5d3 100644 --- a/scheduler/synchro.c +++ b/scheduler/synchro.c @@ -15,12 +15,31 @@ #include "server.h" +/*********************************************************************** + * get_timeout + */ +inline static void get_timeout( struct timeval *when, int timeout ) +{ + gettimeofday( when, 0 ); + if (timeout) + { + long sec = timeout / 1000; + if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000) + { + when->tv_usec -= 1000000; + when->tv_sec++; + } + when->tv_sec += sec; + } +} + + /*********************************************************************** * call_apcs * * Call outstanding APCs. */ -static void call_apcs(void) +static void call_apcs( BOOL alertable ) { FARPROC proc = NULL; FILETIME ft; @@ -32,6 +51,7 @@ static void call_apcs(void) SERVER_START_REQ { struct get_apc_request *req = server_alloc_req( sizeof(*req), sizeof(args) ); + req->alertable = alertable; if (!server_call( REQ_GET_APC )) { type = req->type; @@ -119,6 +139,7 @@ DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles, BOOL alertable ) { int i, ret; + struct timeval tv; if (count > MAXIMUM_WAIT_OBJECTS) { @@ -126,24 +147,33 @@ DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles, return WAIT_FAILED; } - SERVER_START_REQ + if (timeout == INFINITE) tv.tv_sec = tv.tv_usec = 0; + else get_timeout( &tv, timeout ); + + for (;;) { - struct select_request *req = server_alloc_req( sizeof(*req), count * sizeof(int) ); - int *data = server_data_ptr( req ); + SERVER_START_REQ + { + struct select_request *req = server_alloc_req( sizeof(*req), count * sizeof(int) ); + int *data = server_data_ptr( req ); - req->flags = 0; - req->timeout = timeout; - for (i = 0; i < count; i++) data[i] = handles[i]; + req->flags = SELECT_INTERRUPTIBLE; + req->sec = tv.tv_sec; + req->usec = tv.tv_usec; + for (i = 0; i < count; i++) data[i] = handles[i]; - if (wait_all) req->flags |= SELECT_ALL; - if (alertable) req->flags |= SELECT_ALERTABLE; - if (timeout != INFINITE) req->flags |= SELECT_TIMEOUT; + if (wait_all) req->flags |= SELECT_ALL; + if (alertable) req->flags |= SELECT_ALERTABLE; + if (timeout != INFINITE) req->flags |= SELECT_TIMEOUT; - server_call( REQ_SELECT ); - ret = req->signaled; + server_call( REQ_SELECT ); + ret = req->signaled; + } + SERVER_END_REQ; + if (ret != STATUS_USER_APC) break; + call_apcs( alertable ); + if (alertable) break; } - SERVER_END_REQ; - if (ret == STATUS_USER_APC) call_apcs(); return ret; } diff --git a/scheduler/thread.c b/scheduler/thread.c index cea1156f542..3735a11065e 100644 --- a/scheduler/thread.c +++ b/scheduler/thread.c @@ -638,6 +638,7 @@ DWORD WINAPI QueueUserAPC( PAPCFUNC func, HANDLE hthread, ULONG_PTR data ) { struct queue_apc_request *req = server_alloc_req( sizeof(*req), 0 ); req->handle = hthread; + req->user = 1; req->func = func; req->param = (void *)data; ret = !server_call( REQ_QUEUE_APC ); diff --git a/server/async.c b/server/async.c index e740adddc05..838cc6b579b 100644 --- a/server/async.c +++ b/server/async.c @@ -213,25 +213,25 @@ DECL_HANDLER(create_async) static void async_poll_event( struct object *obj, int event ) { struct async *ov = (struct async *) obj; - + /* queue an APC in the client thread to do our dirty work */ ov->obj.ops->remove_queue(&ov->obj,&ov->wait); /* FIXME: this should be a function pointer */ event = serial_async_poll_event(obj,event); - thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 3, + thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 1, 3, ov->client_overlapped, ov->buffer, event); -} +} /* handler for async i/o timeouts */ static void overlapped_timeout (void *private) { struct async *ov = (struct async *) private; - + ov->obj.ops->remove_queue(&ov->obj,&ov->wait); - - thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 3, + + thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 1, 3, ov->client_overlapped,ov->buffer, 0); } diff --git a/server/debugger.c b/server/debugger.c index 202f0dd8e55..32ca8d542a7 100644 --- a/server/debugger.c +++ b/server/debugger.c @@ -363,14 +363,21 @@ static int wait_for_debug_event( int timeout ) struct debug_ctx *debug_ctx = current->debug_ctx; struct object *obj = &debug_ctx->obj; int flags = 0; + struct timeval tv; if (!debug_ctx) /* current thread is not a debugger */ { set_error( STATUS_INVALID_HANDLE ); return 0; } - if (timeout != -1) flags = SELECT_TIMEOUT; - return sleep_on( 1, &obj, flags, timeout, build_wait_debug_reply ); + if (timeout != -1) + { + flags = SELECT_TIMEOUT; + gettimeofday( &tv, 0 ); + add_timeout( &tv, timeout ); + } + else tv.tv_sec = tv.tv_usec = 0; + return sleep_on( 1, &obj, flags, tv.tv_sec, tv.tv_usec, build_wait_debug_reply ); } /* continue a debug event */ @@ -603,7 +610,7 @@ DECL_HANDLER(exception_event) { struct object *obj = &event->obj; current->context = context; - sleep_on( 1, &obj, 0, -1, build_exception_event_reply ); + sleep_on( 1, &obj, 0, 0, 0, build_exception_event_reply ); release_object( event ); } } diff --git a/server/process.c b/server/process.c index 1a84c4017af..bab104333b1 100644 --- a/server/process.c +++ b/server/process.c @@ -771,8 +771,12 @@ DECL_HANDLER(wait_process) } else { + struct timeval timeout; struct object *obj = ¤t->info->obj; - sleep_on( 1, &obj, SELECT_TIMEOUT, req->timeout, build_wait_process_reply ); + gettimeofday( &timeout, 0 ); + add_timeout( &timeout, req->timeout ); + sleep_on( 1, &obj, SELECT_TIMEOUT, timeout.tv_sec, timeout.tv_usec, + build_wait_process_reply ); } } diff --git a/server/thread.c b/server/thread.c index fceca0219dd..31e3ff090dd 100644 --- a/server/thread.c +++ b/server/thread.c @@ -64,7 +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 struct thread_apc *thread_dequeue_apc( struct thread *thread, int system_only ); static const struct object_ops thread_ops = { @@ -147,8 +147,10 @@ struct thread *create_thread( int fd, struct process *process ) thread->queue = NULL; thread->info = NULL; thread->wait = NULL; - thread->apc_head = NULL; - thread->apc_tail = NULL; + thread->system_apc.head = NULL; + thread->system_apc.tail = NULL; + thread->user_apc.head = NULL; + thread->user_apc.tail = NULL; thread->error = 0; thread->pass_fd = -1; thread->request_fd = NULL; @@ -211,7 +213,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; - while ((apc = thread_dequeue_apc( thread ))) free( apc ); + while ((apc = thread_dequeue_apc( thread, 0 ))) 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 ); @@ -354,7 +356,7 @@ static void end_wait( struct thread *thread ) /* build the thread wait structure */ static int wait_on( int count, struct object *objects[], int flags, - int timeout, sleep_reply func ) + int sec, int usec, sleep_reply func ) { struct thread_wait *wait; struct wait_queue_entry *entry; @@ -368,8 +370,8 @@ static int wait_on( int count, struct object *objects[], int flags, wait->reply = func; if (flags & SELECT_TIMEOUT) { - gettimeofday( &wait->timeout, 0 ); - add_timeout( &wait->timeout, timeout ); + wait->timeout.tv_sec = sec; + wait->timeout.tv_usec = usec; } for (i = 0, entry = wait->queues; i < count; i++, entry++) @@ -425,7 +427,8 @@ static int check_wait( struct thread *thread, struct object **object ) } other_checks: - if ((wait->flags & SELECT_ALERTABLE) && thread->apc_head) return STATUS_USER_APC; + if ((wait->flags & SELECT_INTERRUPTIBLE) && thread->system_apc.head) return STATUS_USER_APC; + if ((wait->flags & SELECT_ALERTABLE) && thread->user_apc.head) return STATUS_USER_APC; if (wait->flags & SELECT_TIMEOUT) { struct timeval now; @@ -469,10 +472,10 @@ static void thread_timeout( void *ptr ) } /* sleep on a list of objects */ -int sleep_on( int count, struct object *objects[], int flags, int timeout, sleep_reply func ) +int sleep_on( int count, struct object *objects[], int flags, int sec, int usec, sleep_reply func ) { assert( !current->wait ); - if (!wait_on( count, objects, flags, timeout, func )) return 0; + if (!wait_on( count, objects, flags, sec, usec, func )) return 0; if (wake_thread( current )) return 1; /* now we need to wait */ if (flags & SELECT_TIMEOUT) @@ -488,7 +491,7 @@ int sleep_on( int count, struct object *objects[], int flags, int timeout, sleep } /* select on a list of handles */ -static int select_on( int count, handle_t *handles, int flags, int timeout ) +static int select_on( int count, handle_t *handles, int flags, int sec, int usec ) { int ret = 0; int i; @@ -504,7 +507,7 @@ static int select_on( int count, handle_t *handles, int flags, int timeout ) if (!(objects[i] = get_handle_obj( current->process, handles[i], SYNCHRONIZE, NULL ))) break; } - if (i == count) ret = sleep_on( count, objects, flags, timeout, build_select_reply ); + if (i == count) ret = sleep_on( count, objects, flags, sec, usec, build_select_reply ); while (--i >= 0) release_object( objects[i] ); return ret; } @@ -528,15 +531,16 @@ void wake_up( struct object *obj, int max ) /* queue an async procedure call */ int thread_queue_apc( struct thread *thread, struct object *owner, void *func, - enum apc_type type, int nb_args, ... ) + enum apc_type type, int system, int nb_args, ... ) { struct thread_apc *apc; + struct apc_queue *queue = system ? &thread->system_apc : &thread->user_apc; /* cancel a possible previous APC with the same owner */ - if (owner) thread_cancel_apc( thread, owner ); + if (owner) thread_cancel_apc( thread, owner, system ); if (!(apc = mem_alloc( sizeof(*apc) + (nb_args-1)*sizeof(apc->args[0]) ))) return 0; - apc->prev = thread->apc_tail; + apc->prev = queue->tail; apc->next = NULL; apc->owner = owner; apc->func = func; @@ -550,40 +554,44 @@ int thread_queue_apc( struct thread *thread, struct object *owner, void *func, for (i = 0; i < nb_args; i++) apc->args[i] = va_arg( args, void * ); va_end( args ); } - thread->apc_tail = apc; + queue->tail = apc; if (!apc->prev) /* first one */ { - thread->apc_head = apc; + queue->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 ) +void thread_cancel_apc( struct thread *thread, struct object *owner, int system ) { struct thread_apc *apc; - for (apc = thread->apc_head; apc; apc = apc->next) + struct apc_queue *queue = system ? &thread->system_apc : &thread->user_apc; + for (apc = queue->head; apc; apc = apc->next) { if (apc->owner != owner) continue; if (apc->next) apc->next->prev = apc->prev; - else thread->apc_tail = apc->prev; + else queue->tail = apc->prev; if (apc->prev) apc->prev->next = apc->next; - else thread->apc_head = apc->next; + else queue->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 ) +static struct thread_apc *thread_dequeue_apc( struct thread *thread, int system_only ) { - struct thread_apc *apc = thread->apc_head; - if (apc) + struct thread_apc *apc; + struct apc_queue *queue = &thread->system_apc; + + if (!queue->head && !system_only) queue = &thread->user_apc; + if ((apc = queue->head)) { if (apc->next) apc->next->prev = NULL; - else thread->apc_tail = NULL; - thread->apc_head = apc->next; + else queue->tail = NULL; + queue->head = apc->next; } return apc; } @@ -811,7 +819,7 @@ DECL_HANDLER(resume_thread) DECL_HANDLER(select) { int count = get_req_data_size(req) / sizeof(int); - if (!select_on( count, get_req_data(req), req->flags, req->timeout )) + if (!select_on( count, get_req_data(req), req->flags, req->sec, req->usec )) req->signaled = -1; } @@ -821,7 +829,7 @@ DECL_HANDLER(queue_apc) struct thread *thread; if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT ))) { - thread_queue_apc( thread, NULL, req->func, APC_USER, 1, req->param ); + thread_queue_apc( thread, NULL, req->func, APC_USER, !req->user, 1, req->param ); release_object( thread ); } } @@ -834,7 +842,7 @@ DECL_HANDLER(get_apc) for (;;) { - if (!(apc = thread_dequeue_apc( current ))) + if (!(apc = thread_dequeue_apc( current, !req->alertable ))) { /* no more APCs */ req->func = NULL; diff --git a/server/thread.h b/server/thread.h index 0eea735db4e..4b5aa11fc05 100644 --- a/server/thread.h +++ b/server/thread.h @@ -30,6 +30,11 @@ enum run_state TERMINATED /* terminated */ }; +struct apc_queue +{ + struct thread_apc *head; + struct thread_apc *tail; +}; struct thread { @@ -45,8 +50,8 @@ struct thread 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_head; /* queue of async procedure calls */ - struct thread_apc *apc_tail; /* queue of async procedure calls */ + struct apc_queue system_apc; /* queue of system async procedure calls */ + struct apc_queue user_apc; /* queue of user async procedure calls */ unsigned int error; /* current error code */ struct object *request_fd; /* fd for receiving client requests */ int pass_fd; /* fd to pass to the client */ @@ -92,10 +97,10 @@ extern void remove_queue( struct object *obj, struct wait_queue_entry *entry ); 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 ); + int sec, int usec, 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 ); + enum apc_type type, int system, int nb_args, ... ); +extern void thread_cancel_apc( struct thread *thread, struct object *owner, int system ); extern struct thread_snapshot *thread_snap( int *count ); /* ptrace functions */ diff --git a/server/timer.c b/server/timer.c index f4b159f7587..a76ce414e08 100644 --- a/server/timer.c +++ b/server/timer.c @@ -78,7 +78,7 @@ static void timer_callback( void *private ) /* queue an APC */ if (timer->thread) - thread_queue_apc( timer->thread, &timer->obj, timer->callback, APC_TIMER, 3, + thread_queue_apc( timer->thread, &timer->obj, timer->callback, APC_TIMER, 0, 3, (void *)timer->when.tv_sec, (void *)timer->when.tv_usec, timer->arg ); if (timer->period) /* schedule the next expiration */ @@ -103,7 +103,7 @@ static void cancel_timer( struct timer *timer ) } if (timer->thread) { - thread_cancel_apc( timer->thread, &timer->obj ); + thread_cancel_apc( timer->thread, &timer->obj, 0 ); timer->thread = NULL; } } diff --git a/server/trace.c b/server/trace.c index 59d9a29b744..76c7bdf0a11 100644 --- a/server/trace.c +++ b/server/trace.c @@ -466,12 +466,14 @@ 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=%d,", req->handle ); + fprintf( stderr, " user=%d,", req->user ); fprintf( stderr, " func=%p,", req->func ); fprintf( stderr, " param=%p", req->param ); } static void dump_get_apc_request( const struct get_apc_request *req ) { + fprintf( stderr, " alertable=%d", req->alertable ); } static void dump_get_apc_reply( const struct get_apc_request *req ) @@ -537,7 +539,8 @@ static void dump_open_process_reply( const struct open_process_request *req ) static void dump_select_request( const struct select_request *req ) { fprintf( stderr, " flags=%d,", req->flags ); - fprintf( stderr, " timeout=%d,", req->timeout ); + fprintf( stderr, " sec=%d,", req->sec ); + fprintf( stderr, " usec=%d,", req->usec ); fprintf( stderr, " handles=" ); cur_pos += dump_varargs_handles( req ); }