Commit 1dca5e24 authored by Alexandre Julliard's avatar Alexandre Julliard

Moved poll handling to the generic part of the server objects.

Fixed busy waiting on POLLERR events. Merged struct client into struct thread.
parent 81ee21dd
...@@ -25,7 +25,6 @@ C_SRCS = \ ...@@ -25,7 +25,6 @@ C_SRCS = \
semaphore.c \ semaphore.c \
snapshot.c \ snapshot.c \
sock.c \ sock.c \
socket.c \
thread.c \ thread.c \
timer.c \ timer.c \
trace.c \ trace.c \
......
...@@ -27,24 +27,26 @@ static int change_signaled( struct object *obj, struct thread *thread ); ...@@ -27,24 +27,26 @@ static int change_signaled( struct object *obj, struct thread *thread );
static const struct object_ops change_ops = static const struct object_ops change_ops =
{ {
sizeof(struct change), sizeof(struct change), /* size */
change_dump, change_dump, /* dump */
add_queue, add_queue, /* add_queue */
remove_queue, remove_queue, /* remove_queue */
change_signaled, change_signaled, /* signaled */
no_satisfied, no_satisfied, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
no_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
no_destroy /* destroy */
}; };
static struct change *create_change_notification( int subtree, int filter ) static struct change *create_change_notification( int subtree, int filter )
{ {
struct change *change; struct change *change;
if ((change = alloc_object( &change_ops ))) if ((change = alloc_object( &change_ops, -1 )))
{ {
change->subtree = subtree; change->subtree = subtree;
change->filter = filter; change->filter = filter;
......
...@@ -34,23 +34,25 @@ static int device_get_info( struct object *obj, struct get_file_info_request *re ...@@ -34,23 +34,25 @@ static int device_get_info( struct object *obj, struct get_file_info_request *re
static const struct object_ops device_ops = static const struct object_ops device_ops =
{ {
sizeof(struct device), sizeof(struct device), /* size */
device_dump, device_dump, /* dump */
no_add_queue, no_add_queue, /* add_queue */
NULL, /* should never get called */ NULL, /* remove_queue */
NULL, /* should never get called */ NULL, /* signaled */
NULL, /* should never get called */ NULL, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
device_get_info, no_write_fd, /* get_write_fd */
no_destroy no_flush, /* flush */
device_get_info, /* get_file_info */
no_destroy /* destroy */
}; };
static struct device *create_device( int id ) static struct device *create_device( int id )
{ {
struct device *dev; struct device *dev;
if ((dev = alloc_object( &device_ops ))) if ((dev = alloc_object( &device_ops, -1 )))
{ {
dev->id = id; dev->id = id;
} }
......
...@@ -28,17 +28,19 @@ static int event_satisfied( struct object *obj, struct thread *thread ); ...@@ -28,17 +28,19 @@ static int event_satisfied( struct object *obj, struct thread *thread );
static const struct object_ops event_ops = static const struct object_ops event_ops =
{ {
sizeof(struct event), sizeof(struct event), /* size */
event_dump, event_dump, /* dump */
add_queue, add_queue, /* add_queue */
remove_queue, remove_queue, /* remove_queue */
event_signaled, event_signaled, /* signaled */
event_satisfied, event_satisfied, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
no_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
no_destroy /* destroy */
}; };
......
...@@ -32,8 +32,6 @@ ...@@ -32,8 +32,6 @@
struct file struct file
{ {
struct object obj; /* object header */ struct object obj; /* object header */
int fd; /* file descriptor */
int select; /* select user id */
struct file *next; /* next file in hashing list */ struct file *next; /* next file in hashing list */
char *name; /* file name */ char *name; /* file name */
unsigned int access; /* file access (GENERIC_READ/WRITE) */ unsigned int access; /* file access (GENERIC_READ/WRITE) */
...@@ -46,9 +44,7 @@ struct file ...@@ -46,9 +44,7 @@ struct file
static struct file *file_hash[NAME_HASH_SIZE]; static struct file *file_hash[NAME_HASH_SIZE];
static void file_dump( struct object *obj, int verbose ); static void file_dump( struct object *obj, int verbose );
static int file_add_queue( struct object *obj, struct wait_queue_entry *entry ); static int file_get_poll_events( struct object *obj );
static void file_remove_queue( struct object *obj, struct wait_queue_entry *entry );
static int file_signaled( struct object *obj, struct thread *thread );
static int file_get_read_fd( struct object *obj ); static int file_get_read_fd( struct object *obj );
static int file_get_write_fd( struct object *obj ); static int file_get_write_fd( struct object *obj );
static int file_flush( struct object *obj ); static int file_flush( struct object *obj );
...@@ -57,17 +53,19 @@ static void file_destroy( struct object *obj ); ...@@ -57,17 +53,19 @@ static void file_destroy( struct object *obj );
static const struct object_ops file_ops = static const struct object_ops file_ops =
{ {
sizeof(struct file), sizeof(struct file), /* size */
file_dump, file_dump, /* dump */
file_add_queue, default_poll_add_queue, /* add_queue */
file_remove_queue, default_poll_remove_queue, /* remove_queue */
file_signaled, default_poll_signaled, /* signaled */
no_satisfied, no_satisfied, /* satisfied */
file_get_read_fd, file_get_poll_events, /* get_poll_events */
file_get_write_fd, default_poll_event, /* poll_event */
file_flush, file_get_read_fd, /* get_read_fd */
file_get_info, file_get_write_fd, /* get_write_fd */
file_destroy file_flush, /* flush */
file_get_info, /* get_file_info */
file_destroy /* destroy */
}; };
...@@ -103,23 +101,19 @@ static int check_sharing( const char *name, int hash, unsigned int access, ...@@ -103,23 +101,19 @@ static int check_sharing( const char *name, int hash, unsigned int access,
return 0; return 0;
} }
/* create a file from a file descriptor */
/* if the function fails the fd is closed */
static struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing, static struct file *create_file_for_fd( int fd, unsigned int access, unsigned int sharing,
unsigned int attrs ) unsigned int attrs )
{ {
struct file *file; struct file *file;
if ((file = alloc_object( &file_ops ))) if ((file = alloc_object( &file_ops, fd )))
{ {
file->name = NULL; file->name = NULL;
file->next = NULL; file->next = NULL;
file->fd = fd;
file->access = access; file->access = access;
file->flags = attrs; file->flags = attrs;
file->sharing = sharing; file->sharing = sharing;
if ((file->select = add_select_user( fd, default_select_event, file )) == -1)
{
release_object( file );
file = NULL;
}
} }
return file; return file;
} }
...@@ -171,7 +165,11 @@ static struct file *create_file( const char *nameptr, size_t len, unsigned int a ...@@ -171,7 +165,11 @@ static struct file *create_file( const char *nameptr, size_t len, unsigned int a
goto error; goto error;
} }
if (!(file = create_file_for_fd( fd, access, sharing, attrs ))) goto error; if (!(file = create_file_for_fd( fd, access, sharing, attrs )))
{
free( name );
return NULL;
}
file->name = name; file->name = name;
file->next = file_hash[hash]; file->next = file_hash[hash];
file_hash[hash] = file; file_hash[hash] = file;
...@@ -212,81 +210,41 @@ int create_anonymous_file(void) ...@@ -212,81 +210,41 @@ int create_anonymous_file(void)
/* Create a temp file for anonymous mappings */ /* Create a temp file for anonymous mappings */
struct file *create_temp_file( int access ) struct file *create_temp_file( int access )
{ {
struct file *file;
int fd; int fd;
if ((fd = create_anonymous_file()) == -1) return NULL; if ((fd = create_anonymous_file()) == -1) return NULL;
if (!(file = create_file_for_fd( fd, access, 0, 0 ))) close( fd ); return create_file_for_fd( fd, access, 0, 0 );
return file;
} }
static void file_dump( struct object *obj, int verbose ) static void file_dump( struct object *obj, int verbose )
{ {
struct file *file = (struct file *)obj; struct file *file = (struct file *)obj;
assert( obj->ops == &file_ops ); assert( obj->ops == &file_ops );
fprintf( stderr, "File fd=%d flags=%08x name='%s'\n", file->fd, file->flags, file->name ); fprintf( stderr, "File fd=%d flags=%08x name='%s'\n", file->obj.fd, file->flags, file->name );
} }
static int file_add_queue( struct object *obj, struct wait_queue_entry *entry ) static int file_get_poll_events( struct object *obj )
{ {
struct file *file = (struct file *)obj; struct file *file = (struct file *)obj;
assert( obj->ops == &file_ops );
if (!obj->head) /* first on the queue */
{
int events = 0;
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;
}
static void file_remove_queue( struct object *obj, struct wait_queue_entry *entry )
{
struct file *file = (struct file *)grab_object(obj);
assert( obj->ops == &file_ops );
remove_queue( obj, entry );
if (!obj->head) /* last on the queue is gone */
set_select_events( file->select, 0 );
release_object( obj );
}
static int file_signaled( struct object *obj, struct thread *thread )
{
int events = 0; int events = 0;
struct file *file = (struct file *)obj;
assert( obj->ops == &file_ops ); assert( obj->ops == &file_ops );
if (file->access & GENERIC_READ) events |= POLLIN; if (file->access & GENERIC_READ) events |= POLLIN;
if (file->access & GENERIC_WRITE) events |= POLLOUT; if (file->access & GENERIC_WRITE) events |= POLLOUT;
if (check_select_events( file->fd, events )) return events;
{
/* stop waiting on select() if we are signaled */
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 );
return 0;
}
} }
static int file_get_read_fd( struct object *obj ) static int file_get_read_fd( struct object *obj )
{ {
struct file *file = (struct file *)obj; struct file *file = (struct file *)obj;
assert( obj->ops == &file_ops ); assert( obj->ops == &file_ops );
return dup( file->fd ); return dup( file->obj.fd );
} }
static int file_get_write_fd( struct object *obj ) static int file_get_write_fd( struct object *obj )
{ {
struct file *file = (struct file *)obj; struct file *file = (struct file *)obj;
assert( obj->ops == &file_ops ); assert( obj->ops == &file_ops );
return dup( file->fd ); return dup( file->obj.fd );
} }
static int file_flush( struct object *obj ) static int file_flush( struct object *obj )
...@@ -295,7 +253,7 @@ static int file_flush( struct object *obj ) ...@@ -295,7 +253,7 @@ static int file_flush( struct object *obj )
struct file *file = (struct file *)grab_object(obj); struct file *file = (struct file *)grab_object(obj);
assert( obj->ops == &file_ops ); assert( obj->ops == &file_ops );
ret = (fsync( file->fd ) != -1); ret = (fsync( file->obj.fd ) != -1);
if (!ret) file_set_error(); if (!ret) file_set_error();
release_object( file ); release_object( file );
return ret; return ret;
...@@ -307,13 +265,13 @@ static int file_get_info( struct object *obj, struct get_file_info_request *req ...@@ -307,13 +265,13 @@ static int file_get_info( struct object *obj, struct get_file_info_request *req
struct file *file = (struct file *)obj; struct file *file = (struct file *)obj;
assert( obj->ops == &file_ops ); assert( obj->ops == &file_ops );
if (fstat( file->fd, &st ) == -1) if (fstat( file->obj.fd, &st ) == -1)
{ {
file_set_error(); file_set_error();
return 0; return 0;
} }
if (S_ISCHR(st.st_mode) || S_ISFIFO(st.st_mode) || if (S_ISCHR(st.st_mode) || S_ISFIFO(st.st_mode) ||
S_ISSOCK(st.st_mode) || isatty(file->fd)) req->type = FILE_TYPE_CHAR; S_ISSOCK(st.st_mode) || isatty(file->obj.fd)) req->type = FILE_TYPE_CHAR;
else req->type = FILE_TYPE_DISK; else req->type = FILE_TYPE_DISK;
if (S_ISDIR(st.st_mode)) req->attr = FILE_ATTRIBUTE_DIRECTORY; if (S_ISDIR(st.st_mode)) req->attr = FILE_ATTRIBUTE_DIRECTORY;
else req->attr = FILE_ATTRIBUTE_ARCHIVE; else req->attr = FILE_ATTRIBUTE_ARCHIVE;
...@@ -344,7 +302,6 @@ static void file_destroy( struct object *obj ) ...@@ -344,7 +302,6 @@ static void file_destroy( struct object *obj )
if (file->flags & FILE_FLAG_DELETE_ON_CLOSE) unlink( file->name ); if (file->flags & FILE_FLAG_DELETE_ON_CLOSE) unlink( file->name );
free( file->name ); free( file->name );
} }
remove_select_user( file->select );
} }
/* set the last error depending on errno */ /* set the last error depending on errno */
...@@ -379,7 +336,7 @@ struct file *get_file_obj( struct process *process, int handle, unsigned int acc ...@@ -379,7 +336,7 @@ struct file *get_file_obj( struct process *process, int handle, unsigned int acc
int file_get_mmap_fd( struct file *file ) int file_get_mmap_fd( struct file *file )
{ {
return dup( file->fd ); return dup( file->obj.fd );
} }
static int set_file_pointer( int handle, int *low, int *high, int whence ) static int set_file_pointer( int handle, int *low, int *high, int whence )
...@@ -396,7 +353,7 @@ static int set_file_pointer( int handle, int *low, int *high, int whence ) ...@@ -396,7 +353,7 @@ static int set_file_pointer( int handle, int *low, int *high, int whence )
if (!(file = get_file_obj( current->process, handle, 0 ))) if (!(file = get_file_obj( current->process, handle, 0 )))
return 0; return 0;
if ((result = lseek( file->fd, *low, whence )) == -1) if ((result = lseek( file->obj.fd, *low, whence )) == -1)
{ {
/* Check for seek before start of file */ /* Check for seek before start of file */
if ((errno == EINVAL) && (whence != SEEK_SET) && (*low < 0)) if ((errno == EINVAL) && (whence != SEEK_SET) && (*low < 0))
...@@ -418,8 +375,8 @@ static int truncate_file( int handle ) ...@@ -418,8 +375,8 @@ static int truncate_file( int handle )
if (!(file = get_file_obj( current->process, handle, GENERIC_WRITE ))) if (!(file = get_file_obj( current->process, handle, GENERIC_WRITE )))
return 0; return 0;
if (((result = lseek( file->fd, 0, SEEK_CUR )) == -1) || if (((result = lseek( file->obj.fd, 0, SEEK_CUR )) == -1) ||
(ftruncate( file->fd, result ) == -1)) (ftruncate( file->obj.fd, result ) == -1))
{ {
file_set_error(); file_set_error();
release_object( file ); release_object( file );
...@@ -440,13 +397,13 @@ int grow_file( struct file *file, int size_high, int size_low ) ...@@ -440,13 +397,13 @@ int grow_file( struct file *file, int size_high, int size_low )
set_error( ERROR_INVALID_PARAMETER ); set_error( ERROR_INVALID_PARAMETER );
return 0; return 0;
} }
if (fstat( file->fd, &st ) == -1) if (fstat( file->obj.fd, &st ) == -1)
{ {
file_set_error(); file_set_error();
return 0; return 0;
} }
if (st.st_size >= size_low) return 1; /* already large enough */ if (st.st_size >= size_low) return 1; /* already large enough */
if (ftruncate( file->fd, size_low ) != -1) return 1; if (ftruncate( file->obj.fd, size_low ) != -1) return 1;
file_set_error(); file_set_error();
return 0; return 0;
} }
...@@ -518,7 +475,6 @@ DECL_HANDLER(alloc_file_handle) ...@@ -518,7 +475,6 @@ DECL_HANDLER(alloc_file_handle)
req->handle = alloc_handle( current->process, file, req->access, 0 ); req->handle = alloc_handle( current->process, file, req->access, 0 );
release_object( file ); release_object( file );
} }
else close( fd );
} }
else file_set_error(); else file_set_error();
} }
......
...@@ -69,17 +69,19 @@ static void handle_table_destroy( struct object *obj ); ...@@ -69,17 +69,19 @@ static void handle_table_destroy( struct object *obj );
static const struct object_ops handle_table_ops = static const struct object_ops handle_table_ops =
{ {
sizeof(struct handle_table), sizeof(struct handle_table), /* size */
handle_table_dump, handle_table_dump, /* dump */
no_add_queue, no_add_queue, /* add_queue */
NULL, /* should never get called */ NULL, /* remove_queue */
NULL, /* should never get called */ NULL, /* signaled */
NULL, /* should never get called */ NULL, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
handle_table_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
handle_table_destroy /* destroy */
}; };
/* dump a handle table */ /* dump a handle table */
...@@ -127,7 +129,7 @@ struct object *alloc_handle_table( struct process *process, int count ) ...@@ -127,7 +129,7 @@ struct object *alloc_handle_table( struct process *process, int count )
struct handle_table *table; struct handle_table *table;
if (count < MIN_HANDLE_ENTRIES) count = MIN_HANDLE_ENTRIES; if (count < MIN_HANDLE_ENTRIES) count = MIN_HANDLE_ENTRIES;
if (!(table = alloc_object( &handle_table_ops ))) if (!(table = alloc_object( &handle_table_ops, -1 )))
return NULL; return NULL;
table->process = process; table->process = process;
table->count = count; table->count = count;
......
...@@ -32,17 +32,19 @@ static void mapping_destroy( struct object *obj ); ...@@ -32,17 +32,19 @@ static void mapping_destroy( struct object *obj );
static const struct object_ops mapping_ops = static const struct object_ops mapping_ops =
{ {
sizeof(struct mapping), sizeof(struct mapping), /* size */
mapping_dump, mapping_dump, /* dump */
no_add_queue, no_add_queue, /* add_queue */
NULL, /* should never get called */ NULL, /* remove_queue */
NULL, /* should never get called */ NULL, /* signaled */
NULL, /* should never get called */ NULL, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
mapping_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
mapping_destroy /* destroy */
}; };
#ifdef __i386__ #ifdef __i386__
......
...@@ -32,17 +32,19 @@ static void mutex_destroy( struct object *obj ); ...@@ -32,17 +32,19 @@ static void mutex_destroy( struct object *obj );
static const struct object_ops mutex_ops = static const struct object_ops mutex_ops =
{ {
sizeof(struct mutex), sizeof(struct mutex), /* size */
mutex_dump, mutex_dump, /* dump */
add_queue, add_queue, /* add_queue */
remove_queue, remove_queue, /* remove_queue */
mutex_signaled, mutex_signaled, /* signaled */
mutex_satisfied, mutex_satisfied, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
mutex_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
mutex_destroy /* destroy */
}; };
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "winerror.h" #include "winerror.h"
#include "thread.h" #include "thread.h"
...@@ -121,23 +122,34 @@ static void set_object_name( struct object *obj, struct object_name *ptr ) ...@@ -121,23 +122,34 @@ static void set_object_name( struct object *obj, struct object_name *ptr )
} }
/* allocate and initialize an object */ /* allocate and initialize an object */
void *alloc_object( const struct object_ops *ops ) /* if the function fails the fd is closed */
void *alloc_object( const struct object_ops *ops, int fd )
{ {
struct object *obj = mem_alloc( ops->size ); struct object *obj = mem_alloc( ops->size );
if (obj) if (obj)
{ {
obj->refcount = 1; obj->refcount = 1;
obj->fd = fd;
obj->select = -1;
obj->ops = ops; obj->ops = ops;
obj->head = NULL; obj->head = NULL;
obj->tail = NULL; obj->tail = NULL;
obj->name = NULL; obj->name = NULL;
if ((fd != -1) && (add_select_user( obj ) == -1))
{
close( fd );
free( obj );
return NULL;
}
#ifdef DEBUG_OBJECTS #ifdef DEBUG_OBJECTS
obj->prev = NULL; obj->prev = NULL;
if ((obj->next = first) != NULL) obj->next->prev = obj; if ((obj->next = first) != NULL) obj->next->prev = obj;
first = obj; first = obj;
#endif #endif
return obj;
} }
return obj; if (fd != -1) close( fd );
return NULL;
} }
void *create_named_object( const struct object_ops *ops, const WCHAR *name, size_t len ) void *create_named_object( const struct object_ops *ops, const WCHAR *name, size_t len )
...@@ -145,7 +157,7 @@ void *create_named_object( const struct object_ops *ops, const WCHAR *name, size ...@@ -145,7 +157,7 @@ void *create_named_object( const struct object_ops *ops, const WCHAR *name, size
struct object *obj; struct object *obj;
struct object_name *name_ptr; struct object_name *name_ptr;
if (!name || !len) return alloc_object( ops ); if (!name || !len) return alloc_object( ops, -1 );
if (!(name_ptr = alloc_name( name, len ))) return NULL; if (!(name_ptr = alloc_name( name, len ))) return NULL;
if ((obj = find_object( name_ptr->name, name_ptr->len ))) if ((obj = find_object( name_ptr->name, name_ptr->len )))
...@@ -159,7 +171,7 @@ void *create_named_object( const struct object_ops *ops, const WCHAR *name, size ...@@ -159,7 +171,7 @@ void *create_named_object( const struct object_ops *ops, const WCHAR *name, size
set_error( ERROR_INVALID_HANDLE ); set_error( ERROR_INVALID_HANDLE );
return NULL; return NULL;
} }
if ((obj = alloc_object( ops ))) if ((obj = alloc_object( ops, -1 )))
{ {
set_object_name( obj, name_ptr ); set_object_name( obj, name_ptr );
clear_error(); clear_error();
...@@ -199,14 +211,16 @@ void release_object( void *ptr ) ...@@ -199,14 +211,16 @@ void release_object( void *ptr )
/* if the refcount is 0, nobody can be in the wait queue */ /* if the refcount is 0, nobody can be in the wait queue */
assert( !obj->head ); assert( !obj->head );
assert( !obj->tail ); assert( !obj->tail );
obj->ops->destroy( obj );
if (obj->name) free_name( obj ); if (obj->name) free_name( obj );
if (obj->select != -1) remove_select_user( obj );
if (obj->fd != -1) close( obj->fd );
#ifdef DEBUG_OBJECTS #ifdef DEBUG_OBJECTS
if (obj->next) obj->next->prev = obj->prev; if (obj->next) obj->next->prev = obj->prev;
if (obj->prev) obj->prev->next = obj->next; if (obj->prev) obj->prev->next = obj->next;
else first = obj->next; else first = obj->next;
#endif
obj->ops->destroy( obj );
memset( obj, 0xaa, obj->ops->size ); memset( obj, 0xaa, obj->ops->size );
#endif
free( obj ); free( obj );
} }
} }
...@@ -224,7 +238,7 @@ struct object *find_object( const WCHAR *name, size_t len ) ...@@ -224,7 +238,7 @@ struct object *find_object( const WCHAR *name, size_t len )
return NULL; return NULL;
} }
/* functions for unimplemented object operations */ /* functions for unimplemented/default object operations */
int no_add_queue( struct object *obj, struct wait_queue_entry *entry ) int no_add_queue( struct object *obj, struct wait_queue_entry *entry )
{ {
...@@ -265,9 +279,45 @@ void no_destroy( struct object *obj ) ...@@ -265,9 +279,45 @@ void no_destroy( struct object *obj )
{ {
} }
void default_select_event( int event, void *private ) /* default add_queue() routine for objects that poll() on an fd */
int default_poll_add_queue( struct object *obj, struct wait_queue_entry *entry )
{
if (!obj->head) /* first on the queue */
set_select_events( obj, obj->ops->get_poll_events( obj ) );
add_queue( obj, entry );
return 1;
}
/* default remove_queue() routine for objects that poll() on an fd */
void default_poll_remove_queue( struct object *obj, struct wait_queue_entry *entry )
{
grab_object(obj);
remove_queue( obj, entry );
if (!obj->head) /* last on the queue is gone */
set_select_events( obj, 0 );
release_object( obj );
}
/* default signaled() routine for objects that poll() on an fd */
int default_poll_signaled( struct object *obj, struct thread *thread )
{
int events = obj->ops->get_poll_events( obj );
if (check_select_events( obj->fd, events ))
{
/* stop waiting on select() if we are signaled */
set_select_events( obj, 0 );
return 1;
}
/* restart waiting on select() if we are no longer signaled */
if (obj->head) set_select_events( obj, events );
return 0;
}
/* default handler for poll() events */
void default_poll_event( struct object *obj, int event )
{ {
struct object *obj = (struct object *)private; /* an error occurred, stop polling this fd to avoid busy-looping */
assert( obj ); if (event & (POLLERR | POLLHUP)) set_select_events( obj, -1 );
wake_up( obj, 0 ); wake_up( obj, 0 );
} }
...@@ -41,6 +41,10 @@ struct object_ops ...@@ -41,6 +41,10 @@ struct object_ops
int (*signaled)(struct object *,struct thread *); int (*signaled)(struct object *,struct thread *);
/* wait satisfied; return 1 if abandoned */ /* wait satisfied; return 1 if abandoned */
int (*satisfied)(struct object *,struct thread *); int (*satisfied)(struct object *,struct thread *);
/* get the events we want to poll() for on this object */
int (*get_poll_events)(struct object *);
/* a poll() event occured */
void (*poll_event)(struct object *,int event);
/* return a Unix fd that can be used to read from the object */ /* return a Unix fd that can be used to read from the object */
int (*get_read_fd)(struct object *); int (*get_read_fd)(struct object *);
/* return a Unix fd that can be used to write to the object */ /* return a Unix fd that can be used to write to the object */
...@@ -55,7 +59,9 @@ struct object_ops ...@@ -55,7 +59,9 @@ struct object_ops
struct object struct object
{ {
unsigned int refcount; unsigned int refcount; /* reference count */
int fd; /* file descriptor */
int select; /* select() user id */
const struct object_ops *ops; const struct object_ops *ops;
struct wait_queue_entry *head; struct wait_queue_entry *head;
struct wait_queue_entry *tail; struct wait_queue_entry *tail;
...@@ -68,7 +74,7 @@ struct object ...@@ -68,7 +74,7 @@ struct object
extern void *mem_alloc( size_t size ); /* malloc wrapper */ extern void *mem_alloc( size_t size ); /* malloc wrapper */
extern void *memdup( const void *data, size_t len ); extern void *memdup( const void *data, size_t len );
extern void *alloc_object( const struct object_ops *ops ); extern void *alloc_object( const struct object_ops *ops, int fd );
extern void dump_object_name( struct object *obj ); extern void dump_object_name( struct object *obj );
extern void *create_named_object( const struct object_ops *ops, const WCHAR *name, size_t len ); extern void *create_named_object( const struct object_ops *ops, const WCHAR *name, size_t len );
/* grab/release_object can take any pointer, but you better make sure */ /* grab/release_object can take any pointer, but you better make sure */
...@@ -83,17 +89,20 @@ extern int no_write_fd( struct object *obj ); ...@@ -83,17 +89,20 @@ extern int no_write_fd( struct object *obj );
extern int no_flush( struct object *obj ); extern int no_flush( struct object *obj );
extern int no_get_file_info( struct object *obj, struct get_file_info_request *info ); extern int no_get_file_info( struct object *obj, struct get_file_info_request *info );
extern void no_destroy( struct object *obj ); extern void no_destroy( struct object *obj );
extern void default_select_event( int event, void *private ); extern int default_poll_add_queue( struct object *obj, struct wait_queue_entry *entry );
extern void default_poll_remove_queue( struct object *obj, struct wait_queue_entry *entry );
extern int default_poll_signaled( struct object *obj, struct thread *thread );
extern void default_poll_event( struct object *obj, int event );
#ifdef DEBUG_OBJECTS #ifdef DEBUG_OBJECTS
extern void dump_objects(void); extern void dump_objects(void);
#endif #endif
/* select functions */ /* select functions */
extern int add_select_user( int fd, void (*func)(int, void *), void *private ); extern int add_select_user( struct object *obj );
extern void remove_select_user( int user ); extern void remove_select_user( struct object *obj );
extern void change_select_fd( int user, int fd ); extern void change_select_fd( struct object *obj, int fd );
extern void set_select_events( int user, int events ); extern void set_select_events( struct object *obj, int events );
extern int check_select_events( int fd, int events ); extern int check_select_events( int fd, int events );
extern void select_loop(void); extern void select_loop(void);
...@@ -114,15 +123,6 @@ static inline int time_before( struct timeval *t1, struct timeval *t2 ) ...@@ -114,15 +123,6 @@ static inline int time_before( struct timeval *t1, struct timeval *t2 )
((t1->tv_sec == t2->tv_sec) && (t1->tv_usec < t2->tv_usec))); ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec < t2->tv_usec)));
} }
/* socket functions */
struct client;
extern struct client *add_client( int client_fd, struct thread *self );
extern void remove_client( struct client *client, int exit_code );
extern void client_pass_fd( struct client *client, int pass_fd );
extern void client_reply( struct client *client, unsigned int res );
/* event functions */ /* event functions */
struct event; struct event;
......
...@@ -33,15 +33,11 @@ struct pipe ...@@ -33,15 +33,11 @@ struct pipe
{ {
struct object obj; /* object header */ struct object obj; /* object header */
struct pipe *other; /* the pipe other end */ struct pipe *other; /* the pipe other end */
int fd; /* file descriptor */
int select; /* select user id */
enum side side; /* which side of the pipe is this */ enum side side; /* which side of the pipe is this */
}; };
static void pipe_dump( struct object *obj, int verbose ); static void pipe_dump( struct object *obj, int verbose );
static int pipe_add_queue( struct object *obj, struct wait_queue_entry *entry ); static int pipe_get_poll_events( struct object *obj );
static void pipe_remove_queue( struct object *obj, struct wait_queue_entry *entry );
static int pipe_signaled( struct object *obj, struct thread *thread );
static int pipe_get_read_fd( struct object *obj ); static int pipe_get_read_fd( struct object *obj );
static int pipe_get_write_fd( struct object *obj ); static int pipe_get_write_fd( struct object *obj );
static int pipe_get_info( struct object *obj, struct get_file_info_request *req ); static int pipe_get_info( struct object *obj, struct get_file_info_request *req );
...@@ -49,17 +45,19 @@ static void pipe_destroy( struct object *obj ); ...@@ -49,17 +45,19 @@ static void pipe_destroy( struct object *obj );
static const struct object_ops pipe_ops = static const struct object_ops pipe_ops =
{ {
sizeof(struct pipe), sizeof(struct pipe), /* size */
pipe_dump, pipe_dump, /* dump */
pipe_add_queue, default_poll_add_queue, /* add_queue */
pipe_remove_queue, default_poll_remove_queue, /* remove_queue */
pipe_signaled, default_poll_signaled, /* signaled */
no_satisfied, no_satisfied, /* satisfied */
pipe_get_read_fd, pipe_get_poll_events, /* get_poll_events */
pipe_get_write_fd, default_poll_event, /* poll_event */
no_flush, pipe_get_read_fd, /* get_read_fd */
pipe_get_info, pipe_get_write_fd, /* get_write_fd */
pipe_destroy no_flush, /* flush */
pipe_get_info, /* get_file_info */
pipe_destroy /* destroy */
}; };
...@@ -67,16 +65,10 @@ static struct pipe *create_pipe_side( int fd, int side ) ...@@ -67,16 +65,10 @@ static struct pipe *create_pipe_side( int fd, int side )
{ {
struct pipe *pipe; struct pipe *pipe;
if ((pipe = alloc_object( &pipe_ops ))) if ((pipe = alloc_object( &pipe_ops, fd )))
{ {
pipe->fd = fd;
pipe->other = NULL; pipe->other = NULL;
pipe->side = side; pipe->side = side;
if ((pipe->select = add_select_user( fd, default_select_event, pipe )) == -1)
{
release_object( pipe );
pipe = NULL;
}
} }
return pipe; return pipe;
} }
...@@ -104,8 +96,7 @@ static int create_pipe( struct object *obj[2] ) ...@@ -104,8 +96,7 @@ static int create_pipe( struct object *obj[2] )
} }
release_object( read_pipe ); release_object( read_pipe );
} }
close( fd[0] ); else close( fd[1] );
close( fd[1] );
return 0; return 0;
} }
...@@ -114,49 +105,14 @@ static void pipe_dump( struct object *obj, int verbose ) ...@@ -114,49 +105,14 @@ static void pipe_dump( struct object *obj, int verbose )
struct pipe *pipe = (struct pipe *)obj; struct pipe *pipe = (struct pipe *)obj;
assert( obj->ops == &pipe_ops ); assert( obj->ops == &pipe_ops );
fprintf( stderr, "Pipe %s-side fd=%d\n", fprintf( stderr, "Pipe %s-side fd=%d\n",
(pipe->side == READ_SIDE) ? "read" : "write", pipe->fd ); (pipe->side == READ_SIDE) ? "read" : "write", pipe->obj.fd );
} }
static int pipe_add_queue( struct object *obj, struct wait_queue_entry *entry ) static int pipe_get_poll_events( struct object *obj )
{ {
struct pipe *pipe = (struct pipe *)obj; struct pipe *pipe = (struct pipe *)obj;
assert( obj->ops == &pipe_ops ); assert( obj->ops == &pipe_ops );
if (!obj->head) /* first on the queue */ return (pipe->side == READ_SIDE) ? POLLIN : POLLOUT;
set_select_events( pipe->select, (pipe->side == READ_SIDE) ? POLLIN : POLLOUT );
add_queue( obj, entry );
return 1;
}
static void pipe_remove_queue( struct object *obj, struct wait_queue_entry *entry )
{
struct pipe *pipe = (struct pipe *)grab_object(obj);
assert( obj->ops == &pipe_ops );
remove_queue( obj, entry );
if (!obj->head) /* last on the queue is gone */
set_select_events( pipe->select, 0 );
release_object( obj );
}
static int pipe_signaled( struct object *obj, struct thread *thread )
{
int event;
struct pipe *pipe = (struct pipe *)obj;
assert( obj->ops == &pipe_ops );
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 );
return 1;
}
else
{
/* restart waiting on select() if we are no longer signaled */
if (obj->head) set_select_events( pipe->select, event );
return 0;
}
} }
static int pipe_get_read_fd( struct object *obj ) static int pipe_get_read_fd( struct object *obj )
...@@ -174,7 +130,7 @@ static int pipe_get_read_fd( struct object *obj ) ...@@ -174,7 +130,7 @@ static int pipe_get_read_fd( struct object *obj )
set_error( ERROR_ACCESS_DENIED ); set_error( ERROR_ACCESS_DENIED );
return -1; return -1;
} }
return dup( pipe->fd ); return dup( pipe->obj.fd );
} }
static int pipe_get_write_fd( struct object *obj ) static int pipe_get_write_fd( struct object *obj )
...@@ -192,7 +148,7 @@ static int pipe_get_write_fd( struct object *obj ) ...@@ -192,7 +148,7 @@ static int pipe_get_write_fd( struct object *obj )
set_error( ERROR_ACCESS_DENIED ); set_error( ERROR_ACCESS_DENIED );
return -1; return -1;
} }
return dup( pipe->fd ); return dup( pipe->obj.fd );
} }
static int pipe_get_info( struct object *obj, struct get_file_info_request *req ) static int pipe_get_info( struct object *obj, struct get_file_info_request *req )
...@@ -216,7 +172,6 @@ static void pipe_destroy( struct object *obj ) ...@@ -216,7 +172,6 @@ static void pipe_destroy( struct object *obj )
assert( obj->ops == &pipe_ops ); assert( obj->ops == &pipe_ops );
if (pipe->other) pipe->other->other = NULL; if (pipe->other) pipe->other->other = NULL;
remove_select_user( pipe->select );
} }
/* create an anonymous pipe */ /* create an anonymous pipe */
......
...@@ -36,17 +36,19 @@ static void process_destroy( struct object *obj ); ...@@ -36,17 +36,19 @@ static void process_destroy( struct object *obj );
static const struct object_ops process_ops = static const struct object_ops process_ops =
{ {
sizeof(struct process), sizeof(struct process), /* size */
process_dump, process_dump, /* dump */
add_queue, add_queue, /* add_queue */
remove_queue, remove_queue, /* remove_queue */
process_signaled, process_signaled, /* signaled */
no_satisfied, no_satisfied, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
process_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
process_destroy /* destroy */
}; };
...@@ -56,7 +58,7 @@ static struct process *create_process( struct process *parent, struct new_proces ...@@ -56,7 +58,7 @@ static struct process *create_process( struct process *parent, struct new_proces
{ {
struct process *process; struct process *process;
if (!(process = alloc_object( &process_ops ))) return NULL; if (!(process = alloc_object( &process_ops, -1 ))) return NULL;
process->next = NULL; process->next = NULL;
process->prev = NULL; process->prev = NULL;
process->thread_list = NULL; process->thread_list = NULL;
......
...@@ -106,17 +106,19 @@ static void key_destroy( struct object *obj ); ...@@ -106,17 +106,19 @@ static void key_destroy( struct object *obj );
static const struct object_ops key_ops = static const struct object_ops key_ops =
{ {
sizeof(struct key), sizeof(struct key), /* size */
key_dump, key_dump, /* dump */
no_add_queue, no_add_queue, /* add_queue */
NULL, /* should never get called */ NULL, /* remove_queue */
NULL, /* should never get called */ NULL, /* signaled */
NULL, /* should never get called */ NULL, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
key_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
key_destroy /* destroy */
}; };
...@@ -322,7 +324,7 @@ static WCHAR *req_strdupW( const WCHAR *str ) ...@@ -322,7 +324,7 @@ static WCHAR *req_strdupW( const WCHAR *str )
static struct key *alloc_key( const WCHAR *name, time_t modif ) static struct key *alloc_key( const WCHAR *name, time_t modif )
{ {
struct key *key; struct key *key;
if ((key = (struct key *)alloc_object( &key_ops ))) if ((key = (struct key *)alloc_object( &key_ops, -1 )))
{ {
key->name = NULL; key->name = NULL;
key->class = NULL; key->class = NULL;
......
...@@ -4,13 +4,20 @@ ...@@ -4,13 +4,20 @@
* Copyright (C) 1998 Alexandre Julliard * Copyright (C) 1998 Alexandre Julliard
*/ */
#include "config.h"
#include <assert.h> #include <assert.h>
#include <stdarg.h> #include <errno.h>
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <signal.h> #include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#include <sys/uio.h> #include <sys/uio.h>
#include <unistd.h> #include <unistd.h>
...@@ -22,9 +29,30 @@ ...@@ -22,9 +29,30 @@
#include "server.h" #include "server.h"
#define WANT_REQUEST_HANDLERS #define WANT_REQUEST_HANDLERS
#include "request.h" #include "request.h"
/* Some versions of glibc don't define this */
#ifndef SCM_RIGHTS
#define SCM_RIGHTS 1
#endif
struct thread *current = NULL; /* thread handling the current request */ struct thread *current = NULL; /* thread handling the current request */
/* socket communication static structures */
static struct iovec myiovec;
static struct msghdr msghdr = { NULL, 0, &myiovec, 1, };
#ifndef HAVE_MSGHDR_ACCRIGHTS
struct cmsg_fd
{
int len; /* sizeof structure */
int level; /* SOL_SOCKET */
int type; /* SCM_RIGHTS */
int fd; /* fd to pass */
};
static struct cmsg_fd cmsg = { sizeof(cmsg), SOL_SOCKET, SCM_RIGHTS, -1 };
#endif /* HAVE_MSGHDR_ACCRIGHTS */
/* complain about a protocol error and terminate the client connection */ /* complain about a protocol error and terminate the client connection */
void fatal_protocol_error( struct thread *thread, const char *err, ... ) void fatal_protocol_error( struct thread *thread, const char *err, ... )
{ {
...@@ -34,11 +62,11 @@ void fatal_protocol_error( struct thread *thread, const char *err, ... ) ...@@ -34,11 +62,11 @@ void fatal_protocol_error( struct thread *thread, const char *err, ... )
fprintf( stderr, "Protocol error:%p: ", thread ); fprintf( stderr, "Protocol error:%p: ", thread );
vfprintf( stderr, err, args ); vfprintf( stderr, err, args );
va_end( args ); va_end( args );
remove_client( thread->client, PROTOCOL_ERROR ); kill_thread( thread, PROTOCOL_ERROR );
} }
/* call a request handler */ /* call a request handler */
void call_req_handler( struct thread *thread, enum request req, int fd ) static void call_req_handler( struct thread *thread, enum request req, int fd )
{ {
current = thread; current = thread;
clear_error(); clear_error();
...@@ -65,31 +93,109 @@ void call_timeout_handler( void *thread ) ...@@ -65,31 +93,109 @@ void call_timeout_handler( void *thread )
current = NULL; current = NULL;
} }
/* a thread has been killed */
void call_kill_handler( struct thread *thread, int exit_code )
{
/* must be reentrant WRT call_req_handler */
struct thread *old_current = current;
current = thread;
if (current)
{
if (debug_level) trace_kill( exit_code );
thread_killed( current, exit_code );
}
current = (old_current != thread) ? old_current : NULL;
}
/* set the fd to pass to the thread */ /* set the fd to pass to the thread */
void set_reply_fd( struct thread *thread, int pass_fd ) void set_reply_fd( struct thread *thread, int pass_fd )
{ {
client_pass_fd( thread->client, pass_fd ); assert( thread->pass_fd == -1 );
thread->pass_fd = pass_fd;
} }
/* send a reply to a thread */ /* send a reply to a thread */
void send_reply( struct thread *thread ) void send_reply( struct thread *thread )
{ {
if (thread->state == SLEEPING) thread->state = RUNNING; if (thread->state == SLEEPING) thread->state = RUNNING;
client_reply( thread->client, thread->error ); if (debug_level) trace_reply( thread );
if (!write_request( thread )) set_select_events( &thread->obj, POLLOUT );
}
/* read a message from a client that has something to say */
void read_request( struct thread *thread )
{
int ret;
enum request req;
#ifdef HAVE_MSGHDR_ACCRIGHTS
msghdr.msg_accrightslen = sizeof(int);
msghdr.msg_accrights = (void *)&thread->pass_fd;
#else /* HAVE_MSGHDR_ACCRIGHTS */
msghdr.msg_control = &cmsg;
msghdr.msg_controllen = sizeof(cmsg);
cmsg.fd = -1;
#endif /* HAVE_MSGHDR_ACCRIGHTS */
assert( thread->pass_fd == -1 );
myiovec.iov_base = (void *)&req;
myiovec.iov_len = sizeof(req);
ret = recvmsg( thread->obj.fd, &msghdr, 0 );
#ifndef HAVE_MSGHDR_ACCRIGHTS
thread->pass_fd = cmsg.fd;
#endif
if (ret == sizeof(req))
{
int pass_fd = thread->pass_fd;
thread->pass_fd = -1;
call_req_handler( thread, req, pass_fd );
if (pass_fd != -1) close( pass_fd );
return;
}
if (ret == -1)
{
perror("recvmsg");
kill_thread( thread, BROKEN_PIPE );
return;
}
if (!ret) /* closed pipe */
{
kill_thread( thread, BROKEN_PIPE );
return;
}
fatal_protocol_error( thread, "partial message received %d/%d\n", ret, sizeof(req) );
}
/* send a message to a client that is ready to receive something */
int write_request( struct thread *thread )
{
int ret;
if (thread->pass_fd == -1)
{
ret = write( thread->obj.fd, &thread->error, sizeof(thread->error) );
if (ret == sizeof(thread->error)) goto ok;
}
else /* we have an fd to send */
{
#ifdef HAVE_MSGHDR_ACCRIGHTS
msghdr.msg_accrightslen = sizeof(int);
msghdr.msg_accrights = (void *)&thread->pass_fd;
#else /* HAVE_MSGHDR_ACCRIGHTS */
msghdr.msg_control = &cmsg;
msghdr.msg_controllen = sizeof(cmsg);
cmsg.fd = thread->pass_fd;
#endif /* HAVE_MSGHDR_ACCRIGHTS */
myiovec.iov_base = (void *)&thread->error;
myiovec.iov_len = sizeof(thread->error);
ret = sendmsg( thread->obj.fd, &msghdr, 0 );
close( thread->pass_fd );
thread->pass_fd = -1;
if (ret == sizeof(thread->error)) goto ok;
}
if (ret == -1)
{
if (errno == EWOULDBLOCK) return 0; /* not a fatal error */
if (errno != EPIPE) perror("sendmsg");
}
else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(thread->error) );
kill_thread( thread, BROKEN_PIPE );
return -1;
ok:
set_select_events( &thread->obj, POLLIN );
return 1;
} }
/* set the debug level */ /* set the debug level */
......
...@@ -26,17 +26,17 @@ ...@@ -26,17 +26,17 @@
/* request functions */ /* request functions */
extern void read_request( struct thread *thread );
extern int write_request( struct thread *thread );
extern void fatal_protocol_error( struct thread *thread, const char *err, ... ); extern void fatal_protocol_error( struct thread *thread, const char *err, ... );
extern void call_req_handler( struct thread *thread, enum request req, int fd );
extern void call_timeout_handler( void *thread ); extern void call_timeout_handler( void *thread );
extern void call_kill_handler( struct thread *thread, int exit_code );
extern void set_reply_fd( struct thread *thread, int pass_fd ); extern void set_reply_fd( struct thread *thread, int pass_fd );
extern void send_reply( struct thread *thread ); extern void send_reply( struct thread *thread );
extern void trace_request( enum request req, int fd ); extern void trace_request( enum request req, int fd );
extern void trace_timeout(void); extern void trace_timeout(void);
extern void trace_kill( int exit_code ); extern void trace_kill( struct thread *thread );
extern void trace_reply( struct thread *thread, unsigned int res, int pass_fd ); extern void trace_reply( struct thread *thread );
/* get the request buffer */ /* get the request buffer */
static inline void *get_req_ptr( struct thread *thread ) static inline void *get_req_ptr( struct thread *thread )
......
...@@ -19,12 +19,6 @@ ...@@ -19,12 +19,6 @@
#include "thread.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
{ {
struct timeout_user *next; /* next in sorted timeout list */ struct timeout_user *next; /* next in sorted timeout list */
...@@ -34,32 +28,31 @@ struct timeout_user ...@@ -34,32 +28,31 @@ struct timeout_user
void *private; /* callback private data */ void *private; /* callback private data */
}; };
static struct poll_user *poll_users; /* users array */ static struct object **poll_users; /* users array */
static struct pollfd *pollfd; /* poll fd array */ static struct pollfd *pollfd; /* poll fd array */
static int nb_users; /* count of array entries actually in use */ static int nb_users; /* count of array entries actually in use */
static int active_users; /* current number of active users */ static int active_users; /* current number of active users */
static int allocated_users; /* count of allocated entries in the array */ 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 object **freelist; /* list of free entries in the array */
static struct timeout_user *timeout_head; /* sorted timeouts list head */ static struct timeout_user *timeout_head; /* sorted timeouts list head */
static struct timeout_user *timeout_tail; /* sorted timeouts list tail */ static struct timeout_user *timeout_tail; /* sorted timeouts list tail */
/* add a user and return an opaque handle to it, or -1 on failure */ /* 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 ) int add_select_user( struct object *obj )
{ {
int ret; int ret;
if (freelist) if (freelist)
{ {
ret = freelist - poll_users; ret = freelist - poll_users;
freelist = poll_users[ret].private; freelist = (struct object **)poll_users[ret];
assert( !poll_users[ret].func );
} }
else else
{ {
if (nb_users == allocated_users) if (nb_users == allocated_users)
{ {
struct poll_user *newusers; struct object **newusers;
struct pollfd *newpoll; struct pollfd *newpoll;
int new_count = allocated_users ? (allocated_users + allocated_users / 2) : 16; int new_count = allocated_users ? (allocated_users + allocated_users / 2) : 16;
if (!(newusers = realloc( poll_users, new_count * sizeof(*poll_users) ))) return -1; if (!(newusers = realloc( poll_users, new_count * sizeof(*poll_users) ))) return -1;
...@@ -74,43 +67,53 @@ int add_select_user( int fd, void (*func)(int, void *), void *private ) ...@@ -74,43 +67,53 @@ int add_select_user( int fd, void (*func)(int, void *), void *private )
} }
ret = nb_users++; ret = nb_users++;
} }
pollfd[ret].fd = fd; pollfd[ret].fd = obj->fd;
pollfd[ret].events = 0; pollfd[ret].events = 0;
pollfd[ret].revents = 0; pollfd[ret].revents = 0;
poll_users[ret].func = func; poll_users[ret] = obj;
poll_users[ret].private = private; obj->select = ret;
active_users++; active_users++;
return ret; return ret;
} }
/* remove a user and close its fd */ /* remove an object from the select list and close its fd */
void remove_select_user( int user ) void remove_select_user( struct object *obj )
{ {
if (user == -1) return; /* avoids checking in all callers */ int user = obj->select;
assert( poll_users[user].func ); assert( poll_users[user] == obj );
close( pollfd[user].fd );
pollfd[user].fd = -1; pollfd[user].fd = -1;
pollfd[user].events = 0; pollfd[user].events = 0;
pollfd[user].revents = 0; pollfd[user].revents = 0;
poll_users[user].func = NULL; poll_users[user] = (struct object *)freelist;
poll_users[user].private = freelist;
freelist = &poll_users[user]; freelist = &poll_users[user];
close( obj->fd );
obj->fd = -1;
obj->select = -1;
active_users--; active_users--;
} }
/* change the fd of a select user (the old fd is closed) */ /* change the fd of an object (the old fd is closed) */
void change_select_fd( int user, int fd ) void change_select_fd( struct object *obj, int fd )
{ {
assert( poll_users[user].func ); int user = obj->select;
close( pollfd[user].fd ); assert( poll_users[user] == obj );
pollfd[user].fd = fd; pollfd[user].fd = fd;
close( obj->fd );
obj->fd = fd;
} }
/* set the events that select waits for on this fd */ /* set the events that select waits for on this fd */
void set_select_events( int user, int events ) void set_select_events( struct object *obj, int events )
{ {
assert( poll_users[user].func ); int user = obj->select;
pollfd[user].events = events; assert( poll_users[user] == obj );
if (events == -1) /* stop waiting on this fd completely */
{
pollfd[user].fd = -1;
pollfd[user].events = 0;
pollfd[user].revents = 0;
}
else if (pollfd[user].fd != -1) pollfd[user].events = events;
} }
/* check if events are pending */ /* check if events are pending */
...@@ -259,7 +262,7 @@ void select_loop(void) ...@@ -259,7 +262,7 @@ void select_loop(void)
{ {
if (pollfd[i].revents) if (pollfd[i].revents)
{ {
poll_users[i].func( pollfd[i].revents, poll_users[i].private ); poll_users[i]->ops->poll_event( poll_users[i], pollfd[i].revents );
if (!--ret) break; if (!--ret) break;
} }
} }
......
...@@ -28,17 +28,19 @@ static int semaphore_satisfied( struct object *obj, struct thread *thread ); ...@@ -28,17 +28,19 @@ static int semaphore_satisfied( struct object *obj, struct thread *thread );
static const struct object_ops semaphore_ops = static const struct object_ops semaphore_ops =
{ {
sizeof(struct semaphore), sizeof(struct semaphore), /* size */
semaphore_dump, semaphore_dump, /* dump */
add_queue, add_queue, /* add_queue */
remove_queue, remove_queue, /* remove_queue */
semaphore_signaled, semaphore_signaled, /* signaled */
semaphore_satisfied, semaphore_satisfied, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
no_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
no_destroy /* destroy */
}; };
......
...@@ -33,17 +33,19 @@ static void snapshot_destroy( struct object *obj ); ...@@ -33,17 +33,19 @@ static void snapshot_destroy( struct object *obj );
static const struct object_ops snapshot_ops = static const struct object_ops snapshot_ops =
{ {
sizeof(struct snapshot), sizeof(struct snapshot), /* size */
snapshot_dump, snapshot_dump, /* dump */
no_add_queue, no_add_queue, /* add_queue */
NULL, /* should never get called */ NULL, /* remove_queue */
NULL, /* should never get called */ NULL, /* signaled */
NULL, /* should never get called */ NULL, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
snapshot_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
snapshot_destroy /* destroy */
}; };
...@@ -52,7 +54,7 @@ static struct snapshot *create_snapshot( int flags ) ...@@ -52,7 +54,7 @@ static struct snapshot *create_snapshot( int flags )
{ {
struct snapshot *snapshot; struct snapshot *snapshot;
if ((snapshot = alloc_object( &snapshot_ops ))) if ((snapshot = alloc_object( &snapshot_ops, -1 )))
{ {
if (flags & TH32CS_SNAPPROCESS) if (flags & TH32CS_SNAPPROCESS)
snapshot->process = process_snap( &snapshot->process_count ); snapshot->process = process_snap( &snapshot->process_count );
......
/*
* Server-side socket communication functions
*
* Copyright (C) 1998 Alexandre Julliard
*/
#include "config.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#include <sys/uio.h>
#include <unistd.h>
#include "object.h"
#include "request.h"
/* Some versions of glibc don't define this */
#ifndef SCM_RIGHTS
#define SCM_RIGHTS 1
#endif
/* client structure */
struct client
{
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) */
struct timeout_user *timeout; /* current timeout (opaque pointer) */
};
/* socket communication static structures */
static struct iovec myiovec;
static struct msghdr msghdr = { NULL, 0, &myiovec, 1, };
#ifndef HAVE_MSGHDR_ACCRIGHTS
struct cmsg_fd
{
int len; /* sizeof structure */
int level; /* SOL_SOCKET */
int type; /* SCM_RIGHTS */
int fd; /* fd to pass */
};
static struct cmsg_fd cmsg = { sizeof(cmsg), SOL_SOCKET, SCM_RIGHTS, -1 };
#endif /* HAVE_MSGHDR_ACCRIGHTS */
/* send a message to a client that is ready to receive something */
static int do_write( struct client *client )
{
int ret;
if (client->pass_fd == -1)
{
ret = write( client->fd, &client->res, sizeof(client->res) );
if (ret == sizeof(client->res)) goto ok;
}
else /* we have an fd to send */
{
#ifdef HAVE_MSGHDR_ACCRIGHTS
msghdr.msg_accrightslen = sizeof(int);
msghdr.msg_accrights = (void *)&client->pass_fd;
#else /* HAVE_MSGHDR_ACCRIGHTS */
msghdr.msg_control = &cmsg;
msghdr.msg_controllen = sizeof(cmsg);
cmsg.fd = client->pass_fd;
#endif /* HAVE_MSGHDR_ACCRIGHTS */
myiovec.iov_base = (void *)&client->res;
myiovec.iov_len = sizeof(client->res);
ret = sendmsg( client->fd, &msghdr, 0 );
close( client->pass_fd );
client->pass_fd = -1;
if (ret == sizeof(client->res)) goto ok;
}
if (ret == -1)
{
if (errno == EWOULDBLOCK) return 0; /* not a fatal error */
if (errno != EPIPE) perror("sendmsg");
}
else fprintf( stderr, "Partial message sent %d/%d\n", ret, sizeof(client->res) );
remove_client( client, BROKEN_PIPE );
return -1;
ok:
set_select_events( client->select, POLLIN );
return 1;
}
/* read a message from a client that has something to say */
static void do_read( struct client *client )
{
int ret;
enum request req;
#ifdef HAVE_MSGHDR_ACCRIGHTS
msghdr.msg_accrightslen = sizeof(int);
msghdr.msg_accrights = (void *)&client->pass_fd;
#else /* HAVE_MSGHDR_ACCRIGHTS */
msghdr.msg_control = &cmsg;
msghdr.msg_controllen = sizeof(cmsg);
cmsg.fd = -1;
#endif /* HAVE_MSGHDR_ACCRIGHTS */
assert( client->pass_fd == -1 );
myiovec.iov_base = (void *)&req;
myiovec.iov_len = sizeof(req);
ret = recvmsg( client->fd, &msghdr, 0 );
#ifndef HAVE_MSGHDR_ACCRIGHTS
client->pass_fd = cmsg.fd;
#endif
if (ret == sizeof(req))
{
int pass_fd = client->pass_fd;
client->pass_fd = -1;
call_req_handler( client->self, req, pass_fd );
if (pass_fd != -1) close( pass_fd );
return;
}
if (ret == -1)
{
perror("recvmsg");
remove_client( client, BROKEN_PIPE );
return;
}
if (!ret) /* closed pipe */
{
remove_client( client, BROKEN_PIPE );
return;
}
fatal_protocol_error( client->self, "partial message received %d/%d\n", ret, sizeof(req) );
}
/* handle a client event */
static void client_event( int event, void *private )
{
struct client *client = (struct client *)private;
if (event & (POLLERR | POLLHUP)) remove_client( client, BROKEN_PIPE );
else
{
if (event & POLLOUT) do_write( client );
if (event & POLLIN) do_read( client );
}
}
/*******************************************************************/
/* server-side exported functions */
/* add a client */
struct client *add_client( int fd, struct thread *self )
{
int flags;
struct client *client = mem_alloc( sizeof(*client) );
if (!client) return NULL;
flags = fcntl( fd, F_GETFL, 0 );
fcntl( fd, F_SETFL, flags | O_NONBLOCK );
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;
}
/* remove a client */
void remove_client( struct client *client, int exit_code )
{
assert( client );
call_kill_handler( client->self, exit_code );
if (client->timeout) remove_timeout_user( client->timeout );
remove_select_user( client->select );
/* Purge messages */
if (client->pass_fd != -1) close( client->pass_fd );
free( client );
}
/* set the fd to pass to the client */
void client_pass_fd( struct client *client, int pass_fd )
{
assert( client->pass_fd == -1 );
client->pass_fd = pass_fd;
}
/* send a reply to a client */
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, POLLOUT );
}
...@@ -63,21 +63,24 @@ struct thread_apc ...@@ -63,21 +63,24 @@ struct thread_apc
static void dump_thread( struct object *obj, int verbose ); static void dump_thread( struct object *obj, int verbose );
static int thread_signaled( struct object *obj, struct thread *thread ); static int thread_signaled( struct object *obj, struct thread *thread );
extern void thread_poll_event( struct object *obj, int event );
static void destroy_thread( struct object *obj ); static void destroy_thread( struct object *obj );
static const struct object_ops thread_ops = static const struct object_ops thread_ops =
{ {
sizeof(struct thread), sizeof(struct thread), /* size */
dump_thread, dump_thread, /* dump */
add_queue, add_queue, /* add_queue */
remove_queue, remove_queue, /* remove_queue */
thread_signaled, thread_signaled, /* signaled */
no_satisfied, no_satisfied, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, thread_poll_event, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
destroy_thread no_flush, /* flush */
no_get_file_info, /* get_file_info */
destroy_thread /* destroy */
}; };
static struct thread *first_thread; static struct thread *first_thread;
...@@ -105,9 +108,11 @@ static struct thread *create_thread( int fd, struct process *process, int suspen ...@@ -105,9 +108,11 @@ static struct thread *create_thread( int fd, struct process *process, int suspen
struct thread *thread; struct thread *thread;
int buf_fd; int buf_fd;
if (!(thread = alloc_object( &thread_ops ))) return NULL; int flags = fcntl( fd, F_GETFL, 0 );
fcntl( fd, F_SETFL, flags | O_NONBLOCK );
if (!(thread = alloc_object( &thread_ops, fd ))) return NULL;
thread->client = NULL;
thread->unix_pid = 0; /* not known yet */ thread->unix_pid = 0; /* not known yet */
thread->teb = NULL; thread->teb = NULL;
thread->mutex = NULL; thread->mutex = NULL;
...@@ -118,6 +123,7 @@ static struct thread *create_thread( int fd, struct process *process, int suspen ...@@ -118,6 +123,7 @@ static struct thread *create_thread( int fd, struct process *process, int suspen
thread->apc = NULL; thread->apc = NULL;
thread->apc_count = 0; thread->apc_count = 0;
thread->error = 0; thread->error = 0;
thread->pass_fd = -1;
thread->state = RUNNING; thread->state = RUNNING;
thread->attached = 0; thread->attached = 0;
thread->exit_code = 0x103; /* STILL_ACTIVE */ thread->exit_code = 0x103; /* STILL_ACTIVE */
...@@ -142,11 +148,8 @@ static struct thread *create_thread( int fd, struct process *process, int suspen ...@@ -142,11 +148,8 @@ static struct thread *create_thread( int fd, struct process *process, int suspen
add_process_thread( process, thread ); add_process_thread( process, thread );
if ((buf_fd = alloc_client_buffer( thread )) == -1) goto error; if ((buf_fd = alloc_client_buffer( thread )) == -1) goto error;
if (!(thread->client = add_client( fd, thread )))
{ set_select_events( &thread->obj, POLLIN ); /* start listening to events */
close( buf_fd );
goto error;
}
set_reply_fd( thread, buf_fd ); /* send the fd to the client */ set_reply_fd( thread, buf_fd ); /* send the fd to the client */
send_reply( thread ); send_reply( thread );
return thread; return thread;
...@@ -164,6 +167,20 @@ void create_initial_thread( int fd ) ...@@ -164,6 +167,20 @@ void create_initial_thread( int fd )
select_loop(); select_loop();
} }
/* handle a client event */
void thread_poll_event( struct object *obj, int event )
{
struct thread *thread = (struct thread *)obj;
assert( obj->ops == &thread_ops );
if (event & (POLLERR | POLLHUP)) kill_thread( thread, BROKEN_PIPE );
else
{
if (event & POLLOUT) write_request( thread );
if (event & POLLIN) read_request( thread );
}
}
/* destroy a thread when its refcount is 0 */ /* destroy a thread when its refcount is 0 */
static void destroy_thread( struct object *obj ) static void destroy_thread( struct object *obj )
{ {
...@@ -177,6 +194,7 @@ static void destroy_thread( struct object *obj ) ...@@ -177,6 +194,7 @@ static void destroy_thread( struct object *obj )
else first_thread = thread->next; else first_thread = thread->next;
if (thread->apc) free( thread->apc ); if (thread->apc) free( thread->apc );
if (thread->buffer != (void *)-1) munmap( thread->buffer, MAX_REQUEST_LENGTH ); if (thread->buffer != (void *)-1) munmap( thread->buffer, MAX_REQUEST_LENGTH );
if (thread->pass_fd != -1) close( thread->pass_fd );
} }
/* dump a thread on stdout for debugging purposes */ /* dump a thread on stdout for debugging purposes */
...@@ -499,21 +517,17 @@ static int thread_queue_apc( struct thread *thread, void *func, void *param ) ...@@ -499,21 +517,17 @@ static int thread_queue_apc( struct thread *thread, void *func, void *param )
void kill_thread( struct thread *thread, int exit_code ) void kill_thread( struct thread *thread, int exit_code )
{ {
if (thread->state == TERMINATED) return; /* already killed */ if (thread->state == TERMINATED) return; /* already killed */
remove_client( thread->client, exit_code ); /* this will call thread_killed */
}
/* a thread has been killed */
void thread_killed( struct thread *thread, int exit_code )
{
thread->state = TERMINATED; thread->state = TERMINATED;
thread->exit_code = exit_code; thread->exit_code = exit_code;
thread->client = NULL; if (current == thread) current = NULL;
if (debug_level) trace_kill( thread );
if (thread->wait) end_wait( thread ); if (thread->wait) end_wait( thread );
debug_exit_thread( thread, exit_code ); debug_exit_thread( thread, exit_code );
abandon_mutexes( thread ); abandon_mutexes( thread );
remove_process_thread( thread->process, thread ); remove_process_thread( thread->process, thread );
wake_up( &thread->obj, 0 ); wake_up( &thread->obj, 0 );
detach_thread( thread ); detach_thread( thread );
remove_select_user( &thread->obj );
release_object( thread ); release_object( thread );
} }
......
...@@ -46,10 +46,10 @@ struct thread ...@@ -46,10 +46,10 @@ struct thread
struct thread_apc *apc; /* list of async procedure calls */ struct thread_apc *apc; /* list of async procedure calls */
int apc_count; /* number of outstanding APCs */ int apc_count; /* number of outstanding APCs */
int error; /* current error code */ int error; /* current error code */
int pass_fd; /* fd to pass to the client */
enum run_state state; /* running state */ enum run_state state; /* running state */
int attached; /* is thread attached with ptrace? */ int attached; /* is thread attached with ptrace? */
int exit_code; /* thread exit code */ int exit_code; /* thread exit code */
struct client *client; /* client for socket communications */
int unix_pid; /* Unix pid of client */ int unix_pid; /* Unix pid of client */
void *teb; /* TEB address (in client address space) */ void *teb; /* TEB address (in client address space) */
int priority; /* priority level */ int priority; /* priority level */
...@@ -74,7 +74,6 @@ extern void resume_all_threads( void ); ...@@ -74,7 +74,6 @@ extern void resume_all_threads( void );
extern int add_queue( struct object *obj, struct wait_queue_entry *entry ); extern int add_queue( struct object *obj, struct wait_queue_entry *entry );
extern void remove_queue( struct object *obj, struct wait_queue_entry *entry ); extern void remove_queue( struct object *obj, struct wait_queue_entry *entry );
extern void kill_thread( struct thread *thread, int exit_code ); extern void kill_thread( struct thread *thread, int exit_code );
extern void thread_killed( struct thread *thread, int exit_code );
extern void thread_timeout(void); extern void thread_timeout(void);
extern void wake_up( struct object *obj, int max ); extern void wake_up( struct object *obj, int max );
......
...@@ -39,17 +39,19 @@ static void timer_destroy( struct object *obj ); ...@@ -39,17 +39,19 @@ static void timer_destroy( struct object *obj );
static const struct object_ops timer_ops = static const struct object_ops timer_ops =
{ {
sizeof(struct timer), sizeof(struct timer), /* size */
timer_dump, timer_dump, /* dump */
add_queue, add_queue, /* add_queue */
remove_queue, remove_queue, /* remove_queue */
timer_signaled, timer_signaled, /* signaled */
timer_satisfied, timer_satisfied, /* satisfied */
no_read_fd, NULL, /* get_poll_events */
no_write_fd, NULL, /* poll_event */
no_flush, no_read_fd, /* get_read_fd */
no_get_file_info, no_write_fd, /* get_write_fd */
timer_destroy no_flush, /* flush */
no_get_file_info, /* get_file_info */
timer_destroy /* destroy */
}; };
......
...@@ -1341,22 +1341,22 @@ void trace_timeout(void) ...@@ -1341,22 +1341,22 @@ void trace_timeout(void)
fprintf( stderr, "%08x: *timeout*\n", (unsigned int)current ); fprintf( stderr, "%08x: *timeout*\n", (unsigned int)current );
} }
void trace_kill( int exit_code ) void trace_kill( struct thread *thread )
{ {
fprintf( stderr,"%08x: *killed* exit_code=%d\n", fprintf( stderr,"%08x: *killed* exit_code=%d\n",
(unsigned int)current, exit_code ); (unsigned int)thread, thread->exit_code );
} }
void trace_reply( struct thread *thread, unsigned int res, int pass_fd ) void trace_reply( struct thread *thread )
{ {
fprintf( stderr, "%08x: %s() = %d", fprintf( stderr, "%08x: %s() = %d",
(unsigned int)thread, req_names[thread->last_req], res ); (unsigned int)thread, req_names[thread->last_req], thread->error );
if (reply_dumpers[thread->last_req]) if (reply_dumpers[thread->last_req])
{ {
fprintf( stderr, " {" ); fprintf( stderr, " {" );
reply_dumpers[thread->last_req]( thread->buffer ); reply_dumpers[thread->last_req]( thread->buffer );
fprintf( stderr, " }" ); fprintf( stderr, " }" );
} }
if (pass_fd != -1) fprintf( stderr, " fd=%d\n", pass_fd ); if (thread->pass_fd != -1) fprintf( stderr, " fd=%d\n", thread->pass_fd );
else fprintf( stderr, "\n" ); else fprintf( stderr, "\n" );
} }
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