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(a)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);
}
}