- Add a default asynchronous I/O implementation.
- Make file objects use it.
This commit is contained in:
parent
09d5cc2363
commit
d6a4e34a60
72
server/fd.c
72
server/fd.c
|
@ -146,6 +146,8 @@ struct fd
|
|||
int unix_fd; /* unix file descriptor */
|
||||
int fs_locks; /* can we use filesystem locks for this fd? */
|
||||
int poll_index; /* index of fd in poll array */
|
||||
struct list read_q; /* async readers of this fd */
|
||||
struct list write_q; /* async writers of this fd */
|
||||
};
|
||||
|
||||
static void fd_dump( struct object *obj, int verbose );
|
||||
|
@ -1073,6 +1075,9 @@ static void fd_destroy( struct object *obj )
|
|||
{
|
||||
struct fd *fd = (struct fd *)obj;
|
||||
|
||||
async_terminate_queue( &fd->read_q, STATUS_CANCELLED );
|
||||
async_terminate_queue( &fd->write_q, STATUS_CANCELLED );
|
||||
|
||||
remove_fd_locks( fd );
|
||||
list_remove( &fd->inode_entry );
|
||||
if (fd->poll_index != -1) remove_poll_user( fd, fd->poll_index );
|
||||
|
@ -1126,6 +1131,8 @@ struct fd *alloc_fd( const struct fd_ops *fd_user_ops, struct object *user )
|
|||
fd->poll_index = -1;
|
||||
list_init( &fd->inode_entry );
|
||||
list_init( &fd->locks );
|
||||
list_init( &fd->read_q );
|
||||
list_init( &fd->write_q );
|
||||
|
||||
if ((fd->poll_index = add_poll_user( fd )) == -1)
|
||||
{
|
||||
|
@ -1370,14 +1377,77 @@ int default_fd_signaled( struct object *obj, struct thread *thread )
|
|||
return ret;
|
||||
}
|
||||
|
||||
int default_fd_get_poll_events( struct fd *fd )
|
||||
{
|
||||
int events = 0;
|
||||
|
||||
if( !list_empty( &fd->read_q ))
|
||||
events |= POLLIN;
|
||||
if( !list_empty( &fd->write_q ))
|
||||
events |= POLLOUT;
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
/* default handler for poll() events */
|
||||
void default_poll_event( struct fd *fd, int event )
|
||||
{
|
||||
/* an error occurred, stop polling this fd to avoid busy-looping */
|
||||
if (!list_empty( &fd->read_q ) && (POLLIN & event) )
|
||||
{
|
||||
async_terminate_head( &fd->read_q, STATUS_ALERTED );
|
||||
return;
|
||||
}
|
||||
if (!list_empty( &fd->write_q ) && (POLLOUT & event) )
|
||||
{
|
||||
async_terminate_head( &fd->write_q, STATUS_ALERTED );
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an error occurred, stop polling this fd to avoid busy-looping */
|
||||
if (event & (POLLERR | POLLHUP)) set_fd_events( fd, -1 );
|
||||
wake_up( fd->user, 0 );
|
||||
}
|
||||
|
||||
void default_fd_queue_async( struct fd *fd, void *apc, void *user, void *io_sb, int type, int count )
|
||||
{
|
||||
struct list *queue;
|
||||
int events;
|
||||
|
||||
if (!(fd->fd_ops->get_file_info( fd ) & FD_FLAG_OVERLAPPED))
|
||||
{
|
||||
set_error( STATUS_INVALID_HANDLE );
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ASYNC_TYPE_READ:
|
||||
queue = &fd->read_q;
|
||||
break;
|
||||
case ASYNC_TYPE_WRITE:
|
||||
queue = &fd->write_q;
|
||||
break;
|
||||
default:
|
||||
set_error( STATUS_INVALID_PARAMETER );
|
||||
return;
|
||||
}
|
||||
|
||||
if (!create_async( current, NULL, queue, apc, user, io_sb ))
|
||||
return;
|
||||
|
||||
/* Check if the new pending request can be served immediately */
|
||||
events = check_fd_events( fd, fd->fd_ops->get_poll_events( fd ) );
|
||||
if (events) fd->fd_ops->poll_event( fd, events );
|
||||
|
||||
set_fd_events( fd, fd->fd_ops->get_poll_events( fd ) );
|
||||
}
|
||||
|
||||
void default_fd_cancel_async( struct fd *fd )
|
||||
{
|
||||
async_terminate_queue( &fd->read_q, STATUS_CANCELLED );
|
||||
async_terminate_queue( &fd->write_q, STATUS_CANCELLED );
|
||||
}
|
||||
|
||||
/* default flush() routine */
|
||||
int no_flush( struct fd *fd, struct event **event )
|
||||
{
|
||||
|
|
|
@ -60,8 +60,6 @@ struct file
|
|||
struct fd *fd; /* file descriptor for this file */
|
||||
unsigned int access; /* file access (GENERIC_READ/WRITE) */
|
||||
unsigned int options; /* file options (FILE_DELETE_ON_CLOSE, FILE_SYNCHRONOUS...) */
|
||||
struct list read_q;
|
||||
struct list write_q;
|
||||
};
|
||||
|
||||
static void file_dump( struct object *obj, int verbose );
|
||||
|
@ -69,11 +67,8 @@ static struct fd *file_get_fd( struct object *obj );
|
|||
static void file_destroy( struct object *obj );
|
||||
|
||||
static int file_get_poll_events( struct fd *fd );
|
||||
static void file_poll_event( struct fd *fd, int event );
|
||||
static int file_flush( struct fd *fd, struct event **event );
|
||||
static int file_get_info( struct fd *fd );
|
||||
static void file_queue_async( struct fd *fd, void *apc, void *user, void* iosb, int type, int count );
|
||||
static void file_cancel_async( struct fd *fd );
|
||||
|
||||
static const struct object_ops file_ops =
|
||||
{
|
||||
|
@ -91,11 +86,11 @@ static const struct object_ops file_ops =
|
|||
static const struct fd_ops file_fd_ops =
|
||||
{
|
||||
file_get_poll_events, /* get_poll_events */
|
||||
file_poll_event, /* poll_event */
|
||||
default_poll_event, /* poll_event */
|
||||
file_flush, /* flush */
|
||||
file_get_info, /* get_file_info */
|
||||
file_queue_async, /* queue_async */
|
||||
file_cancel_async /* cancel_async */
|
||||
default_fd_queue_async, /* queue_async */
|
||||
default_fd_cancel_async /* cancel_async */
|
||||
};
|
||||
|
||||
static inline int is_overlapped( const struct file *file )
|
||||
|
@ -164,8 +159,6 @@ static struct object *create_file( const char *nameptr, size_t len, unsigned int
|
|||
|
||||
file->access = access;
|
||||
file->options = options;
|
||||
list_init( &file->read_q );
|
||||
list_init( &file->write_q );
|
||||
|
||||
/* FIXME: should set error to STATUS_OBJECT_NAME_COLLISION if file existed before */
|
||||
if (!(file->fd = alloc_fd( &file_fd_ops, &file->obj )) ||
|
||||
|
@ -233,27 +226,6 @@ static int file_get_poll_events( struct fd *fd )
|
|||
return events;
|
||||
}
|
||||
|
||||
static void file_poll_event( struct fd *fd, int event )
|
||||
{
|
||||
struct file *file = get_fd_user( fd );
|
||||
assert( file->obj.ops == &file_ops );
|
||||
if (is_overlapped( file ))
|
||||
{
|
||||
if (!list_empty( &file->read_q ) && (POLLIN & event) )
|
||||
{
|
||||
async_terminate_head( &file->read_q, STATUS_ALERTED );
|
||||
return;
|
||||
}
|
||||
if (!list_empty( &file->write_q ) && (POLLOUT & event) )
|
||||
{
|
||||
async_terminate_head( &file->write_q, STATUS_ALERTED );
|
||||
return;
|
||||
}
|
||||
}
|
||||
default_poll_event( fd, event );
|
||||
}
|
||||
|
||||
|
||||
static int file_flush( struct fd *fd, struct event **event )
|
||||
{
|
||||
int ret = (fsync( get_unix_fd(fd) ) != -1);
|
||||
|
@ -269,53 +241,6 @@ static int file_get_info( struct fd *fd )
|
|||
else return 0;
|
||||
}
|
||||
|
||||
static void file_queue_async( struct fd *fd, void *apc, void *user, void *iosb,
|
||||
int type, int count )
|
||||
{
|
||||
struct file *file = get_fd_user( fd );
|
||||
struct list *queue;
|
||||
int events;
|
||||
|
||||
assert( file->obj.ops == &file_ops );
|
||||
|
||||
if (!is_overlapped( file ))
|
||||
{
|
||||
set_error( STATUS_INVALID_HANDLE );
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ASYNC_TYPE_READ:
|
||||
queue = &file->read_q;
|
||||
break;
|
||||
case ASYNC_TYPE_WRITE:
|
||||
queue = &file->write_q;
|
||||
break;
|
||||
default:
|
||||
set_error( STATUS_INVALID_PARAMETER );
|
||||
return;
|
||||
}
|
||||
|
||||
if (!create_async( current, NULL, queue, apc, user, iosb ))
|
||||
return;
|
||||
|
||||
/* Check if the new pending request can be served immediately */
|
||||
events = check_fd_events( fd, file_get_poll_events( fd ) );
|
||||
if (events) file_poll_event( fd, events );
|
||||
|
||||
set_fd_events( fd, file_get_poll_events( fd ));
|
||||
}
|
||||
|
||||
static void file_cancel_async( struct fd *fd )
|
||||
{
|
||||
struct file *file = get_fd_user( fd );
|
||||
assert( file->obj.ops == &file_ops );
|
||||
|
||||
async_terminate_queue( &file->read_q, STATUS_CANCELLED );
|
||||
async_terminate_queue( &file->write_q, STATUS_CANCELLED );
|
||||
}
|
||||
|
||||
static struct fd *file_get_fd( struct object *obj )
|
||||
{
|
||||
struct file *file = (struct file *)obj;
|
||||
|
@ -328,11 +253,6 @@ static void file_destroy( struct object *obj )
|
|||
struct file *file = (struct file *)obj;
|
||||
assert( obj->ops == &file_ops );
|
||||
|
||||
if (is_overlapped( file ))
|
||||
{
|
||||
async_terminate_queue( &file->read_q, STATUS_CANCELLED );
|
||||
async_terminate_queue( &file->write_q, STATUS_CANCELLED );
|
||||
}
|
||||
if (file->fd) release_object( file->fd );
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,10 @@ extern int flush_cached_fd( struct process *process, obj_handle_t handle );
|
|||
extern int default_fd_add_queue( struct object *obj, struct wait_queue_entry *entry );
|
||||
extern void default_fd_remove_queue( struct object *obj, struct wait_queue_entry *entry );
|
||||
extern int default_fd_signaled( struct object *obj, struct thread *thread );
|
||||
extern int default_fd_get_poll_events( struct fd *fd );
|
||||
extern void default_poll_event( struct fd *fd, int event );
|
||||
extern void default_fd_queue_async( struct fd *fd, void *apc, void *user, void *io_sb, int type, int count );
|
||||
extern void default_fd_cancel_async( struct fd *fd );
|
||||
extern int no_flush( struct fd *fd, struct event **event );
|
||||
extern int no_get_file_info( struct fd *fd );
|
||||
extern void no_queue_async( struct fd *fd, void* apc, void* user, void* io_sb, int type, int count);
|
||||
|
|
Loading…
Reference in New Issue