This patch is a repost of my patch of 2002-01-03, now against the current CVS version including Mike McCormack's latest patches (01-cancelio.diff, 01-write_file_update.diff, 01-comm16_update.diff as of 2002-01-06). It is the first in a series of patches that constitute intermediate steps towards async socket IO. I consider them pretty clean and have done basic testing. My testing possibilities are limited, though - I'd be grateful to anybody who could run tests especially with 16-bit network-oriented windows applications. Bugs in my code should show up immediately as regressions. Patch file: 001-file_overlapped.diff Purpose: This patch fixes overlapped I/O on regular files. Created: 2002-01-07 Applies: CVS 2002-01-07 with Mike McCorkack's patches of 2002-01-06 applied (01-cancelio.diff, 01-write_file_update.diff, 01-comm16_update.diff) Add server side support for async IO on files. FILE_Async[Read|Write]Service and WriteFile() use pread() / pwrite() rather than read() / write () for fd's on which lseek()ing is possible to honour the fact that lpOverlapped->Offset and lpOverlapped->OffsetHigh contain the offset from which the file is to be read. files/file.c: OVERLAPPED_OFFSET: Macro to calculate file offset from OVERLAPPED struct. FILE_AsyncReadService(): Use pread() on fd's that allow lseek(). FILE_AsyncWriteService(): Use pwrite() on fd's that allow lseek(). WriteFile(): dito. server/file.c: struct file: Add two struct async_queue fields (for async read/write). file_ops: file specific poll_event() method accounts for overlapped IO. define queue_async() method. create_file_for_fd(): call init_async_queue() on overlapped files. file_poll_event(): call default_poll_event() only after checking for queued async requests. file_get_info(): return FD_TYPE_OVERLAPPED if FILE_FLAG_OVERLAPPED is set. file_queue_async(): implement queue_async() method for files. file_destroy(): destroy async queues on overlapped files. Martin Wilck <Martin.Wilck@Fujitsu-Siemens.com> diff -ruX diffignore CVS/wine/files/file.c MW/wine/files/file.c --- CVS/wine/files/file.c Mon Jan 7 15:54:59 2002 +++ MW/wine/files/file.c Mon Jan 7 15:55:36 2002 @@ -9,6 +9,9 @@ * Right now, they simply call the CopyFile method. */ +/* This is needed to avoid compile-time warnings about pread / pwrite */ +#define _GNU_SOURCE +#define _XOPEN_SOURCE 500 #include "config.h" #include "wine/port.h" @@ -55,6 +58,9 @@ /* Size of per-process table of DOS handles */ #define DOS_TABLE_SIZE 256 +/* Macro to derive file offset from OVERLAPPED struct */ +#define OVERLAPPED_OFFSET(overlapped) ((off_t) (overlapped)->Offset + ((off_t) (overlapped)->OffsetHigh << 32)) + static HANDLE dos_handles[DOS_TABLE_SIZE]; @@ -1304,12 +1310,21 @@ { LPOVERLAPPED lpOverlapped = ovp->lpOverlapped; int result, r; + int already = lpOverlapped->InternalHigh; TRACE("%p %p\n", lpOverlapped, ovp->buffer ); /* check to see if the data is ready (non-blocking) */ - result = read(ovp->fd, &ovp->buffer[lpOverlapped->InternalHigh], - ovp->count - lpOverlapped->InternalHigh); + + /* Check if this file object can lseek() */ + /* FIXME: test this only once for each file object ?? */ + if (lseek (ovp->fd, 0, SEEK_CUR) != (off_t) -1) + result = pread (ovp->fd, + &ovp->buffer[already], + ovp->count - already, + OVERLAPPED_OFFSET (lpOverlapped) + already); + else + result = read (ovp->fd, &ovp->buffer[already], ovp->count - already); if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR))) { @@ -1543,12 +1558,21 @@ { LPOVERLAPPED lpOverlapped = ovp->lpOverlapped; int result, r; + int already = lpOverlapped->InternalHigh; TRACE("(%p %p)\n",lpOverlapped,ovp->buffer); /* write some data (non-blocking) */ - result = write(ovp->fd, &ovp->buffer[lpOverlapped->InternalHigh], - ovp->count-lpOverlapped->InternalHigh); + + /* Check if this file object can lseek() */ + /* FIXME: test this only once for each file object ?? */ + if (lseek (ovp->fd, 0, SEEK_CUR) != (off_t) -1) + result = pwrite (ovp->fd, + &ovp->buffer[already], + ovp->count - already, + OVERLAPPED_OFFSET (lpOverlapped) + already); + else + result = write (ovp->fd, &ovp->buffer[already], ovp->count - already); if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR))) { @@ -1678,7 +1702,13 @@ } /* see if we can write some data already (this shouldn't block) */ - result = write( unix_handle, buffer, bytesToWrite ); + + if ( lseek(unix_handle, 0, SEEK_CUR) != (off_t) -1 ) + result = pwrite( unix_handle, buffer, bytesToWrite, + OVERLAPPED_OFFSET (overlapped) ); + else + result = write( unix_handle, buffer, bytesToWrite ); + close(unix_handle); if(result<0) @@ -1693,7 +1723,7 @@ result = 0; } - /* if we read enough to keep the app happy, then return now */ + /* if we wrote enough to keep the app happy, then return now */ if(result>=bytesToWrite) { *bytesWritten = result; diff -ruX diffignore CVS/wine/server/file.c MW/wine/server/file.c --- CVS/wine/server/file.c Mon Jan 7 15:54:59 2002 +++ MW/wine/server/file.c Mon Jan 7 15:55:28 2002 @@ -29,6 +29,7 @@ #include "handle.h" #include "thread.h" #include "request.h" +#include "async.h" struct file { @@ -39,6 +40,9 @@ unsigned int flags; /* flags (FILE_FLAG_*) */ unsigned int sharing; /* file sharing mode */ int drive_type; /* type of drive the file is on */ + + struct async_queue read_q; + struct async_queue write_q; }; #define NAME_HASH_SIZE 37 @@ -47,10 +51,12 @@ static void file_dump( struct object *obj, int verbose ); static int file_get_poll_events( struct object *obj ); +static void file_poll_event( struct object *obj, int event ); static int file_get_fd( struct object *obj ); static int file_flush( struct object *obj ); static int file_get_info( struct object *obj, struct get_file_info_reply *reply ); static void file_destroy( struct object *obj ); +static struct async_queue * file_queue_async(struct object *obj, struct async* async, int type, int count); static const struct object_ops file_ops = { @@ -61,11 +67,11 @@ default_poll_signaled, /* signaled */ no_satisfied, /* satisfied */ file_get_poll_events, /* get_poll_events */ - default_poll_event, /* poll_event */ + file_poll_event, /* poll_event */ file_get_fd, /* get_fd */ file_flush, /* flush */ file_get_info, /* get_file_info */ - NULL, /* queue_async */ + file_queue_async, /* queue_async */ file_destroy /* destroy */ }; @@ -116,6 +122,11 @@ file->flags = attrs; file->sharing = sharing; file->drive_type = drive_type; + if (file->flags & FILE_FLAG_OVERLAPPED) + { + init_async_queue (&file->read_q); + init_async_queue (&file->write_q); + } } return file; } @@ -253,6 +264,27 @@ return events; } +static void file_poll_event( struct object *obj, int event ) +{ + struct file *file = (struct file *)obj; + assert( obj->ops == &file_ops ); + if ( file->flags & FILE_FLAG_OVERLAPPED ) + { + if( IS_READY(file->read_q) && (POLLIN & event) ) + { + async_notify(file->read_q.head, STATUS_ALERTED); + return; + } + if( IS_READY(file->write_q) && (POLLIN & event) ) + { + async_notify(file->write_q.head, STATUS_ALERTED); + return; + } + } + default_poll_event( obj, event ); +} + + static int file_get_fd( struct object *obj ) { struct file *file = (struct file *)obj; @@ -308,9 +340,45 @@ reply->index_low = st.st_ino; reply->serial = 0; /* FIXME */ } + + if (file->flags & FILE_FLAG_OVERLAPPED) + return FD_TYPE_OVERLAPPED; + return FD_TYPE_DEFAULT; } +static struct async_queue *file_queue_async(struct object *obj, struct async *async, int type, int count) +{ + struct file *file = (struct file *)obj; + struct async_queue *q; + + assert( obj->ops == &file_ops ); + + if ( !(file->flags & FILE_FLAG_OVERLAPPED) ) + { + set_error ( STATUS_INVALID_HANDLE ); + return NULL; + } + + switch(type) + { + case ASYNC_TYPE_READ: + q = &file->read_q; + break; + case ASYNC_TYPE_WRITE: + q = &file->write_q; + break; + default: + set_error( STATUS_INVALID_PARAMETER ); + return NULL; + } + + if(async && !async->q) + async_insert(q, async); + + return q; +} + static void file_destroy( struct object *obj ) { struct file *file = (struct file *)obj; @@ -325,6 +393,11 @@ *pptr = (*pptr)->next; if (file->flags & FILE_FLAG_DELETE_ON_CLOSE) unlink( file->name ); free( file->name ); + } + if (file->flags & FILE_FLAG_OVERLAPPED) + { + destroy_async_queue (&file->read_q); + destroy_async_queue (&file->write_q); } }