Added APC support in waitable timers.

diff --git a/server/request.h b/server/request.h
index 7876540..1615f84 100644
--- a/server/request.h
+++ b/server/request.h
@@ -89,7 +89,7 @@
 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 @@
     (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 1899132..c00e61e 100644
--- a/server/thread.c
+++ b/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_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 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 @@
     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 @@
 /* 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 @@
     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 @@
     }
 
  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 @@
 }
 
 /* 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 @@
     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 910c330..8a59ca2 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -43,10 +43,10 @@
     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 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 ec47917..c914d93 100644
--- a/server/timer.c
+++ b/server/timer.c
@@ -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 @@
     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 @@
             timer->when.tv_usec = 0;
             timer->period       = 0;
             timer->timeout      = NULL;
+            timer->thread       = NULL;
         }
     }
     return timer;
@@ -79,6 +77,11 @@
 {
     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 @@
     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 @@
     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 79a95d8..8774c81 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -165,11 +165,11 @@
     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 @@
     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 @@
     (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 @@
     (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 @@
     "load_dll",
     "unload_dll",
     "queue_apc",
-    "get_apcs",
+    "get_apc",
     "close_handle",
     "get_handle_info",
     "set_handle_info",