Added support for nested server waits (to allow waiting in signal
handlers).
This commit is contained in:
parent
5142150f9c
commit
e5dedb1906
|
@ -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( ¤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 @@ 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=" );
|
||||
|
|
Loading…
Reference in New Issue