Use poll() instead of select() for the server main loop.
Fixed races with SIGCHLD handling and ptrace. Minor fixes to timeout handling.
This commit is contained in:
parent
9e7b45fbc3
commit
247b8aeeb9
|
@ -38,7 +38,8 @@ struct screen_buffer;
|
|||
struct console_input
|
||||
{
|
||||
struct object obj; /* object header */
|
||||
struct select_user select; /* select user */
|
||||
int fd; /* file descriptor */
|
||||
int select; /* select user id */
|
||||
int mode; /* input mode */
|
||||
struct screen_buffer *output; /* associated screen buffer */
|
||||
int recnum; /* number of input records */
|
||||
|
@ -48,7 +49,8 @@ struct console_input
|
|||
struct screen_buffer
|
||||
{
|
||||
struct object obj; /* object header */
|
||||
struct select_user select; /* select user */
|
||||
int fd; /* file descriptor */
|
||||
int select; /* select user id */
|
||||
int mode; /* output mode */
|
||||
struct console_input *input; /* associated console input */
|
||||
int cursor_size; /* size of cursor (percentage filled) */
|
||||
|
@ -117,16 +119,16 @@ static struct object *create_console_input( int fd )
|
|||
}
|
||||
if ((console_input = alloc_object( &console_input_ops )))
|
||||
{
|
||||
console_input->select.fd = fd;
|
||||
console_input->select.func = default_select_event;
|
||||
console_input->select.private = console_input;
|
||||
console_input->mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
|
||||
ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
|
||||
console_input->output = NULL;
|
||||
console_input->recnum = 0;
|
||||
console_input->records = NULL;
|
||||
register_select_user( &console_input->select );
|
||||
return &console_input->obj;
|
||||
console_input->fd = fd;
|
||||
console_input->mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
|
||||
ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
|
||||
console_input->output = NULL;
|
||||
console_input->recnum = 0;
|
||||
console_input->records = NULL;
|
||||
console_input->select = add_select_user( fd, default_select_event, console_input );
|
||||
if (console_input->select != -1) return &console_input->obj;
|
||||
release_object( console_input );
|
||||
return NULL;
|
||||
}
|
||||
close( fd );
|
||||
return NULL;
|
||||
|
@ -144,16 +146,19 @@ static struct object *create_console_output( int fd, struct object *input )
|
|||
}
|
||||
if ((screen_buffer = alloc_object( &screen_buffer_ops )))
|
||||
{
|
||||
screen_buffer->select.fd = fd;
|
||||
screen_buffer->select.func = default_select_event;
|
||||
screen_buffer->select.private = screen_buffer;
|
||||
screen_buffer->fd = fd;
|
||||
screen_buffer->mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
|
||||
screen_buffer->input = console_input;
|
||||
screen_buffer->cursor_size = 100;
|
||||
screen_buffer->cursor_visible = 1;
|
||||
screen_buffer->pid = 0;
|
||||
screen_buffer->title = strdup( "Wine console" );
|
||||
register_select_user( &screen_buffer->select );
|
||||
screen_buffer->select = add_select_user( fd, default_select_event, screen_buffer );
|
||||
if (screen_buffer->select == -1)
|
||||
{
|
||||
release_object( screen_buffer );
|
||||
return NULL;
|
||||
}
|
||||
console_input->output = screen_buffer;
|
||||
return &screen_buffer->obj;
|
||||
}
|
||||
|
@ -218,15 +223,11 @@ static int set_console_fd( int handle, int fd_in, int fd_out, int pid )
|
|||
assert( !input->obj.head );
|
||||
assert( !output->obj.head );
|
||||
|
||||
unregister_select_user( &input->select );
|
||||
unregister_select_user( &output->select );
|
||||
close( input->select.fd );
|
||||
close( output->select.fd );
|
||||
input->select.fd = fd_in;
|
||||
output->select.fd = fd_out;
|
||||
output->pid = pid;
|
||||
register_select_user( &input->select );
|
||||
register_select_user( &output->select );
|
||||
change_select_fd( input->select, fd_in );
|
||||
change_select_fd( output->select, fd_out );
|
||||
input->fd = fd_in;
|
||||
output->fd = fd_out;
|
||||
output->pid = pid;
|
||||
release_object( input );
|
||||
release_object( output );
|
||||
return 1;
|
||||
|
@ -359,7 +360,7 @@ static void console_input_dump( struct object *obj, int verbose )
|
|||
{
|
||||
struct console_input *console = (struct console_input *)obj;
|
||||
assert( obj->ops == &console_input_ops );
|
||||
fprintf( stderr, "Console input fd=%d\n", console->select.fd );
|
||||
fprintf( stderr, "Console input fd=%d\n", console->fd );
|
||||
}
|
||||
|
||||
static int console_input_add_queue( struct object *obj, struct wait_queue_entry *entry )
|
||||
|
@ -367,7 +368,7 @@ static int console_input_add_queue( struct object *obj, struct wait_queue_entry
|
|||
struct console_input *console = (struct console_input *)obj;
|
||||
assert( obj->ops == &console_input_ops );
|
||||
if (!obj->head) /* first on the queue */
|
||||
set_select_events( &console->select, READ_EVENT );
|
||||
set_select_events( console->select, POLLIN );
|
||||
add_queue( obj, entry );
|
||||
return 1;
|
||||
}
|
||||
|
@ -379,7 +380,7 @@ static void console_input_remove_queue( struct object *obj, struct wait_queue_en
|
|||
|
||||
remove_queue( obj, entry );
|
||||
if (!obj->head) /* last on the queue is gone */
|
||||
set_select_events( &console->select, 0 );
|
||||
set_select_events( console->select, 0 );
|
||||
release_object( obj );
|
||||
}
|
||||
|
||||
|
@ -388,16 +389,16 @@ static int console_input_signaled( struct object *obj, struct thread *thread )
|
|||
struct console_input *console = (struct console_input *)obj;
|
||||
assert( obj->ops == &console_input_ops );
|
||||
|
||||
if (check_select_events( &console->select, READ_EVENT ))
|
||||
if (check_select_events( console->fd, POLLIN ))
|
||||
{
|
||||
/* stop waiting on select() if we are signaled */
|
||||
set_select_events( &console->select, 0 );
|
||||
set_select_events( console->select, 0 );
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* restart waiting on select() if we are no longer signaled */
|
||||
if (obj->head) set_select_events( &console->select, READ_EVENT );
|
||||
if (obj->head) set_select_events( console->select, POLLIN );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +407,7 @@ static int console_input_get_read_fd( struct object *obj )
|
|||
{
|
||||
struct console_input *console = (struct console_input *)obj;
|
||||
assert( obj->ops == &console_input_ops );
|
||||
return dup( console->select.fd );
|
||||
return dup( console->fd );
|
||||
}
|
||||
|
||||
static int console_get_info( struct object *obj, struct get_file_info_request *req )
|
||||
|
@ -428,8 +429,7 @@ static void console_input_destroy( struct object *obj )
|
|||
{
|
||||
struct console_input *console = (struct console_input *)obj;
|
||||
assert( obj->ops == &console_input_ops );
|
||||
unregister_select_user( &console->select );
|
||||
close( console->select.fd );
|
||||
remove_select_user( console->select );
|
||||
if (console->output) console->output->input = NULL;
|
||||
}
|
||||
|
||||
|
@ -437,7 +437,7 @@ static void screen_buffer_dump( struct object *obj, int verbose )
|
|||
{
|
||||
struct screen_buffer *console = (struct screen_buffer *)obj;
|
||||
assert( obj->ops == &screen_buffer_ops );
|
||||
fprintf( stderr, "Console screen buffer fd=%d\n", console->select.fd );
|
||||
fprintf( stderr, "Console screen buffer fd=%d\n", console->fd );
|
||||
}
|
||||
|
||||
static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry *entry )
|
||||
|
@ -445,7 +445,7 @@ static int screen_buffer_add_queue( struct object *obj, struct wait_queue_entry
|
|||
struct screen_buffer *console = (struct screen_buffer *)obj;
|
||||
assert( obj->ops == &screen_buffer_ops );
|
||||
if (!obj->head) /* first on the queue */
|
||||
set_select_events( &console->select, WRITE_EVENT );
|
||||
set_select_events( console->select, POLLOUT );
|
||||
add_queue( obj, entry );
|
||||
return 1;
|
||||
}
|
||||
|
@ -457,7 +457,7 @@ static void screen_buffer_remove_queue( struct object *obj, struct wait_queue_en
|
|||
|
||||
remove_queue( obj, entry );
|
||||
if (!obj->head) /* last on the queue is gone */
|
||||
set_select_events( &console->select, 0 );
|
||||
set_select_events( console->select, 0 );
|
||||
release_object( obj );
|
||||
}
|
||||
|
||||
|
@ -466,16 +466,16 @@ static int screen_buffer_signaled( struct object *obj, struct thread *thread )
|
|||
struct screen_buffer *console = (struct screen_buffer *)obj;
|
||||
assert( obj->ops == &screen_buffer_ops );
|
||||
|
||||
if (check_select_events( &console->select, WRITE_EVENT ))
|
||||
if (check_select_events( console->fd, POLLOUT ))
|
||||
{
|
||||
/* stop waiting on select() if we are signaled */
|
||||
set_select_events( &console->select, 0 );
|
||||
set_select_events( console->select, 0 );
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* restart waiting on select() if we are no longer signaled */
|
||||
if (obj->head) set_select_events( &console->select, WRITE_EVENT );
|
||||
if (obj->head) set_select_events( console->select, POLLOUT );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -484,15 +484,14 @@ static int screen_buffer_get_write_fd( struct object *obj )
|
|||
{
|
||||
struct screen_buffer *console = (struct screen_buffer *)obj;
|
||||
assert( obj->ops == &screen_buffer_ops );
|
||||
return dup( console->select.fd );
|
||||
return dup( console->fd );
|
||||
}
|
||||
|
||||
static void screen_buffer_destroy( struct object *obj )
|
||||
{
|
||||
struct screen_buffer *console = (struct screen_buffer *)obj;
|
||||
assert( obj->ops == &screen_buffer_ops );
|
||||
unregister_select_user( &console->select );
|
||||
close( console->select.fd );
|
||||
remove_select_user( console->select );
|
||||
if (console->input) console->input->output = NULL;
|
||||
if (console->title) free( console->title );
|
||||
}
|
||||
|
|
|
@ -218,7 +218,8 @@ static int wait_for_debug_event( int timeout )
|
|||
}
|
||||
if (timeout != -1) /* start the timeout */
|
||||
{
|
||||
make_timeout( &when, timeout );
|
||||
gettimeofday( &when, 0 );
|
||||
add_timeout( &when, timeout );
|
||||
if (!(debug_ctx->timeout = add_timeout_user( &when, wait_event_timeout, debug_ctx )))
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
struct file
|
||||
{
|
||||
struct object obj; /* object header */
|
||||
struct select_user select; /* select user */
|
||||
int fd; /* file descriptor */
|
||||
int select; /* select user id */
|
||||
struct file *next; /* next file in hashing list */
|
||||
char *name; /* file name */
|
||||
unsigned int access; /* file access (GENERIC_READ/WRITE) */
|
||||
|
@ -108,15 +109,17 @@ static struct file *create_file_for_fd( int fd, unsigned int access, unsigned in
|
|||
struct file *file;
|
||||
if ((file = alloc_object( &file_ops )))
|
||||
{
|
||||
file->name = NULL;
|
||||
file->next = NULL;
|
||||
file->select.fd = fd;
|
||||
file->select.func = default_select_event;
|
||||
file->select.private = file;
|
||||
file->access = access;
|
||||
file->flags = attrs;
|
||||
file->sharing = sharing;
|
||||
register_select_user( &file->select );
|
||||
file->name = NULL;
|
||||
file->next = NULL;
|
||||
file->fd = fd;
|
||||
file->access = access;
|
||||
file->flags = attrs;
|
||||
file->sharing = sharing;
|
||||
if ((file->select = add_select_user( fd, default_select_event, file )) == -1)
|
||||
{
|
||||
release_object( file );
|
||||
file = NULL;
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
@ -221,8 +224,7 @@ static void file_dump( struct object *obj, int verbose )
|
|||
{
|
||||
struct file *file = (struct file *)obj;
|
||||
assert( obj->ops == &file_ops );
|
||||
fprintf( stderr, "File fd=%d flags=%08x name='%s'\n",
|
||||
file->select.fd, file->flags, file->name );
|
||||
fprintf( stderr, "File fd=%d flags=%08x name='%s'\n", file->fd, file->flags, file->name );
|
||||
}
|
||||
|
||||
static int file_add_queue( struct object *obj, struct wait_queue_entry *entry )
|
||||
|
@ -232,9 +234,9 @@ static int file_add_queue( struct object *obj, struct wait_queue_entry *entry )
|
|||
if (!obj->head) /* first on the queue */
|
||||
{
|
||||
int events = 0;
|
||||
if (file->access & GENERIC_READ) events |= READ_EVENT;
|
||||
if (file->access & GENERIC_WRITE) events |= WRITE_EVENT;
|
||||
set_select_events( &file->select, events );
|
||||
if (file->access & GENERIC_READ) events |= POLLIN;
|
||||
if (file->access & GENERIC_WRITE) events |= POLLOUT;
|
||||
set_select_events( file->select, events );
|
||||
}
|
||||
add_queue( obj, entry );
|
||||
return 1;
|
||||
|
@ -247,7 +249,7 @@ static void file_remove_queue( struct object *obj, struct wait_queue_entry *entr
|
|||
|
||||
remove_queue( obj, entry );
|
||||
if (!obj->head) /* last on the queue is gone */
|
||||
set_select_events( &file->select, 0 );
|
||||
set_select_events( file->select, 0 );
|
||||
release_object( obj );
|
||||
}
|
||||
|
||||
|
@ -257,18 +259,18 @@ static int file_signaled( struct object *obj, struct thread *thread )
|
|||
struct file *file = (struct file *)obj;
|
||||
assert( obj->ops == &file_ops );
|
||||
|
||||
if (file->access & GENERIC_READ) events |= READ_EVENT;
|
||||
if (file->access & GENERIC_WRITE) events |= WRITE_EVENT;
|
||||
if (check_select_events( &file->select, events ))
|
||||
if (file->access & GENERIC_READ) events |= POLLIN;
|
||||
if (file->access & GENERIC_WRITE) events |= POLLOUT;
|
||||
if (check_select_events( file->fd, events ))
|
||||
{
|
||||
/* stop waiting on select() if we are signaled */
|
||||
set_select_events( &file->select, 0 );
|
||||
set_select_events( file->select, 0 );
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* restart waiting on select() if we are no longer signaled */
|
||||
if (obj->head) set_select_events( &file->select, events );
|
||||
if (obj->head) set_select_events( file->select, events );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -277,14 +279,14 @@ static int file_get_read_fd( struct object *obj )
|
|||
{
|
||||
struct file *file = (struct file *)obj;
|
||||
assert( obj->ops == &file_ops );
|
||||
return dup( file->select.fd );
|
||||
return dup( file->fd );
|
||||
}
|
||||
|
||||
static int file_get_write_fd( struct object *obj )
|
||||
{
|
||||
struct file *file = (struct file *)obj;
|
||||
assert( obj->ops == &file_ops );
|
||||
return dup( file->select.fd );
|
||||
return dup( file->fd );
|
||||
}
|
||||
|
||||
static int file_flush( struct object *obj )
|
||||
|
@ -293,7 +295,7 @@ static int file_flush( struct object *obj )
|
|||
struct file *file = (struct file *)grab_object(obj);
|
||||
assert( obj->ops == &file_ops );
|
||||
|
||||
ret = (fsync( file->select.fd ) != -1);
|
||||
ret = (fsync( file->fd ) != -1);
|
||||
if (!ret) file_set_error();
|
||||
release_object( file );
|
||||
return ret;
|
||||
|
@ -305,13 +307,13 @@ static int file_get_info( struct object *obj, struct get_file_info_request *req
|
|||
struct file *file = (struct file *)obj;
|
||||
assert( obj->ops == &file_ops );
|
||||
|
||||
if (fstat( file->select.fd, &st ) == -1)
|
||||
if (fstat( file->fd, &st ) == -1)
|
||||
{
|
||||
file_set_error();
|
||||
return 0;
|
||||
}
|
||||
if (S_ISCHR(st.st_mode) || S_ISFIFO(st.st_mode) ||
|
||||
S_ISSOCK(st.st_mode) || isatty(file->select.fd)) req->type = FILE_TYPE_CHAR;
|
||||
S_ISSOCK(st.st_mode) || isatty(file->fd)) req->type = FILE_TYPE_CHAR;
|
||||
else req->type = FILE_TYPE_DISK;
|
||||
if (S_ISDIR(st.st_mode)) req->attr = FILE_ATTRIBUTE_DIRECTORY;
|
||||
else req->attr = FILE_ATTRIBUTE_ARCHIVE;
|
||||
|
@ -342,8 +344,7 @@ static void file_destroy( struct object *obj )
|
|||
if (file->flags & FILE_FLAG_DELETE_ON_CLOSE) unlink( file->name );
|
||||
free( file->name );
|
||||
}
|
||||
unregister_select_user( &file->select );
|
||||
close( file->select.fd );
|
||||
remove_select_user( file->select );
|
||||
}
|
||||
|
||||
/* set the last error depending on errno */
|
||||
|
@ -378,7 +379,7 @@ struct file *get_file_obj( struct process *process, int handle, unsigned int acc
|
|||
|
||||
int file_get_mmap_fd( struct file *file )
|
||||
{
|
||||
return dup( file->select.fd );
|
||||
return dup( file->fd );
|
||||
}
|
||||
|
||||
static int set_file_pointer( int handle, int *low, int *high, int whence )
|
||||
|
@ -395,7 +396,7 @@ static int set_file_pointer( int handle, int *low, int *high, int whence )
|
|||
|
||||
if (!(file = get_file_obj( current->process, handle, 0 )))
|
||||
return 0;
|
||||
if ((result = lseek( file->select.fd, *low, whence )) == -1)
|
||||
if ((result = lseek( file->fd, *low, whence )) == -1)
|
||||
{
|
||||
/* Check for seek before start of file */
|
||||
if ((errno == EINVAL) && (whence != SEEK_SET) && (*low < 0))
|
||||
|
@ -417,8 +418,8 @@ static int truncate_file( int handle )
|
|||
|
||||
if (!(file = get_file_obj( current->process, handle, GENERIC_WRITE )))
|
||||
return 0;
|
||||
if (((result = lseek( file->select.fd, 0, SEEK_CUR )) == -1) ||
|
||||
(ftruncate( file->select.fd, result ) == -1))
|
||||
if (((result = lseek( file->fd, 0, SEEK_CUR )) == -1) ||
|
||||
(ftruncate( file->fd, result ) == -1))
|
||||
{
|
||||
file_set_error();
|
||||
release_object( file );
|
||||
|
@ -439,13 +440,13 @@ int grow_file( struct file *file, int size_high, int size_low )
|
|||
set_error( ERROR_INVALID_PARAMETER );
|
||||
return 0;
|
||||
}
|
||||
if (fstat( file->select.fd, &st ) == -1)
|
||||
if (fstat( file->fd, &st ) == -1)
|
||||
{
|
||||
file_set_error();
|
||||
return 0;
|
||||
}
|
||||
if (st.st_size >= size_low) return 1; /* already large enough */
|
||||
if (ftruncate( file->select.fd, size_low ) != -1) return 1;
|
||||
if (ftruncate( file->fd, size_low ) != -1) return 1;
|
||||
file_set_error();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#error This file can only be used in the Wine server
|
||||
#endif
|
||||
|
||||
#include <sys/poll.h>
|
||||
#include <sys/time.h>
|
||||
#include "server.h"
|
||||
|
||||
|
@ -89,21 +90,11 @@ extern void dump_objects(void);
|
|||
|
||||
/* select functions */
|
||||
|
||||
#define READ_EVENT 1
|
||||
#define WRITE_EVENT 2
|
||||
#define EXCEPT_EVENT 4
|
||||
|
||||
struct select_user
|
||||
{
|
||||
int fd; /* user fd */
|
||||
void (*func)(int event, void *private); /* callback function */
|
||||
void *private; /* callback private data */
|
||||
};
|
||||
|
||||
extern void register_select_user( struct select_user *user );
|
||||
extern void unregister_select_user( struct select_user *user );
|
||||
extern void set_select_events( struct select_user *user, int events );
|
||||
extern int check_select_events( struct select_user *user, int events );
|
||||
extern int add_select_user( int fd, void (*func)(int, void *), void *private );
|
||||
extern void remove_select_user( int user );
|
||||
extern void change_select_fd( int user, int fd );
|
||||
extern void set_select_events( int user, int events );
|
||||
extern int check_select_events( int fd, int events );
|
||||
extern void select_loop(void);
|
||||
|
||||
/* timeout functions */
|
||||
|
@ -115,7 +106,13 @@ typedef void (*timeout_callback)( void *private );
|
|||
extern struct timeout_user *add_timeout_user( struct timeval *when,
|
||||
timeout_callback func, void *private );
|
||||
extern void remove_timeout_user( struct timeout_user *user );
|
||||
extern void make_timeout( struct timeval *when, int timeout );
|
||||
extern void add_timeout( struct timeval *when, int timeout );
|
||||
/* return 1 if t1 is before t2 */
|
||||
static inline int time_before( struct timeval *t1, struct timeval *t2 )
|
||||
{
|
||||
return ((t1->tv_sec < t2->tv_sec) ||
|
||||
((t1->tv_sec == t2->tv_sec) && (t1->tv_usec < t2->tv_usec)));
|
||||
}
|
||||
|
||||
/* socket functions */
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ struct pipe
|
|||
{
|
||||
struct object obj; /* object header */
|
||||
struct pipe *other; /* the pipe other end */
|
||||
struct select_user select; /* select user */
|
||||
int fd; /* file descriptor */
|
||||
int select; /* select user id */
|
||||
enum side side; /* which side of the pipe is this */
|
||||
};
|
||||
|
||||
|
@ -68,12 +69,14 @@ static struct pipe *create_pipe_side( int fd, int side )
|
|||
|
||||
if ((pipe = alloc_object( &pipe_ops )))
|
||||
{
|
||||
pipe->select.fd = fd;
|
||||
pipe->select.func = default_select_event;
|
||||
pipe->select.private = pipe;
|
||||
pipe->other = NULL;
|
||||
pipe->side = side;
|
||||
register_select_user( &pipe->select );
|
||||
pipe->fd = fd;
|
||||
pipe->other = NULL;
|
||||
pipe->side = side;
|
||||
if ((pipe->select = add_select_user( fd, default_select_event, pipe )) == -1)
|
||||
{
|
||||
release_object( pipe );
|
||||
pipe = NULL;
|
||||
}
|
||||
}
|
||||
return pipe;
|
||||
}
|
||||
|
@ -111,7 +114,7 @@ static void pipe_dump( struct object *obj, int verbose )
|
|||
struct pipe *pipe = (struct pipe *)obj;
|
||||
assert( obj->ops == &pipe_ops );
|
||||
fprintf( stderr, "Pipe %s-side fd=%d\n",
|
||||
(pipe->side == READ_SIDE) ? "read" : "write", pipe->select.fd );
|
||||
(pipe->side == READ_SIDE) ? "read" : "write", pipe->fd );
|
||||
}
|
||||
|
||||
static int pipe_add_queue( struct object *obj, struct wait_queue_entry *entry )
|
||||
|
@ -119,8 +122,7 @@ static int pipe_add_queue( struct object *obj, struct wait_queue_entry *entry )
|
|||
struct pipe *pipe = (struct pipe *)obj;
|
||||
assert( obj->ops == &pipe_ops );
|
||||
if (!obj->head) /* first on the queue */
|
||||
set_select_events( &pipe->select,
|
||||
(pipe->side == READ_SIDE) ? READ_EVENT : WRITE_EVENT );
|
||||
set_select_events( pipe->select, (pipe->side == READ_SIDE) ? POLLIN : POLLOUT );
|
||||
add_queue( obj, entry );
|
||||
return 1;
|
||||
}
|
||||
|
@ -132,7 +134,7 @@ static void pipe_remove_queue( struct object *obj, struct wait_queue_entry *entr
|
|||
|
||||
remove_queue( obj, entry );
|
||||
if (!obj->head) /* last on the queue is gone */
|
||||
set_select_events( &pipe->select, 0 );
|
||||
set_select_events( pipe->select, 0 );
|
||||
release_object( obj );
|
||||
}
|
||||
|
||||
|
@ -142,17 +144,17 @@ static int pipe_signaled( struct object *obj, struct thread *thread )
|
|||
struct pipe *pipe = (struct pipe *)obj;
|
||||
assert( obj->ops == &pipe_ops );
|
||||
|
||||
event = (pipe->side == READ_SIDE) ? READ_EVENT : WRITE_EVENT;
|
||||
if (check_select_events( &pipe->select, event ))
|
||||
event = (pipe->side == READ_SIDE) ? POLLIN : POLLOUT;
|
||||
if (check_select_events( pipe->fd, event ))
|
||||
{
|
||||
/* stop waiting on select() if we are signaled */
|
||||
set_select_events( &pipe->select, 0 );
|
||||
set_select_events( pipe->select, 0 );
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* restart waiting on select() if we are no longer signaled */
|
||||
if (obj->head) set_select_events( &pipe->select, event );
|
||||
if (obj->head) set_select_events( pipe->select, event );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +174,7 @@ static int pipe_get_read_fd( struct object *obj )
|
|||
set_error( ERROR_ACCESS_DENIED );
|
||||
return -1;
|
||||
}
|
||||
return dup( pipe->select.fd );
|
||||
return dup( pipe->fd );
|
||||
}
|
||||
|
||||
static int pipe_get_write_fd( struct object *obj )
|
||||
|
@ -190,7 +192,7 @@ static int pipe_get_write_fd( struct object *obj )
|
|||
set_error( ERROR_ACCESS_DENIED );
|
||||
return -1;
|
||||
}
|
||||
return dup( pipe->select.fd );
|
||||
return dup( pipe->fd );
|
||||
}
|
||||
|
||||
static int pipe_get_info( struct object *obj, struct get_file_info_request *req )
|
||||
|
@ -214,8 +216,7 @@ static void pipe_destroy( struct object *obj )
|
|||
assert( obj->ops == &pipe_ops );
|
||||
|
||||
if (pipe->other) pipe->other->other = NULL;
|
||||
unregister_select_user( &pipe->select );
|
||||
close( pipe->select.fd );
|
||||
remove_select_user( pipe->select );
|
||||
}
|
||||
|
||||
/* create an anonymous pipe */
|
||||
|
|
|
@ -39,58 +39,70 @@
|
|||
|
||||
static const int use_ptrace = 1; /* set to 0 to disable ptrace */
|
||||
|
||||
|
||||
/* wait for a ptraced child to get a certain signal */
|
||||
/* if the signal is 0, we simply check if anything is pending and return at once */
|
||||
void wait4_thread( struct thread *thread, int signal )
|
||||
/* handle a status returned by wait4 */
|
||||
static int handle_child_status( struct thread *thread, int pid, int status )
|
||||
{
|
||||
int status;
|
||||
int pid;
|
||||
|
||||
restart:
|
||||
pid = thread ? thread->unix_pid : -1;
|
||||
if ((pid = wait4( pid, &status, WUNTRACED | (signal ? 0 : WNOHANG), NULL )) == -1)
|
||||
{
|
||||
perror( "wait4" );
|
||||
return;
|
||||
}
|
||||
if (WIFSTOPPED(status))
|
||||
{
|
||||
int sig = WSTOPSIG(status);
|
||||
if (debug_level) fprintf( stderr, "ptrace: pid %d got sig %d\n", pid, sig );
|
||||
if (debug_level && thread)
|
||||
fprintf( stderr, "%08x: *signal* signal=%d\n", (unsigned int)thread, sig );
|
||||
switch(sig)
|
||||
{
|
||||
case SIGSTOP: /* continue at once if not suspended */
|
||||
if (!thread)
|
||||
if (!(thread = get_thread_from_pid( pid ))) break;
|
||||
if (!(thread->process->suspend + thread->suspend))
|
||||
if (!thread || !(thread->process->suspend + thread->suspend))
|
||||
ptrace( PTRACE_CONT, pid, 1, sig );
|
||||
break;
|
||||
default: /* ignore other signals for now */
|
||||
ptrace( PTRACE_CONT, pid, 1, sig );
|
||||
break;
|
||||
}
|
||||
if (signal && sig != signal) goto restart;
|
||||
return sig;
|
||||
}
|
||||
else if (WIFSIGNALED(status))
|
||||
if (thread && (WIFSIGNALED(status) || WIFEXITED(status)))
|
||||
{
|
||||
int exit_code = WTERMSIG(status);
|
||||
thread->attached = 0;
|
||||
thread->unix_pid = 0;
|
||||
if (debug_level)
|
||||
fprintf( stderr, "ptrace: pid %d killed by sig %d\n", pid, exit_code );
|
||||
if (!thread)
|
||||
if (!(thread = get_thread_from_pid( pid ))) return;
|
||||
if (thread->client) remove_client( thread->client, exit_code );
|
||||
{
|
||||
if (WIFSIGNALED(status))
|
||||
fprintf( stderr, "%08x: *exited* signal=%d\n",
|
||||
(unsigned int)thread, WTERMSIG(status) );
|
||||
else
|
||||
fprintf( stderr, "%08x: *exited* status=%d\n",
|
||||
(unsigned int)thread, WEXITSTATUS(status) );
|
||||
}
|
||||
}
|
||||
else if (WIFEXITED(status))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle a SIGCHLD signal */
|
||||
void sigchld_handler()
|
||||
{
|
||||
int pid, status;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int exit_code = WEXITSTATUS(status);
|
||||
if (debug_level)
|
||||
fprintf( stderr, "ptrace: pid %d exited with status %d\n", pid, exit_code );
|
||||
if (!thread)
|
||||
if (!(thread = get_thread_from_pid( pid ))) return;
|
||||
if (thread->client) remove_client( thread->client, exit_code );
|
||||
if (!(pid = wait4( -1, &status, WUNTRACED | WNOHANG, NULL ))) break;
|
||||
if (pid != -1) handle_child_status( get_thread_from_pid(pid), pid, status );
|
||||
else break;
|
||||
}
|
||||
else fprintf( stderr, "wait4: pid %d unknown status %x\n", pid, status );
|
||||
}
|
||||
|
||||
/* wait for a ptraced child to get a certain signal */
|
||||
void wait4_thread( struct thread *thread, int signal )
|
||||
{
|
||||
int res, status;
|
||||
|
||||
do
|
||||
{
|
||||
if ((res = wait4( thread->unix_pid, &status, WUNTRACED, NULL )) == -1)
|
||||
{
|
||||
perror( "wait4" );
|
||||
return;
|
||||
}
|
||||
res = handle_child_status( thread, res, status );
|
||||
} while (res && res != signal);
|
||||
}
|
||||
|
||||
/* attach to a Unix thread */
|
||||
|
@ -98,7 +110,7 @@ static int attach_thread( struct thread *thread )
|
|||
{
|
||||
/* this may fail if the client is already being debugged */
|
||||
if (!use_ptrace || (ptrace( PTRACE_ATTACH, thread->unix_pid, 0, 0 ) == -1)) return 0;
|
||||
if (debug_level) fprintf( stderr, "ptrace: attached to pid %d\n", thread->unix_pid );
|
||||
if (debug_level) fprintf( stderr, "%08x: *attached*\n", (unsigned int)thread );
|
||||
thread->attached = 1;
|
||||
wait4_thread( thread, SIGSTOP );
|
||||
return 1;
|
||||
|
@ -113,7 +125,7 @@ void detach_thread( struct thread *thread )
|
|||
if (thread->attached)
|
||||
{
|
||||
wait4_thread( thread, SIGTERM );
|
||||
if (debug_level) fprintf( stderr, "ptrace: detaching from %d\n", thread->unix_pid );
|
||||
if (debug_level) fprintf( stderr, "%08x: *detached*\n", (unsigned int)thread );
|
||||
ptrace( PTRACE_DETACH, thread->unix_pid, 1, SIGTERM );
|
||||
thread->attached = 0;
|
||||
}
|
||||
|
|
268
server/select.c
268
server/select.c
|
@ -10,6 +10,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
@ -17,6 +18,13 @@
|
|||
#include "object.h"
|
||||
#include "thread.h"
|
||||
|
||||
|
||||
struct poll_user
|
||||
{
|
||||
void (*func)(int event, void *private); /* callback function */
|
||||
void *private; /* callback private data */
|
||||
};
|
||||
|
||||
struct timeout_user
|
||||
{
|
||||
struct timeout_user *next; /* next in sorted timeout list */
|
||||
|
@ -26,62 +34,92 @@ struct timeout_user
|
|||
void *private; /* callback private data */
|
||||
};
|
||||
|
||||
static struct select_user *users[FD_SETSIZE]; /* users array */
|
||||
static fd_set read_set, write_set, except_set; /* current select sets */
|
||||
static int nb_users; /* current number of users */
|
||||
static int max_fd; /* max fd in use */
|
||||
static struct poll_user *poll_users; /* users array */
|
||||
static struct pollfd *pollfd; /* poll fd array */
|
||||
static int nb_users; /* count of array entries actually in use */
|
||||
static int active_users; /* current number of active users */
|
||||
static int allocated_users; /* count of allocated entries in the array */
|
||||
static struct poll_user *freelist; /* list of free entries in the array */
|
||||
|
||||
static struct timeout_user *timeout_head; /* sorted timeouts list head */
|
||||
static struct timeout_user *timeout_tail; /* sorted timeouts list tail */
|
||||
|
||||
|
||||
/* register a user */
|
||||
void register_select_user( struct select_user *user )
|
||||
/* add a user and return an opaque handle to it, or -1 on failure */
|
||||
int add_select_user( int fd, void (*func)(int, void *), void *private )
|
||||
{
|
||||
assert( !users[user->fd] );
|
||||
|
||||
users[user->fd] = user;
|
||||
if (user->fd > max_fd) max_fd = user->fd;
|
||||
nb_users++;
|
||||
int ret;
|
||||
if (freelist)
|
||||
{
|
||||
ret = freelist - poll_users;
|
||||
freelist = poll_users[ret].private;
|
||||
assert( !poll_users[ret].func );
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nb_users == allocated_users)
|
||||
{
|
||||
struct poll_user *newusers;
|
||||
struct pollfd *newpoll;
|
||||
int new_count = allocated_users ? (allocated_users + allocated_users / 2) : 16;
|
||||
if (!(newusers = realloc( poll_users, new_count * sizeof(*poll_users) ))) return -1;
|
||||
if (!(newpoll = realloc( pollfd, new_count * sizeof(*pollfd) )))
|
||||
{
|
||||
free( newusers );
|
||||
return -1;
|
||||
}
|
||||
poll_users = newusers;
|
||||
pollfd = newpoll;
|
||||
allocated_users = new_count;
|
||||
}
|
||||
ret = nb_users++;
|
||||
}
|
||||
pollfd[ret].fd = fd;
|
||||
pollfd[ret].events = 0;
|
||||
pollfd[ret].revents = 0;
|
||||
poll_users[ret].func = func;
|
||||
poll_users[ret].private = private;
|
||||
active_users++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* remove a user */
|
||||
void unregister_select_user( struct select_user *user )
|
||||
/* remove a user and close its fd */
|
||||
void remove_select_user( int user )
|
||||
{
|
||||
assert( users[user->fd] == user );
|
||||
if (user == -1) return; /* avoids checking in all callers */
|
||||
assert( poll_users[user].func );
|
||||
close( pollfd[user].fd );
|
||||
pollfd[user].fd = -1;
|
||||
pollfd[user].events = 0;
|
||||
pollfd[user].revents = 0;
|
||||
poll_users[user].func = NULL;
|
||||
poll_users[user].private = freelist;
|
||||
freelist = &poll_users[user];
|
||||
active_users--;
|
||||
}
|
||||
|
||||
FD_CLR( user->fd, &read_set );
|
||||
FD_CLR( user->fd, &write_set );
|
||||
FD_CLR( user->fd, &except_set );
|
||||
users[user->fd] = NULL;
|
||||
if (max_fd == user->fd) while (max_fd && !users[max_fd]) max_fd--;
|
||||
nb_users--;
|
||||
/* change the fd of a select user (the old fd is closed) */
|
||||
void change_select_fd( int user, int fd )
|
||||
{
|
||||
assert( poll_users[user].func );
|
||||
close( pollfd[user].fd );
|
||||
pollfd[user].fd = fd;
|
||||
}
|
||||
|
||||
/* set the events that select waits for on this fd */
|
||||
void set_select_events( struct select_user *user, int events )
|
||||
void set_select_events( int user, int events )
|
||||
{
|
||||
assert( users[user->fd] == user );
|
||||
if (events & READ_EVENT) FD_SET( user->fd, &read_set );
|
||||
else FD_CLR( user->fd, &read_set );
|
||||
if (events & WRITE_EVENT) FD_SET( user->fd, &write_set );
|
||||
else FD_CLR( user->fd, &write_set );
|
||||
if (events & EXCEPT_EVENT) FD_SET( user->fd, &except_set );
|
||||
else FD_CLR( user->fd, &except_set );
|
||||
assert( poll_users[user].func );
|
||||
pollfd[user].events = events;
|
||||
}
|
||||
|
||||
/* check if events are pending */
|
||||
int check_select_events( struct select_user *user, int events )
|
||||
int check_select_events( int fd, int events )
|
||||
{
|
||||
fd_set read_fds, write_fds, except_fds;
|
||||
struct timeval tv = { 0, 0 };
|
||||
|
||||
FD_ZERO( &read_fds );
|
||||
FD_ZERO( &write_fds );
|
||||
FD_ZERO( &except_fds );
|
||||
if (events & READ_EVENT) FD_SET( user->fd, &read_fds );
|
||||
if (events & WRITE_EVENT) FD_SET( user->fd, &write_fds );
|
||||
if (events & EXCEPT_EVENT) FD_SET( user->fd, &except_fds );
|
||||
return select( user->fd + 1, &read_fds, &write_fds, &except_fds, &tv ) > 0;
|
||||
struct pollfd pfd;
|
||||
pfd.fd = fd;
|
||||
pfd.events = events;
|
||||
return poll( &pfd, 1, 0 ) > 0;
|
||||
}
|
||||
|
||||
/* add a timeout user */
|
||||
|
@ -98,11 +136,7 @@ struct timeout_user *add_timeout_user( struct timeval *when, timeout_callback fu
|
|||
/* Now insert it in the linked list */
|
||||
|
||||
for (pos = timeout_head; pos; pos = pos->next)
|
||||
{
|
||||
if (pos->when.tv_sec > user->when.tv_sec) break;
|
||||
if ((pos->when.tv_sec == user->when.tv_sec) &&
|
||||
(pos->when.tv_usec > user->when.tv_usec)) break;
|
||||
}
|
||||
if (!time_before( &pos->when, when )) break;
|
||||
|
||||
if (pos) /* insert it before 'pos' */
|
||||
{
|
||||
|
@ -132,123 +166,103 @@ void remove_timeout_user( struct timeout_user *user )
|
|||
free( user );
|
||||
}
|
||||
|
||||
/* make an absolute timeout value from a relative timeout in milliseconds */
|
||||
void make_timeout( struct timeval *when, int timeout )
|
||||
/* add a timeout in milliseconds to an absolute time */
|
||||
void add_timeout( struct timeval *when, int timeout )
|
||||
{
|
||||
gettimeofday( when, 0 );
|
||||
if (!timeout) return;
|
||||
if ((when->tv_usec += (timeout % 1000) * 1000) >= 1000000)
|
||||
if (timeout)
|
||||
{
|
||||
when->tv_usec -= 1000000;
|
||||
when->tv_sec++;
|
||||
long sec = timeout / 1000;
|
||||
if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
|
||||
{
|
||||
when->tv_usec -= 1000000;
|
||||
when->tv_sec++;
|
||||
}
|
||||
when->tv_sec += sec;
|
||||
}
|
||||
when->tv_sec += timeout / 1000;
|
||||
}
|
||||
|
||||
/* handle an expired timeout */
|
||||
static void handle_timeout( struct timeout_user *user )
|
||||
/* handle the next expired timeout */
|
||||
static void handle_timeout(void)
|
||||
{
|
||||
struct timeout_user *user = timeout_head;
|
||||
timeout_head = user->next;
|
||||
if (user->next) user->next->prev = user->prev;
|
||||
else timeout_tail = user->prev;
|
||||
if (user->prev) user->prev->next = user->next;
|
||||
else timeout_head = user->next;
|
||||
user->callback( user->private );
|
||||
free( user );
|
||||
}
|
||||
|
||||
#ifdef DEBUG_OBJECTS
|
||||
static int do_dump_objects;
|
||||
|
||||
/* SIGHUP handler */
|
||||
static void sighup()
|
||||
static void sighup_handler()
|
||||
{
|
||||
do_dump_objects = 1;
|
||||
}
|
||||
#ifdef DEBUG_OBJECTS
|
||||
dump_objects();
|
||||
#endif
|
||||
|
||||
/* dummy SIGCHLD handler */
|
||||
static void sigchld()
|
||||
{
|
||||
}
|
||||
|
||||
/* server main loop */
|
||||
void select_loop(void)
|
||||
{
|
||||
int i, ret;
|
||||
int ret;
|
||||
sigset_t sigset;
|
||||
struct sigaction action;
|
||||
|
||||
setsid();
|
||||
signal( SIGPIPE, SIG_IGN );
|
||||
signal( SIGCHLD, sigchld );
|
||||
#ifdef DEBUG_OBJECTS
|
||||
signal( SIGHUP, sighup );
|
||||
#endif
|
||||
|
||||
while (nb_users)
|
||||
/* block the signals we use */
|
||||
sigemptyset( &sigset );
|
||||
sigaddset( &sigset, SIGCHLD );
|
||||
sigaddset( &sigset, SIGHUP );
|
||||
sigprocmask( SIG_BLOCK, &sigset, NULL );
|
||||
|
||||
/* set the handlers */
|
||||
action.sa_mask = sigset;
|
||||
action.sa_flags = 0;
|
||||
action.sa_handler = sigchld_handler;
|
||||
sigaction( SIGCHLD, &action, NULL );
|
||||
action.sa_handler = sighup_handler;
|
||||
sigaction( SIGHUP, &action, NULL );
|
||||
|
||||
while (active_users)
|
||||
{
|
||||
fd_set read = read_set, write = write_set, except = except_set;
|
||||
long diff = -1;
|
||||
if (timeout_head)
|
||||
{
|
||||
struct timeval tv, now;
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
if ((timeout_head->when.tv_sec < now.tv_sec) ||
|
||||
((timeout_head->when.tv_sec == now.tv_sec) &&
|
||||
(timeout_head->when.tv_usec < now.tv_usec)))
|
||||
while (timeout_head)
|
||||
{
|
||||
handle_timeout( timeout_head );
|
||||
continue;
|
||||
if (!time_before( &now, &timeout_head->when )) handle_timeout();
|
||||
else
|
||||
{
|
||||
diff = (timeout_head->when.tv_sec - now.tv_sec) * 1000
|
||||
+ (timeout_head->when.tv_usec - now.tv_usec) / 1000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tv.tv_sec = timeout_head->when.tv_sec - now.tv_sec;
|
||||
if ((tv.tv_usec = timeout_head->when.tv_usec - now.tv_usec) < 0)
|
||||
{
|
||||
tv.tv_usec += 1000000;
|
||||
tv.tv_sec--;
|
||||
}
|
||||
#if 0
|
||||
printf( "select: " );
|
||||
for (i = 0; i <= max_fd; i++) printf( "%c", FD_ISSET( i, &read_set ) ? 'r' :
|
||||
(FD_ISSET( i, &write_set ) ? 'w' : '-') );
|
||||
printf( " timeout %d.%06d\n", tv.tv_sec, tv.tv_usec );
|
||||
#endif
|
||||
ret = select( max_fd + 1, &read, &write, &except, &tv );
|
||||
}
|
||||
else /* no timeout */
|
||||
|
||||
sigprocmask( SIG_UNBLOCK, &sigset, NULL );
|
||||
|
||||
/* Note: we assume that the signal handlers do not manipulate the pollfd array
|
||||
* or the timeout list, otherwise there is a race here.
|
||||
*/
|
||||
ret = poll( pollfd, nb_users, diff );
|
||||
|
||||
sigprocmask( SIG_BLOCK, &sigset, NULL );
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
#if 0
|
||||
printf( "select: " );
|
||||
for (i = 0; i <= max_fd; i++) printf( "%c", FD_ISSET( i, &read_set ) ? 'r' :
|
||||
(FD_ISSET( i, &write_set ) ? 'w' : '-') );
|
||||
printf( " no timeout\n" );
|
||||
#endif
|
||||
ret = select( max_fd + 1, &read, &write, &except, NULL );
|
||||
}
|
||||
|
||||
if (!ret) continue;
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR)
|
||||
int i;
|
||||
for (i = 0; i < nb_users; i++)
|
||||
{
|
||||
#ifdef DEBUG_OBJECTS
|
||||
if (do_dump_objects) dump_objects();
|
||||
do_dump_objects = 0;
|
||||
#endif
|
||||
wait4_thread( NULL, 0 );
|
||||
continue;
|
||||
if (pollfd[i].revents)
|
||||
{
|
||||
poll_users[i].func( pollfd[i].revents, poll_users[i].private );
|
||||
if (!--ret) break;
|
||||
}
|
||||
}
|
||||
perror("select");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i <= max_fd; i++)
|
||||
{
|
||||
int event = 0;
|
||||
if (FD_ISSET( i, &except )) event |= EXCEPT_EVENT;
|
||||
if (FD_ISSET( i, &write )) event |= WRITE_EVENT;
|
||||
if (FD_ISSET( i, &read )) event |= READ_EVENT;
|
||||
|
||||
/* Note: users[i] might be NULL here, because an event routine
|
||||
called in an earlier pass of this loop might have removed
|
||||
the current user ... */
|
||||
if (event && users[i])
|
||||
users[i]->func( event, users[i]->private );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
114
server/sock.c
114
server/sock.c
|
@ -42,7 +42,8 @@
|
|||
struct sock
|
||||
{
|
||||
struct object obj; /* object header */
|
||||
struct select_user select; /* select user */
|
||||
int fd; /* file descriptor */
|
||||
int select; /* select user */
|
||||
unsigned int state; /* status bits */
|
||||
unsigned int mask; /* event mask */
|
||||
unsigned int hmask; /* held (blocked) events */
|
||||
|
@ -77,17 +78,17 @@ static const struct object_ops sock_ops =
|
|||
static int sock_event( struct sock *sock )
|
||||
{
|
||||
unsigned int mask = sock->mask & sock->state & ~sock->hmask;
|
||||
int ev = EXCEPT_EVENT;
|
||||
int ev = 0;
|
||||
|
||||
if (sock->state & WS_FD_CONNECT)
|
||||
/* connecting, wait for writable */
|
||||
return WRITE_EVENT | EXCEPT_EVENT;
|
||||
return POLLOUT;
|
||||
if (sock->state & WS_FD_LISTENING)
|
||||
/* listening, wait for readable */
|
||||
return ((sock->hmask & FD_ACCEPT) ? 0 : READ_EVENT) | EXCEPT_EVENT;
|
||||
return (sock->hmask & FD_ACCEPT) ? 0 : POLLIN;
|
||||
|
||||
if (mask & FD_READ) ev |= READ_EVENT;
|
||||
if (mask & FD_WRITE) ev |= WRITE_EVENT;
|
||||
if (mask & FD_READ) ev |= POLLIN;
|
||||
if (mask & FD_WRITE) ev |= POLLOUT;
|
||||
return ev;
|
||||
}
|
||||
|
||||
|
@ -95,8 +96,8 @@ static void sock_reselect( struct sock *sock )
|
|||
{
|
||||
int ev = sock_event( sock );
|
||||
if (debug_level)
|
||||
fprintf(stderr,"sock_reselect(%d): new mask %x\n", sock->select.fd, ev);
|
||||
set_select_events( &sock->select, ev );
|
||||
fprintf(stderr,"sock_reselect(%d): new mask %x\n", sock->fd, ev);
|
||||
set_select_events( sock->select, ev );
|
||||
}
|
||||
|
||||
inline static int sock_error(int s)
|
||||
|
@ -114,11 +115,11 @@ static void sock_select_event( int event, void *private )
|
|||
unsigned int emask;
|
||||
assert( sock->obj.ops == &sock_ops );
|
||||
if (debug_level)
|
||||
fprintf(stderr, "socket %d select event: %x\n", sock->select.fd, event);
|
||||
fprintf(stderr, "socket %d select event: %x\n", sock->fd, event);
|
||||
if (sock->state & WS_FD_CONNECT)
|
||||
{
|
||||
/* connecting */
|
||||
if (event & WRITE_EVENT)
|
||||
if (event & POLLOUT)
|
||||
{
|
||||
/* we got connected */
|
||||
sock->state |= WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE;
|
||||
|
@ -126,43 +127,43 @@ static void sock_select_event( int event, void *private )
|
|||
sock->pmask |= FD_CONNECT;
|
||||
sock->errors[FD_CONNECT_BIT] = 0;
|
||||
if (debug_level)
|
||||
fprintf(stderr, "socket %d connection success\n", sock->select.fd);
|
||||
fprintf(stderr, "socket %d connection success\n", sock->fd);
|
||||
}
|
||||
else if (event & EXCEPT_EVENT)
|
||||
else if (event & (POLLERR|POLLHUP))
|
||||
{
|
||||
/* we didn't get connected? */
|
||||
sock->state &= ~WS_FD_CONNECT;
|
||||
sock->pmask |= FD_CONNECT;
|
||||
sock->errors[FD_CONNECT_BIT] = sock_error( sock->select.fd );
|
||||
sock->errors[FD_CONNECT_BIT] = sock_error( sock->fd );
|
||||
if (debug_level)
|
||||
fprintf(stderr, "socket %d connection failure\n", sock->select.fd);
|
||||
fprintf(stderr, "socket %d connection failure\n", sock->fd);
|
||||
}
|
||||
} else
|
||||
if (sock->state & WS_FD_LISTENING)
|
||||
{
|
||||
/* listening */
|
||||
if (event & READ_EVENT)
|
||||
if (event & POLLIN)
|
||||
{
|
||||
/* incoming connection */
|
||||
sock->pmask |= FD_ACCEPT;
|
||||
sock->errors[FD_ACCEPT_BIT] = 0;
|
||||
sock->hmask |= FD_ACCEPT;
|
||||
}
|
||||
else if (event & EXCEPT_EVENT)
|
||||
else if (event & (POLLERR|POLLHUP))
|
||||
{
|
||||
/* failed incoming connection? */
|
||||
sock->pmask |= FD_ACCEPT;
|
||||
sock->errors[FD_ACCEPT_BIT] = sock_error( sock->select.fd );
|
||||
sock->errors[FD_ACCEPT_BIT] = sock_error( sock->fd );
|
||||
sock->hmask |= FD_ACCEPT;
|
||||
}
|
||||
} else
|
||||
{
|
||||
/* normal data flow */
|
||||
if (event & READ_EVENT)
|
||||
if (event & POLLIN)
|
||||
{
|
||||
/* make sure there's data here */
|
||||
int bytes = 0;
|
||||
ioctl(sock->select.fd, FIONREAD, (char*)&bytes);
|
||||
ioctl(sock->fd, FIONREAD, (char*)&bytes);
|
||||
if (bytes)
|
||||
{
|
||||
/* incoming data */
|
||||
|
@ -170,7 +171,7 @@ static void sock_select_event( int event, void *private )
|
|||
sock->hmask |= FD_READ;
|
||||
sock->errors[FD_READ_BIT] = 0;
|
||||
if (debug_level)
|
||||
fprintf(stderr, "socket %d has %d bytes\n", sock->select.fd, bytes);
|
||||
fprintf(stderr, "socket %d has %d bytes\n", sock->fd, bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -179,27 +180,27 @@ static void sock_select_event( int event, void *private )
|
|||
sock->pmask |= FD_CLOSE;
|
||||
sock->errors[FD_CLOSE_BIT] = 0;
|
||||
if (debug_level)
|
||||
fprintf(stderr, "socket %d is closing\n", sock->select.fd);
|
||||
fprintf(stderr, "socket %d is closing\n", sock->fd);
|
||||
}
|
||||
}
|
||||
if (event & WRITE_EVENT)
|
||||
if (event & POLLOUT)
|
||||
{
|
||||
sock->pmask |= FD_WRITE;
|
||||
sock->hmask |= FD_WRITE;
|
||||
sock->errors[FD_WRITE_BIT] = 0;
|
||||
if (debug_level)
|
||||
fprintf(stderr, "socket %d is writable\n", sock->select.fd);
|
||||
fprintf(stderr, "socket %d is writable\n", sock->fd);
|
||||
}
|
||||
if (event & EXCEPT_EVENT)
|
||||
if (event & (POLLERR|POLLHUP))
|
||||
{
|
||||
sock->errors[FD_CLOSE_BIT] = sock_error( sock->select.fd );
|
||||
sock->errors[FD_CLOSE_BIT] = sock_error( sock->fd );
|
||||
if (sock->errors[FD_CLOSE_BIT])
|
||||
{
|
||||
/* we got an error, socket closing? */
|
||||
sock->state &= ~(WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE);
|
||||
sock->pmask |= FD_CLOSE;
|
||||
if (debug_level)
|
||||
fprintf(stderr, "socket %d aborted by error %d\n", sock->select.fd, sock->errors[FD_CLOSE_BIT]);
|
||||
fprintf(stderr, "socket %d aborted by error %d\n", sock->fd, sock->errors[FD_CLOSE_BIT]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -207,7 +208,7 @@ static void sock_select_event( int event, void *private )
|
|||
sock->pmask |= FD_OOB;
|
||||
sock->hmask |= FD_OOB;
|
||||
if (debug_level)
|
||||
fprintf(stderr, "socket %d got OOB data\n", sock->select.fd);
|
||||
fprintf(stderr, "socket %d got OOB data\n", sock->fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +217,7 @@ static void sock_select_event( int event, void *private )
|
|||
/* wake up anyone waiting for whatever just happened */
|
||||
emask = sock->pmask & sock->mask;
|
||||
if (debug_level && emask)
|
||||
fprintf(stderr, "socket %d pending events: %x\n", sock->select.fd, emask);
|
||||
fprintf(stderr, "socket %d pending events: %x\n", sock->fd, emask);
|
||||
if (emask && sock->event) {
|
||||
if (debug_level) fprintf(stderr, "signalling event ptr %p\n", sock->event);
|
||||
set_event(sock->event);
|
||||
|
@ -232,7 +233,7 @@ static void sock_dump( struct object *obj, int verbose )
|
|||
struct sock *sock = (struct sock *)obj;
|
||||
assert( obj->ops == &sock_ops );
|
||||
printf( "Socket fd=%d, state=%x, mask=%x, pending=%x, held=%x\n",
|
||||
sock->select.fd, sock->state,
|
||||
sock->fd, sock->state,
|
||||
sock->mask, sock->pmask, sock->hmask );
|
||||
}
|
||||
|
||||
|
@ -259,7 +260,7 @@ static int sock_signaled( struct object *obj, struct thread *thread )
|
|||
struct sock *sock = (struct sock *)obj;
|
||||
assert( obj->ops == &sock_ops );
|
||||
|
||||
return check_select_events( &sock->select, sock_event( sock ) );
|
||||
return check_select_events( sock->fd, sock_event( sock ) );
|
||||
}
|
||||
|
||||
static int sock_get_fd( struct object *obj )
|
||||
|
@ -267,7 +268,7 @@ static int sock_get_fd( struct object *obj )
|
|||
struct sock *sock = (struct sock *)obj;
|
||||
int fd;
|
||||
assert( obj->ops == &sock_ops );
|
||||
fd = dup( sock->select.fd );
|
||||
fd = dup( sock->fd );
|
||||
if (fd==-1)
|
||||
sock_set_error();
|
||||
return fd;
|
||||
|
@ -278,9 +279,8 @@ static void sock_destroy( struct object *obj )
|
|||
struct sock *sock = (struct sock *)obj;
|
||||
assert( obj->ops == &sock_ops );
|
||||
|
||||
unregister_select_user( &sock->select );
|
||||
/* FIXME: special socket shutdown stuff? */
|
||||
close( sock->select.fd );
|
||||
remove_select_user( sock->select );
|
||||
if (sock->event)
|
||||
{
|
||||
/* if the service thread was waiting for the event object,
|
||||
|
@ -311,15 +311,17 @@ static struct object *create_socket( int family, int type, int protocol )
|
|||
close( sockfd );
|
||||
return NULL;
|
||||
}
|
||||
sock->select.fd = sockfd;
|
||||
sock->select.func = sock_select_event;
|
||||
sock->select.private = sock;
|
||||
sock->state = (type!=SOCK_STREAM) ? WS_FD_READ|WS_FD_WRITE : 0;
|
||||
sock->mask = 0;
|
||||
sock->hmask = 0;
|
||||
sock->pmask = 0;
|
||||
sock->event = NULL;
|
||||
register_select_user( &sock->select );
|
||||
sock->fd = sockfd;
|
||||
sock->state = (type!=SOCK_STREAM) ? WS_FD_READ|WS_FD_WRITE : 0;
|
||||
sock->mask = 0;
|
||||
sock->hmask = 0;
|
||||
sock->pmask = 0;
|
||||
sock->event = NULL;
|
||||
if ((sock->select = add_select_user( sockfd, sock_select_event, sock )) == -1)
|
||||
{
|
||||
release_object( sock );
|
||||
return NULL;
|
||||
}
|
||||
sock_reselect( sock );
|
||||
clear_error();
|
||||
return &sock->obj;
|
||||
|
@ -343,7 +345,7 @@ static struct object *accept_socket( int handle )
|
|||
* return.
|
||||
*/
|
||||
slen = sizeof(saddr);
|
||||
acceptfd = accept(sock->select.fd,&saddr,&slen);
|
||||
acceptfd = accept(sock->fd,&saddr,&slen);
|
||||
if (acceptfd==-1) {
|
||||
sock_set_error();
|
||||
release_object( sock );
|
||||
|
@ -356,19 +358,21 @@ static struct object *accept_socket( int handle )
|
|||
return NULL;
|
||||
}
|
||||
|
||||
acceptsock->select.fd = acceptfd;
|
||||
acceptsock->select.func = sock_select_event;
|
||||
acceptsock->select.private = acceptsock;
|
||||
acceptsock->state = WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE;
|
||||
acceptsock->mask = sock->mask;
|
||||
acceptsock->hmask = 0;
|
||||
acceptsock->pmask = 0;
|
||||
if (sock->event)
|
||||
acceptsock->event = (struct event *)grab_object( sock->event );
|
||||
else
|
||||
acceptsock->event = NULL;
|
||||
acceptsock->fd = acceptfd;
|
||||
acceptsock->state = WS_FD_CONNECTED|WS_FD_READ|WS_FD_WRITE;
|
||||
acceptsock->mask = sock->mask;
|
||||
acceptsock->hmask = 0;
|
||||
acceptsock->pmask = 0;
|
||||
acceptsock->event = NULL;
|
||||
acceptsock->select = add_select_user( acceptfd, sock_select_event, acceptsock );
|
||||
if (acceptsock->select == -1)
|
||||
{
|
||||
release_object( acceptsock );
|
||||
release_object( sock );
|
||||
return NULL;
|
||||
}
|
||||
if (sock->event) acceptsock->event = (struct event *)grab_object( sock->event );
|
||||
|
||||
register_select_user( &acceptsock->select );
|
||||
sock_reselect( acceptsock );
|
||||
clear_error();
|
||||
sock->pmask &= ~FD_ACCEPT;
|
||||
|
|
|
@ -32,7 +32,8 @@
|
|||
/* client structure */
|
||||
struct client
|
||||
{
|
||||
struct select_user select; /* select user */
|
||||
int fd; /* socket file descriptor */
|
||||
int select; /* select user id */
|
||||
unsigned int res; /* current result to send */
|
||||
int pass_fd; /* fd to pass to and from the client */
|
||||
struct thread *self; /* client thread (opaque pointer) */
|
||||
|
@ -62,7 +63,7 @@ static int do_write( struct client *client )
|
|||
|
||||
if (client->pass_fd == -1)
|
||||
{
|
||||
ret = write( client->select.fd, &client->res, sizeof(client->res) );
|
||||
ret = write( client->fd, &client->res, sizeof(client->res) );
|
||||
if (ret == sizeof(client->res)) goto ok;
|
||||
}
|
||||
else /* we have an fd to send */
|
||||
|
@ -79,7 +80,7 @@ static int do_write( struct client *client )
|
|||
myiovec.iov_base = (void *)&client->res;
|
||||
myiovec.iov_len = sizeof(client->res);
|
||||
|
||||
ret = sendmsg( client->select.fd, &msghdr, 0 );
|
||||
ret = sendmsg( client->fd, &msghdr, 0 );
|
||||
close( client->pass_fd );
|
||||
client->pass_fd = -1;
|
||||
if (ret == sizeof(client->res)) goto ok;
|
||||
|
@ -91,10 +92,10 @@ static int do_write( struct client *client )
|
|||
}
|
||||
else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(client->res) );
|
||||
remove_client( client, BROKEN_PIPE );
|
||||
return 0;
|
||||
return -1;
|
||||
|
||||
ok:
|
||||
set_select_events( &client->select, READ_EVENT );
|
||||
set_select_events( client->select, POLLIN );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -119,7 +120,7 @@ static void do_read( struct client *client )
|
|||
myiovec.iov_base = (void *)&req;
|
||||
myiovec.iov_len = sizeof(req);
|
||||
|
||||
ret = recvmsg( client->select.fd, &msghdr, 0 );
|
||||
ret = recvmsg( client->fd, &msghdr, 0 );
|
||||
#ifndef HAVE_MSGHDR_ACCRIGHTS
|
||||
client->pass_fd = cmsg.fd;
|
||||
#endif
|
||||
|
@ -150,8 +151,12 @@ static void do_read( struct client *client )
|
|||
static void client_event( int event, void *private )
|
||||
{
|
||||
struct client *client = (struct client *)private;
|
||||
if (event & WRITE_EVENT) do_write( client );
|
||||
if (event & READ_EVENT) do_read( client );
|
||||
if (event & (POLLERR | POLLHUP)) remove_client( client, BROKEN_PIPE );
|
||||
else
|
||||
{
|
||||
if (event & POLLOUT) do_write( client );
|
||||
if (event & POLLIN) do_read( client );
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************/
|
||||
|
@ -167,14 +172,16 @@ struct client *add_client( int fd, struct thread *self )
|
|||
flags = fcntl( fd, F_GETFL, 0 );
|
||||
fcntl( fd, F_SETFL, flags | O_NONBLOCK );
|
||||
|
||||
client->select.fd = fd;
|
||||
client->select.func = client_event;
|
||||
client->select.private = client;
|
||||
client->self = self;
|
||||
client->timeout = NULL;
|
||||
client->pass_fd = -1;
|
||||
register_select_user( &client->select );
|
||||
set_select_events( &client->select, READ_EVENT );
|
||||
client->fd = fd;
|
||||
client->self = self;
|
||||
client->timeout = NULL;
|
||||
client->pass_fd = -1;
|
||||
if ((client->select = add_select_user( fd, client_event, client )) == -1)
|
||||
{
|
||||
free( client );
|
||||
return NULL;
|
||||
}
|
||||
set_select_events( client->select, POLLIN );
|
||||
return client;
|
||||
}
|
||||
|
||||
|
@ -186,8 +193,7 @@ void remove_client( struct client *client, int exit_code )
|
|||
call_kill_handler( client->self, exit_code );
|
||||
|
||||
if (client->timeout) remove_timeout_user( client->timeout );
|
||||
unregister_select_user( &client->select );
|
||||
close( client->select.fd );
|
||||
remove_select_user( client->select );
|
||||
|
||||
/* Purge messages */
|
||||
if (client->pass_fd != -1) close( client->pass_fd );
|
||||
|
@ -206,5 +212,5 @@ void client_reply( struct client *client, unsigned int res )
|
|||
{
|
||||
if (debug_level) trace_reply( client->self, res, client->pass_fd );
|
||||
client->res = res;
|
||||
if (!do_write( client )) set_select_events( &client->select, WRITE_EVENT );
|
||||
if (!do_write( client )) set_select_events( client->select, POLLOUT );
|
||||
}
|
||||
|
|
|
@ -330,7 +330,11 @@ static int wait_on( struct thread *thread, int count,
|
|||
wait->count = count;
|
||||
wait->flags = flags;
|
||||
wait->user = NULL;
|
||||
if (flags & SELECT_TIMEOUT) make_timeout( &wait->timeout, timeout );
|
||||
if (flags & SELECT_TIMEOUT)
|
||||
{
|
||||
gettimeofday( &wait->timeout, 0 );
|
||||
add_timeout( &wait->timeout, timeout );
|
||||
}
|
||||
|
||||
for (i = 0, entry = wait->queues; i < count; i++, entry++)
|
||||
{
|
||||
|
@ -399,9 +403,7 @@ static int check_wait( struct thread *thread, int *signaled )
|
|||
{
|
||||
struct timeval now;
|
||||
gettimeofday( &now, NULL );
|
||||
if ((now.tv_sec > wait->timeout.tv_sec) ||
|
||||
((now.tv_sec == wait->timeout.tv_sec) &&
|
||||
(now.tv_usec >= wait->timeout.tv_usec)))
|
||||
if (!time_before( &now, &wait->timeout ))
|
||||
{
|
||||
*signaled = STATUS_TIMEOUT;
|
||||
return 1;
|
||||
|
|
|
@ -80,7 +80,8 @@ extern void wake_up( struct object *obj, int max );
|
|||
|
||||
/* ptrace functions */
|
||||
|
||||
extern void wait4_thread( struct thread *thread, int wait );
|
||||
extern void sigchld_handler();
|
||||
extern void wait4_thread( struct thread *thread, int signal );
|
||||
extern void stop_thread( struct thread *thread );
|
||||
extern void continue_thread( struct thread *thread );
|
||||
extern void detach_thread( struct thread *thread );
|
||||
|
|
|
@ -81,7 +81,7 @@ static void timer_callback( void *private )
|
|||
|
||||
if (timer->period) /* schedule the next expiration */
|
||||
{
|
||||
make_timeout( &timer->when, timer->period );
|
||||
add_timeout( &timer->when, timer->period );
|
||||
timer->timeout = add_timeout_user( &timer->when, timer_callback, timer );
|
||||
}
|
||||
else timer->timeout = NULL;
|
||||
|
@ -101,8 +101,17 @@ static void set_timer( struct timer *timer, int sec, int usec, int period,
|
|||
timer->signaled = 0;
|
||||
}
|
||||
if (timer->timeout) remove_timeout_user( timer->timeout );
|
||||
timer->when.tv_sec = sec;
|
||||
timer->when.tv_usec = usec;
|
||||
if (!sec && !usec)
|
||||
{
|
||||
/* special case: use now + period as first expiration */
|
||||
gettimeofday( &timer->when, 0 );
|
||||
add_timeout( &timer->when, period );
|
||||
}
|
||||
else
|
||||
{
|
||||
timer->when.tv_sec = sec;
|
||||
timer->when.tv_usec = usec;
|
||||
}
|
||||
timer->period = period;
|
||||
timer->callback = callback;
|
||||
timer->arg = arg;
|
||||
|
|
Loading…
Reference in New Issue