Commit e5dedb19 authored by Alexandre Julliard's avatar Alexandre Julliard

Added support for nested server waits (to allow waiting in signal

handlers).
parent 5142150f
......@@ -125,6 +125,13 @@ struct send_fd
int fd; /* file descriptor on client-side */
};
/* structure sent by the server on the wait fifo */
struct wake_up_reply
{
void *cookie; /* magic cookie that was passed in select_request */
int signaled; /* wait result */
};
/* Create a new process from the context of the parent */
struct new_process_request
{
......@@ -425,6 +432,7 @@ struct select_request
{
REQUEST_HEADER; /* request header */
IN int flags; /* wait flags (see below) */
IN void* cookie; /* magic cookie to return to client */
IN int sec; /* absolute timeout */
IN int usec; /* absolute timeout */
IN VARARG(handles,handles); /* handles to select on */
......@@ -1603,7 +1611,7 @@ union generic_request
struct async_result_request async_result;
};
#define SERVER_PROTOCOL_VERSION 41
#define SERVER_PROTOCOL_VERSION 42
/* ### make_requests end ### */
/* Everything above this line is generated automatically by tools/make_requests */
......
......@@ -97,13 +97,13 @@ typedef struct _TEB
unsigned int buffer_size; /* --3 208 Buffer size */
int request_fd; /* --3 20c fd for sending server requests */
int reply_fd; /* --3 210 fd for receiving server replies */
int wait_fd; /* --3 214 fd for sleeping server requests */
void *debug_info; /* --3 218 Info for debugstr functions */
void *pthread_data; /* --3 21c Data for pthread emulation */
int wait_fd[2]; /* --3 214 fd for sleeping server requests */
void *debug_info; /* --3 21c Info for debugstr functions */
void *pthread_data; /* --3 220 Data for pthread emulation */
/* here is plenty space for wine specific fields (don't forget to change pad6!!) */
/* the following are nt specific fields */
DWORD pad6[630]; /* --n 220 */
DWORD pad6[629]; /* --n 224 */
UNICODE_STRING StaticUnicodeString; /* -2- bf8 used by advapi32 */
USHORT StaticUnicodeBuffer[261]; /* -2- c00 used by advapi32 */
DWORD pad7; /* --n e0c */
......
......@@ -638,22 +638,22 @@ void CLIENT_InitThread(void)
{
TEB *teb = NtCurrentTeb();
int version, ret;
int reply_pipe[2], wait_pipe[2];
int reply_pipe[2];
/* ignore SIGPIPE so that we get a EPIPE error instead */
signal( SIGPIPE, SIG_IGN );
/* create the server->client communication pipes */
if (pipe( reply_pipe ) == -1) server_protocol_perror( "pipe" );
if (pipe( wait_pipe ) == -1) server_protocol_perror( "pipe" );
if (pipe( teb->wait_fd ) == -1) server_protocol_perror( "pipe" );
wine_server_send_fd( reply_pipe[1] );
wine_server_send_fd( wait_pipe[1] );
wine_server_send_fd( teb->wait_fd[1] );
teb->reply_fd = reply_pipe[0];
teb->wait_fd = wait_pipe[0];
/* set close on exec flag */
fcntl( teb->reply_fd, F_SETFD, 1 );
fcntl( teb->wait_fd, F_SETFD, 1 );
fcntl( teb->wait_fd[0], F_SETFD, 1 );
fcntl( teb->wait_fd[1], F_SETFD, 1 );
SERVER_START_REQ( init_thread )
{
......@@ -661,7 +661,7 @@ void CLIENT_InitThread(void)
req->teb = teb;
req->entry = teb->entry_point;
req->reply_fd = reply_pipe[1];
req->wait_fd = wait_pipe[1];
req->wait_fd = teb->wait_fd[1];
ret = SERVER_CALL();
teb->pid = req->pid;
teb->tid = req->tid;
......@@ -669,7 +669,6 @@ void CLIENT_InitThread(void)
if (req->boot) boot_thread_id = teb->tid;
else if (boot_thread_id == teb->tid) boot_thread_id = 0;
close( reply_pipe[1] );
close( wait_pipe[1] );
}
SERVER_END_REQ;
......
......@@ -40,18 +40,33 @@ inline static void get_timeout( struct timeval *when, int timeout )
*
* Wait for a reply on the waiting pipe of the current thread.
*/
static int wait_reply(void)
static int wait_reply( void *cookie )
{
int signaled;
struct wake_up_reply reply;
for (;;)
{
int ret = read( NtCurrentTeb()->wait_fd, &signaled, sizeof(signaled) );
if (ret == sizeof(signaled)) return signaled;
if (!ret) break;
if (ret > 0) server_protocol_error( "partial wakeup read %d\n", ret );
int ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) );
if (ret == sizeof(reply))
{
if (!reply.cookie) break; /* thread got killed */
if (reply.cookie == cookie) return reply.signaled;
/* we stole another reply, wait for the real one */
signaled = wait_reply( cookie );
/* and now put the wrong one back in the pipe */
for (;;)
{
ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) );
if (ret == sizeof(reply)) break;
if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
if (errno == EINTR) continue;
server_protocol_perror("wakeup write");
}
return signaled;
}
if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
if (errno == EINTR) continue;
if (errno == EPIPE) break;
server_protocol_perror("read");
server_protocol_perror("wakeup read");
}
/* the server closed the connection; time to die... */
SYSDEPS_ExitThread(0);
......@@ -161,7 +176,7 @@ DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
BOOL wait_all, DWORD timeout,
BOOL alertable )
{
int i, ret;
int i, ret, cookie;
struct timeval tv;
if (count > MAXIMUM_WAIT_OBJECTS)
......@@ -180,6 +195,7 @@ DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
int *data = server_data_ptr( req );
req->flags = SELECT_INTERRUPTIBLE;
req->cookie = &cookie;
req->sec = tv.tv_sec;
req->usec = tv.tv_usec;
for (i = 0; i < count; i++) data[i] = handles[i];
......@@ -191,7 +207,7 @@ DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
ret = SERVER_CALL();
}
SERVER_END_VAR_REQ;
if (ret == STATUS_PENDING) ret = wait_reply();
if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
if (ret != STATUS_USER_APC) break;
call_apcs( alertable );
if (alertable) break;
......
......@@ -91,7 +91,8 @@ static BOOL THREAD_InitTEB( TEB *teb )
teb->exit_code = STILL_ACTIVE;
teb->request_fd = -1;
teb->reply_fd = -1;
teb->wait_fd = -1;
teb->wait_fd[0] = -1;
teb->wait_fd[1] = -1;
teb->stack_top = (void *)~0UL;
teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer);
teb->StaticUnicodeString.Buffer = (PWSTR)teb->StaticUnicodeBuffer;
......@@ -115,7 +116,8 @@ static void CALLBACK THREAD_FreeTEB( TEB *teb )
close( teb->request_fd );
close( teb->reply_fd );
close( teb->wait_fd );
close( teb->wait_fd[0] );
close( teb->wait_fd[1] );
if (teb->stack_sel) FreeSelector16( teb->stack_sel );
FreeSelector16( teb->teb_sel );
if (teb->buffer) munmap( (void *)teb->buffer, teb->buffer_size );
......
......@@ -277,20 +277,6 @@ int receive_fd( struct process *process )
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 )
{
......
......@@ -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 );
......
......@@ -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 @@ static void end_wait( struct thread *thread )
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 @@ static int wait_on( int count, struct object *objects[], int flags, int sec, int
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 @@ static int check_wait( struct thread *thread )
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 @@ static void select_on( int count, handle_t *handles, int flags, int sec, int use
if (flags & SELECT_TIMEOUT)
{
if (!(current->wait->user = add_timeout_user( &current->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 @@ int thread_queue_apc( struct thread *thread, struct object *owner, void *func,
if (!apc->prev) /* first one */
{
queue->head = apc;
if (thread->wait) wake_thread( thread );
wake_thread( thread );
}
return 1;
}
......@@ -661,7 +695,8 @@ void kill_thread( struct thread *thread, int violent_death )
(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(resume_thread)
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 */
......
......@@ -554,6 +554,7 @@ 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, " cookie=%p,", req->cookie );
fprintf( stderr, " sec=%d,", req->sec );
fprintf( stderr, " usec=%d,", req->usec );
fprintf( stderr, " handles=" );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment