Added support for nested server waits (to allow waiting in signal
handlers).
diff --git a/server/request.c b/server/request.c
index 4072fd1..348db97 100644
--- a/server/request.c
+++ b/server/request.c
@@ -277,20 +277,6 @@
return -1;
}
-/* send the wakeup signal to a thread */
-int send_thread_wakeup( struct thread *thread, int signaled )
-{
- int ret = write( thread->wait_fd, &signaled, sizeof(signaled) );
- if (ret == sizeof(signaled)) return 0;
- if (ret >= 0)
- fatal_protocol_error( thread, "partial wakeup write %d\n", ret );
- else if (errno == EPIPE)
- kill_thread( thread, 0 ); /* normal death */
- else
- fatal_protocol_perror( thread, "write" );
- return -1;
-}
-
/* send an fd to a client */
int send_client_fd( struct process *process, int fd, handle_t handle )
{
diff --git a/server/request.h b/server/request.h
index 1c7b4fb..a24d04b 100644
--- a/server/request.h
+++ b/server/request.h
@@ -24,15 +24,19 @@
#ifdef __GNUC__
extern void fatal_protocol_error( struct thread *thread,
const char *err, ... ) __attribute__((format (printf,2,3)));
+extern void fatal_protocol_perror( struct thread *thread,
+ const char *err, ... ) __attribute__((format (printf,2,3)));
+extern void fatal_error( const char *err, ... ) __attribute__((noreturn,format(printf,1,2)));
+extern void fatal_perror( const char *err, ... ) __attribute__((noreturn,format(printf,1,2)));
#else
extern void fatal_protocol_error( struct thread *thread, const char *err, ... );
+extern void fatal_protocol_perror( struct thread *thread, const char *err, ... );
+extern void fatal_error( const char *err, ... );
+extern void fatal_perror( const char *err, ... );
#endif
-extern void fatal_error( const char *err, ... ) WINE_NORETURN;
-extern void fatal_perror( const char *err, ... ) WINE_NORETURN;
extern const char *get_config_dir(void);
extern int receive_fd( struct process *process );
-extern int send_thread_wakeup( struct thread *thread, int signaled );
extern int send_client_fd( struct process *process, int fd, handle_t handle );
extern void read_request( struct thread *thread );
extern void send_reply( struct thread *thread, union generic_request *request );
diff --git a/server/thread.c b/server/thread.c
index 60363c7..ed01952 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -7,6 +7,7 @@
#include "config.h"
#include <assert.h>
+#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
@@ -32,8 +33,11 @@
struct thread_wait
{
+ struct thread_wait *next; /* next wait structure for this thread */
+ struct thread *thread; /* owner thread */
int count; /* count of objects */
int flags;
+ void *cookie; /* magic cookie to return to client */
struct timeval timeout;
struct timeout_user *user;
struct wait_queue_entry queues[1];
@@ -325,8 +329,8 @@
for (i = 0, entry = wait->queues; i < wait->count; i++, entry++)
entry->obj->ops->remove_queue( entry->obj, entry );
if (wait->user) remove_timeout_user( wait->user );
+ thread->wait = wait->next;
free( wait );
- thread->wait = NULL;
}
/* build the thread wait structure */
@@ -337,10 +341,12 @@
int i;
if (!(wait = mem_alloc( sizeof(*wait) + (count-1) * sizeof(*entry) ))) return 0;
- current->wait = wait;
+ wait->next = current->wait;
+ wait->thread = current;
wait->count = count;
wait->flags = flags;
wait->user = NULL;
+ current->wait = wait;
if (flags & SELECT_TIMEOUT)
{
wait->timeout.tv_sec = sec;
@@ -409,41 +415,68 @@
return -1;
}
+/* send the wakeup signal to a thread */
+static int send_thread_wakeup( struct thread *thread, void *cookie, int signaled )
+{
+ struct wake_up_reply reply;
+ int ret;
+
+ reply.cookie = cookie;
+ reply.signaled = signaled;
+ if ((ret = write( thread->wait_fd, &reply, sizeof(reply) )) == sizeof(reply)) return 0;
+ if (ret >= 0)
+ fatal_protocol_error( thread, "partial wakeup write %d\n", ret );
+ else if (errno == EPIPE)
+ kill_thread( thread, 0 ); /* normal death */
+ else
+ fatal_protocol_perror( thread, "write" );
+ return -1;
+}
+
/* attempt to wake up a thread */
-/* return 1 if OK, 0 if the wait condition is still not satisfied */
+/* return >0 if OK, 0 if the wait condition is still not satisfied */
static int wake_thread( struct thread *thread )
{
- int signaled;
- if ((signaled = check_wait( thread )) == -1) return 0;
+ int signaled, count;
+ void *cookie;
- if (debug_level) fprintf( stderr, "%08x: *wakeup* object=%d\n",
- (unsigned int)thread, signaled );
- end_wait( thread );
- send_thread_wakeup( thread, signaled );
- return 1;
+ for (count = 0; thread->wait; count++)
+ {
+ if ((signaled = check_wait( thread )) == -1) break;
+
+ cookie = thread->wait->cookie;
+ if (debug_level) fprintf( stderr, "%08x: *wakeup* signaled=%d cookie=%p\n",
+ (unsigned int)thread, signaled, cookie );
+ end_wait( thread );
+ send_thread_wakeup( thread, cookie, signaled );
+ }
+ return count;
}
/* thread wait timeout */
static void thread_timeout( void *ptr )
{
- struct thread *thread = ptr;
+ struct thread_wait *wait = ptr;
+ struct thread *thread = wait->thread;
+ void *cookie = wait->cookie;
- if (debug_level) fprintf( stderr, "%08x: *timeout*\n", (unsigned int)thread );
+ wait->user = NULL;
+ if (thread->wait != wait) return; /* not the top-level wait, ignore it */
- assert( thread->wait );
- thread->wait->user = NULL;
+ if (debug_level) fprintf( stderr, "%08x: *wakeup* signaled=%d cookie=%p\n",
+ (unsigned int)thread, STATUS_TIMEOUT, cookie );
end_wait( thread );
- send_thread_wakeup( thread, STATUS_TIMEOUT );
+ send_thread_wakeup( thread, cookie, STATUS_TIMEOUT );
+ /* check if other objects have become signaled in the meantime */
+ wake_thread( thread );
}
/* select on a list of handles */
-static void select_on( int count, handle_t *handles, int flags, int sec, int usec )
+static void select_on( int count, void *cookie, handle_t *handles, int flags, int sec, int usec )
{
int ret, i;
struct object *objects[MAXIMUM_WAIT_OBJECTS];
- assert( !current->wait );
-
if ((count < 0) || (count > MAXIMUM_WAIT_OBJECTS))
{
set_error( STATUS_INVALID_PARAMETER );
@@ -470,12 +503,13 @@
if (flags & SELECT_TIMEOUT)
{
if (!(current->wait->user = add_timeout_user( ¤t->wait->timeout,
- thread_timeout, current )))
+ thread_timeout, current->wait )))
{
end_wait( current );
goto done;
}
}
+ current->wait->cookie = cookie;
set_error( STATUS_PENDING );
done:
@@ -527,7 +561,7 @@
if (!apc->prev) /* first one */
{
queue->head = apc;
- if (thread->wait) wake_thread( thread );
+ wake_thread( thread );
}
return 1;
}
@@ -661,7 +695,8 @@
(unsigned int)thread, thread->exit_code );
if (thread->wait)
{
- end_wait( thread );
+ while (thread->wait) end_wait( thread );
+ send_thread_wakeup( thread, NULL, STATUS_PENDING );
/* if it is waiting on the socket, we don't need to send a SIGTERM */
violent_death = 0;
}
@@ -886,7 +921,7 @@
DECL_HANDLER(select)
{
int count = get_req_data_size(req) / sizeof(int);
- select_on( count, get_req_data(req), req->flags, req->sec, req->usec );
+ select_on( count, req->cookie, get_req_data(req), req->flags, req->sec, req->usec );
}
/* queue an APC for a thread */
diff --git a/server/trace.c b/server/trace.c
index 14fa6f2..2b9d77b 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -554,6 +554,7 @@
static void dump_select_request( const struct select_request *req )
{
fprintf( stderr, " flags=%d,", req->flags );
+ fprintf( stderr, " cookie=%p,", req->cookie );
fprintf( stderr, " sec=%d,", req->sec );
fprintf( stderr, " usec=%d,", req->usec );
fprintf( stderr, " handles=" );