here is a patch that fixes some of the issues with the async IO
implementation in Wine that we discussed previously. My work on this
was delayed by other duties, sorry. To make matters worse, I'll be
out of office for the next 4 weeks (vacation :-)
THIS PATCH IS NOT FOR PRODUCTION USE.
I am submitting it as a RFC to show the basic concepts I am aiming at.
Most important is the server-side code (server/async.h, server/async.c)
that demonstrates the basic mechanism I am suggesting.
As an example, I recoded ReadFileEx() to use the new approach. Basic
testing with disk file I/O works as expected. The code no longer messes
up the order of requests.
Please have a look at this code and tell me if you consider it a
reasonable basis for further development.
The patch is against today's (04/12/2001) CVS version.
--
Martin Wilck Phone: +49 5251 8 15113
Fujitsu Siemens Computers Fax: +49 5251 8 20409
Heinz-Nixdorf-Ring 1 mailto:Martin.Wilck@Fujitsu-Siemens.com
D-33106 Paderborn
http://www.fujitsu-siemens.com/primergy
diff -ruN -X diffignore CVS/wine/files/file.c MW/wine/files/file.c
--- CVS/wine/files/file.c Mon Dec 3 13:08:10 2001
+++ MW/wine/files/file.c Tue Dec 4 22:10:16 2001
@@ -57,6 +57,19 @@
static HANDLE dos_handles[DOS_TABLE_SIZE];
+typedef struct file_async_request {
+ int fd;
+ HANDLE request_handle;
+ LPOVERLAPPED ovl;
+ LPOVERLAPPED_COMPLETION_ROUTINE completion_func;
+ LPVOID buffer;
+ unsigned int count;
+ mode_t flags;
+} file_async_request;
+
+/* A trick to avoid clash with S_IFMT */
+#define _FILE_ASYNC_CANSEEK S_IXOTH
+#define FILE_ASYNC_CANSEEK(as_req) ((as_req)->flags & _FILE_ASYNC_CANSEEK)
/***********************************************************************
* FILE_ConvertOFMode
@@ -1256,6 +1269,231 @@
return FALSE;
}
+static void FILE_AsyncIoFinish (struct file_async_request* as)
+{
+ LPOVERLAPPED ovl = as->ovl;
+
+ NtSetEvent (ovl->hEvent, NULL);
+
+ /* This will dequeue the request in the server */
+ CloseHandle (as->request_handle);
+ close (as->fd);
+
+ HeapFree (GetProcessHeap(), 0, as);
+ SetLastError (ovl->Internal); /* FIXME: correct ?? */
+}
+
+/***********************************************************************
+ * FILE_AsyncIoFinish (INTERNAL)
+ *
+ */
+static void CALLBACK FILE_AsyncIoCallCompletion (ULONG_PTR data)
+{
+ struct file_async_request *as = (struct file_async_request*) data;
+ LPOVERLAPPED ovl = as->ovl;
+
+ TRACE ("as %p, ovl %p, func %p, status %lx, transferred %ld\n",
+ as, ovl, as->completion_func, ovl->Internal, ovl->InternalHigh);
+
+ as->completion_func (ovl->Internal, ovl->InternalHigh, ovl);
+
+ FILE_AsyncIoFinish (as);
+
+}
+
+/***********************************************************************
+ * FILE_ReadFile_APC (INTERNAL)
+ *
+ */
+static void FILE_ReadFile_APC (struct file_async_request *as, int events)
+{
+ int r;
+ int already;
+ LPOVERLAPPED ovl = as->ovl;
+
+ TRACE ("events: %x, request: %p\n", events, as);
+
+ if (ovl->Internal != STATUS_PENDING)
+ {
+ ERR ("status is %lx, should be %x\n", ovl->Internal, STATUS_PENDING);
+ r = STATUS_UNSUCCESSFUL;
+ goto apc_end;
+ }
+
+ if (events & (POLLHUP | POLLERR | POLLNVAL))
+ {
+ TRACE ("poll error condition for fd %d in async request %p\n", as->fd, as);
+ r = STATUS_UNSUCCESSFUL;
+ goto apc_end;
+ }
+
+ if (!events)
+ {
+ TRACE ("read timed out\n");
+ r = STATUS_TIMEOUT;
+ goto apc_end;
+ }
+
+ already = ovl->InternalHigh;
+
+ if (FILE_ASYNC_CANSEEK (as))
+ {
+ off_t ofs = already + ovl->Offset + ((off_t) ovl->OffsetHigh << 32);
+ r = pread (as->fd, as->buffer + already, as->count - already, ofs);
+ }
+ else
+ r = read (as->fd, as->buffer + already, as->count - already);
+
+ if (r < 0)
+ {
+ int err = errno;
+ TRACE ("read returned errno %d\n", err);
+ r = (err == EAGAIN || err == EINTR ? STATUS_PENDING : STATUS_UNSUCCESSFUL);
+ goto apc_end;
+ }
+
+ else if (!r)
+ {
+ r = STATUS_END_OF_FILE;
+ goto apc_end;
+ }
+
+ TRACE ("read %d bytes, cumul: %d, requested: %d\n", r, already + r, as->count);
+ ovl->InternalHigh += r;
+
+ /* FIXME: is this correct behaviour ? */
+ /* If less then count (but > 0) bytes were read, we return success for FIFOs */
+ /* For regular files, this should only happen at EOF */
+
+ if (ovl->InternalHigh >= as->count || !S_ISREG (as->flags))
+ r = STATUS_SUCCESS;
+ else
+ r = STATUS_END_OF_FILE;
+
+apc_end:
+
+ ovl->Internal = r;
+
+ if (r == STATUS_PENDING)
+ {
+
+ TRACE ("rescheduling request\n");
+ SERVER_START_REQ (requeue_async_request)
+ {
+ req->handle = as->request_handle;
+ r = wine_server_call_err (req);
+ }
+ SERVER_END_REQ;
+
+ if (r)
+ ovl->Internal = RtlNtStatusToDosError (r);
+ else
+ return;
+ }
+
+ TRACE ("request finished, status: %x\n", r);
+
+ if (as->completion_func)
+ QueueUserAPC (FILE_AsyncIoCallCompletion, GetCurrentThread(), (ULONG_PTR) as);
+ else
+ FILE_AsyncIoFinish (as);
+}
+
+/***********************************************************************
+ * FILE_ReadFileEx (INTERNAL)
+ */
+static BOOL FILE_ReadFileEx (HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
+ LPOVERLAPPED overlapped,
+ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
+{
+ struct file_async_request *as;
+ int fd, r, err;
+ struct stat st;
+
+ TRACE ("file %d to buf %p num %ld %p func %p\n",
+ hFile, buffer, bytesToRead, overlapped, lpCompletionRoutine);
+
+ /* check that there is an overlapped struct with an event flag */
+ if ( (overlapped==NULL) || NtResetEvent( overlapped->hEvent, NULL ) )
+ {
+ TRACE ("Overlapped not specified or invalid event flag\n");
+ err = ERROR_INVALID_PARAMETER;
+ goto out_err;
+ }
+
+ fd = FILE_GetUnixHandle( hFile, GENERIC_READ );
+ if(fd < 0)
+ {
+ TRACE ("Cannot get fd\n");
+ err = ERROR_INVALID_HANDLE;
+ goto out_err;
+ }
+
+ as = (struct file_async_request*)
+ HeapAlloc (GetProcessHeap(), 0, sizeof (struct file_async_request));
+
+ if(!as)
+ {
+ TRACE ("HeapAlloc Failed\n");
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto out_close;
+ }
+
+ overlapped->InternalHigh = 0;
+ overlapped->Internal = STATUS_PENDING;
+
+ as->fd = fd;
+ as->ovl = overlapped;
+ as->completion_func = lpCompletionRoutine;
+ as->buffer = buffer;
+
+ if (fstat (fd, &st) == -1)
+ {
+ TRACE ("cannot fstat fd %d\n", fd);
+ err = ERROR_FILE_INVALID;
+ goto out_free;
+ }
+
+ as->flags = st.st_mode & S_IFMT;
+ if (lseek (fd, 0, SEEK_CUR) != -1)
+ as->flags |= _FILE_ASYNC_CANSEEK;
+
+ SERVER_START_REQ (create_async_request)
+ {
+ req->type = ASYNC_TYPE_READ;
+ req->io_handle = hFile;
+ req->count = as->count = bytesToRead;
+ req->func = FILE_ReadFile_APC;
+ req->data = as;
+ r = wine_server_call_err (req);
+ if (!r)
+ as->request_handle = reply->handle;
+ }
+ SERVER_END_REQ;
+
+ if (r)
+ {
+ TRACE ("Couldn't create request: %lx\n", GetLastError());
+ err = RtlNtStatusToDosError (r);
+ goto out_free;
+ }
+
+ TRACE ("success\n");
+ return TRUE;
+
+out_free:
+ HeapFree (GetProcessHeap(), 0, as);
+
+out_close:
+ close (fd);
+
+out_err:
+ SetLastError (err);
+ return FALSE;
+
+}
+
+
/***********************************************************************
* FILE_AsyncReadService (INTERNAL)
*
@@ -1354,7 +1592,7 @@
/***********************************************************************
* FILE_ReadFileEx (INTERNAL)
*/
-static BOOL FILE_ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
+static BOOL FILE_ReadFileExOLD(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
LPOVERLAPPED overlapped,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
@@ -1456,27 +1694,32 @@
}
/* see if we can read some data already (this shouldn't block) */
- result = read( unix_handle, buffer, bytesToRead );
- close(unix_handle);
- if(result<0)
- {
- FILE_SetDosError();
- return FALSE;
- }
+/* !! MW: commented out immediate reading for testing purposes (test overlapped IO) !! */
+
+ result = 0;
+
+/* result = read( unix_handle, buffer, bytesToRead ); */
+/* close(unix_handle); */
+
+/* if(result<0) */
+/* { */
+/* FILE_SetDosError(); */
+/* return FALSE; */
+/* } */
/* if we read enough to keep the app happy, then return now */
- if(result>=bytesToRead)
- {
- *bytesRead = result;
- return TRUE;
- }
+/* if(result>=bytesToRead) */
+/* { */
+/* *bytesRead = result; */
+/* return TRUE; */
+/* } */
/* at last resort, do an overlapped read */
overlapped->Internal = STATUS_PENDING;
overlapped->InternalHigh = result;
- if(!FILE_ReadFileEx(hFile, buffer, bytesToRead, overlapped, NULL))
+ if(!FILE_ReadFileEx (hFile, buffer, bytesToRead, overlapped, NULL))
return FALSE;
/* fail on return, with ERROR_IO_PENDING */
diff -ruN -X diffignore CVS/wine/server/async.c MW/wine/server/async.c
--- CVS/wine/server/async.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/async.c Tue Dec 4 22:05:19 2001
@@ -3,20 +3,477 @@
*
* Copyright (C) 1998 Alexandre Julliard
* Copyright (C) 2000 Mike McCormack
+ * Copyright (C) 2001 Martin Wilck
*
*/
#include "config.h"
#include <assert.h>
+#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
+#include <sys/poll.h>
#include "handle.h"
#include "thread.h"
#include "request.h"
+#include "async.h"
+/* This is a wine server object only because we need a client handle for later reference */
+struct async_request
+{
+ struct object obj;
+ struct async_request *next; /* Linked list of requests */
+ struct async_request *prev;
+ struct thread *thread; /* Thread or process that issued this request */
+ struct object *io_obj; /* The object that represents the IO in the server */
+ unsigned int events; /* Events this request is interested in */
+ unsigned int flags; /* Request type, misc flags */
+ struct timeout_user *timeout; /* For timeout requests */
+ struct timeval tv; /* Time at which request times out */
+ int count; /* Number of bytes to transfer, needed for setup */
+ int result; /* Async IO result - FIXME: do we need this ? */
+ async_io_handler_t func; /* Client handler function */
+ void *data; /* Client handler data */
+};
+
+static void async_request_dump (struct object *, int);
+static void async_request_destroy (struct object *obj );
+static const struct object_ops async_request_ops =
+{
+ sizeof (struct async_request), /* size */
+ async_request_dump, /* dump */
+ no_add_queue, /* add_queue */
+ NULL, /* remove_queue */
+ NULL, /* signaled */
+ NULL, /* satisfied */
+ NULL, /* get_poll_events */
+ NULL, /* poll_event */
+ no_get_fd, /* get_fd */
+ no_flush, /* flush */
+ no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
+ async_request_destroy /* destroy */
+};
+
+static void
+async_request_dump (struct object *obj, int verbose)
+{
+ struct async_request *r = (struct async_request*) obj;
+
+ assert (obj->ops == &async_request_ops);
+
+ fprintf (stderr, "async_request: io_obj: %p events: %x, flags: %x\n",
+ r->io_obj, r->events, r->flags);
+ if (!verbose)
+ return;
+ fprintf (stderr, " %s"
+ " func: %p, data: %p\n",
+ (r->timeout ? "with timeout\n" : ""),
+ r->func, r->data);
+}
+
+struct async*
+alloc_async (struct object *obj)
+{
+ struct async *async = NULL;
+ async = mem_alloc (sizeof (struct async));
+ if (async) {
+ async->readers = async->writers = async->others = NULL;
+ async->flags = 0U;
+ async->wait.obj = obj;
+ async->poll_event = async_poll_event_1;
+ async->get_poll_events = async_get_poll_events_1;
+ async->setup_request = async_setup_request;
+ }
+ return async;
+}
+
+static inline struct async_request**
+get_async_queue (struct async* async, struct async_request* request)
+{
+ switch (ASYNC_REQUEST_TYPE (request))
+ {
+ case ASYNC_TYPE_READ:
+ return &async->readers;
+ case ASYNC_TYPE_WRITE:
+ return &async->writers;
+ default:
+ return &async->others;
+ }
+}
+
+static void
+queue_async_request (struct object *obj, struct async* async, struct async_request* req)
+{
+ struct async_request **queue = get_async_queue (async, req);
+ int must_add = (! (async->readers || async->writers || async->others));
+
+ if (!(*queue))
+ *queue = req->prev = req->next = req;
+ else
+ {
+ struct async_request *last = (*queue)->prev;
+ last->next = req;
+ req->next = *queue;
+ req->prev = last;
+ (*queue)->prev = req;
+ }
+
+ if (must_add)
+ obj->ops->add_queue (obj, &async->wait);
+}
+
+static void
+dequeue_async_request (struct object* obj, struct async_request* req)
+{
+ struct async *async;
+ struct async_request **queue;
+
+ obj->ops->get_async (obj, &async);
+
+ if (!async)
+ {
+ fprintf (stderr, "dequeue_async_request: invalid IO object\n");
+ goto do_req;
+ }
+
+ queue = get_async_queue (async, req);
+
+ if (!(*queue))
+ {
+ fprintf (stderr, "dequeue_async_request: no queue\n");
+ goto do_req;
+ }
+
+ if (!(req->next && req->prev))
+ {
+ fprintf (stderr, "dequeue_async_request: request isn't queued\n");
+ goto do_req;
+ }
+
+ if (req->next == req) /* One-element-list */
+ {
+ if (*queue == req)
+ {
+ *queue = NULL;
+
+ /* remove the IO object from poll wait queue if all request queues are empty */
+ if (! (async->readers || async->writers || async->others))
+ obj->ops->remove_queue (obj, &async->wait);
+ }
+ else /* One element lists must be queue heads */
+ {
+ fprintf (stderr, "dequeue_async_request: invalid request\n");
+ goto do_req;
+ }
+ }
+ else
+ {
+ req->next->prev = req->prev;
+ req->prev->next = req->next;
+ if (*queue == req)
+ *queue = req->next;
+ }
+
+ /* The event set may have changed after we removed this request */
+ set_select_events (obj, obj->ops->get_poll_events (obj));
+
+do_req:
+ /* After handling the queues, carry out the necessary steps for this request */
+
+ if (req->timeout)
+ {
+ remove_timeout_user (req->timeout);
+ req->timeout = NULL;
+ }
+
+ req->next = req->prev = NULL;
+ return;
+}
+
+static void
+async_request_destroy (struct object *obj )
+{
+ struct async_request *r = (struct async_request*) obj;
+ struct object *io;
+
+ assert (obj->ops == &async_request_ops);
+
+ io = r->io_obj;
+ grab_object (io);
+ dequeue_async_request (io, r);
+ release_object (io);
+}
+
+static inline int
+queue_async_apc (struct async_request *request, int event)
+{
+ if (ASYNC_REQUEST_APC_QUEUED (request))
+ return 0;
+
+ request->flags |= _ASYNC_REQUEST_APC_QUEUED;
+
+ if (request->timeout) /* Not a timeout */
+ {
+ remove_timeout_user (request->timeout);
+ request->timeout = NULL;
+ }
+
+ thread_queue_apc (request->thread,
+ NULL, /* no owner - don't dequeue other APCs */
+ request->func,
+ APC_ASYNC,
+ 1, /* use system queue */
+ 2, /* number of arguments */
+ request->data, /* client data */
+ event);
+ return 1;
+}
+
+static void
+async_timeout (void* private)
+{
+ struct async_request *r = (struct async_request*) private;
+ r->timeout = NULL;
+ queue_async_apc (r, 0);
+}
+
+static void
+async_queue_all (struct async_request* queue, int event)
+{
+ struct async_request* r;
+
+ if (!queue)
+ return;
+
+ r = queue;
+
+ queue_async_apc (r, event);
+ for (r = r->next; r != queue; r = r->next)
+ queue_async_apc (r, event);
+}
+
+static void
+async_queue_all_matching (struct async_request* queue, int event)
+{
+ struct async_request* r;
+
+ if (!queue)
+ return;
+
+ r = queue;
+ if (r->events & event)
+ queue_async_apc (r, event);
+ for (r = r->next; r != queue; r = r->next)
+ if (r->events & event)
+ queue_async_apc (r, event);
+}
+
+/* This function never dequeues anything. Requests are only
+ taken off the queue(s) if the client side signals they have finished. */
+/* This variant queues only the first reader / writer. */
+/* Other variations may follow that queue more than a single request at a time
+ for objects that support it. */
+/* Note that objects that implement async IO may use their own function instead,
+ which should provide similar functionality . */
+void
+async_poll_event_1 (struct object* obj, int event)
+{
+ struct async *async;
+ struct async_request *request;
+ obj->ops->get_async (obj, &async);
+
+ if (!async)
+ {
+ fprintf (stderr, "aync_poll_event: invalid object\n");
+ return;
+ }
+
+ if ((request = async->readers) && (event & POLLIN) && queue_async_apc (request, event))
+ event &= ~POLLIN; /* FIXME: is this correct ? */
+
+ if ((request = async->writers) && (event & POLLOUT) && queue_async_apc (request, event))
+ event &= ~POLLOUT; /* FIXME: is this correct ? */
+
+ async_queue_all_matching (async->others, event);
+
+ /* Queue everything unconditionally if we got errors - client side must cancel the requests */
+ if (event & (POLLHUP | POLLERR | POLLNVAL))
+ {
+ async_queue_all (async->readers, event);
+ async_queue_all (async->writers, event);
+ async_queue_all (async->others, event);
+ }
+
+ /* re-check the event list we listen to */
+ set_select_events (obj, obj->ops->get_poll_events (obj));
+ return;
+}
+
+/* Requests that are already queued do not take part in polling anymore */
+/* This variant assumes only one reader / writer can receive events */
+/* Note that objects that implement async IO may use their own function instead,
+ which should provide similar functionality . */
+int
+async_get_poll_events_1 (struct object* obj)
+{
+ struct async *as = NULL;
+ int ret = POLLHUP | POLLERR | POLLNVAL; /* catch errors */
+
+ obj->ops->get_async (obj, &as);
+
+ if (!as)
+ return -1;
+
+ if (as->readers && !ASYNC_REQUEST_APC_QUEUED(as->readers))
+ ret |= POLLIN;
+
+ if (as->writers && !ASYNC_REQUEST_APC_QUEUED(as->writers))
+ ret |= POLLOUT;
+
+ if (as->others)
+ {
+ struct async_request *r = as->others;
+ if (!ASYNC_REQUEST_APC_QUEUED(r))
+ ret |= r->events;
+ for (r = r->next; r != as->others; r = r->next)
+ if (!ASYNC_REQUEST_APC_QUEUED(r))
+ ret |= r->events;
+ }
+
+ return ret;
+}
+
+/* Default (dummy) setup function. Supports only read and write, and disables timeouts. */
+/* This will be overwritten e.g. by the serial port implementation. */
+int
+async_setup_request (struct async* async, struct async_request* request)
+{
+ request->tv.tv_usec = request->tv.tv_sec = 0;
+
+ request->events = 0;
+ switch (ASYNC_REQUEST_TYPE (request))
+ {
+ case ASYNC_TYPE_READ:
+ request->events |= POLLIN;
+ break;
+ case ASYNC_TYPE_WRITE:
+ request->events |= POLLOUT;
+ break;
+ default:
+ fprintf (stderr, "async_setup_request: unsupported request type\n");
+ return -1;
+ };
+ return 0;
+}
+
+static struct async_request*
+alloc_async_request (struct object *obj)
+{
+ struct async_request* r = NULL;
+ r = alloc_object (&async_request_ops, -1);
+ if (r)
+ {
+ r->io_obj = obj;
+ r->thread = current;
+ r->prev = r->next = NULL;
+ r->timeout = NULL;
+ r->result = 0;
+ }
+ return r;
+}
+
+DECL_HANDLER (create_async_request)
+{
+ struct object *obj;
+ struct async *async;
+ struct async_request *request;
+ int err = STATUS_INVALID_HANDLE;
+
+ if (!(obj = get_handle_obj( current->process, req->io_handle, 0, NULL )))
+ goto out;
+
+ if ((err = obj->ops->get_async (obj, &async)) != STATUS_SUCCESS)
+ goto error;
+
+ request = alloc_async_request (obj);
+
+ if (!request)
+ {
+ err = STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ /* FIXME: flags other than type ? */
+ request->flags = req->type & (~_ASYNC_REQUEST_APC_QUEUED & ASYNC_REQUEST_TYPE_MASK);
+ request->count = req->count;
+ request->func = req->func;
+ request->data = req->data;
+
+ if (async->setup_request (async, request) < 0)
+ {
+ err = STATUS_UNSUCCESSFUL;
+ goto free;
+ }
+
+ if (ASYNC_USES_TIMEOUT (async))
+ request->timeout = add_timeout_user (&request->tv, async_timeout, request);
+
+ queue_async_request (obj, async, request);
+
+ reply->handle = alloc_handle (current->process, request, GENERIC_READ|GENERIC_WRITE, 0);
+ set_error (STATUS_SUCCESS); /* FIXME - is that necessary ?? */
+
+ release_object (request);
+ release_object (obj); /* FIXME: should we skip this release_object to prevent
+ the io-object to be freed while there are async requests around,
+ or should the destroy routine of the object handle that ?? */
+
+ return;
+
+free:
+ release_object (request);
+
+error:
+ release_object (obj);
+
+out:
+ reply->handle = 0;
+ set_error (err);
+ return;
+}
+
+/* This is called when an APC does not finish */
+DECL_HANDLER(requeue_async_request)
+{
+ struct async_request *request;
+ int err = STATUS_INVALID_HANDLE;
+ struct object *io;
+
+ if (!(request = (struct async_request*) get_handle_obj (current->process, req->handle, 0, NULL))
+ || request->obj.ops != &async_request_ops
+ || !ASYNC_REQUEST_APC_QUEUED (request))
+ goto out;
+
+ io = request->io_obj;
+ grab_object (io);
+
+ request->flags &= ~_ASYNC_REQUEST_APC_QUEUED;
+ set_select_events (io, io->ops->get_poll_events (io));
+
+ release_object (io);
+ err = STATUS_SUCCESS;
+
+out:
+ set_error (err);
+ return;
+}
+
+
+/* The current CVS code, in order not to break Wine */
DECL_HANDLER(create_async)
{
@@ -32,3 +489,4 @@
release_object(obj);
}
+
diff -ruN -X diffignore CVS/wine/server/async.h MW/wine/server/async.h
--- CVS/wine/server/async.h Thu Jan 1 01:00:00 1970
+++ MW/wine/server/async.h Tue Dec 4 16:19:54 2001
@@ -0,0 +1,48 @@
+/*
+ * Wine server async I/O support
+ *
+ * Copyright (C) 2001 Martin Wilck
+ */
+
+#ifndef __WINE_SERVER_ASYNC_H
+#define __WINE_SERVER_ASYNC_H
+
+#include "object.h"
+
+#define _ASYNC_USES_TIMEOUT 0x01U
+#define ASYNC_USES_TIMEOUT(async) ((async)->flags & _ASYNC_USES_TIMEOUT)
+
+#define ASYNC_REQUEST_TYPE_MASK 0xffU
+#define ASYNC_REQUEST_TYPE(req) ((req)->flags & ASYNC_REQUEST_TYPE_MASK)
+#define _ASYNC_REQUEST_APC_QUEUED 0x100U
+#define ASYNC_REQUEST_APC_QUEUED(req) ((req)->flags & _ASYNC_REQUEST_APC_QUEUED)
+
+struct async_request;
+typedef int (*async_get_poll_events_t) (struct object*);
+typedef void (*async_poll_event_t) (struct object*, int);
+typedef int (*async_setup_request_t) (struct async*, struct async_request*);
+typedef void (*async_io_handler_t) (void*, int);
+
+/* This struct contains information about async IO features
+ of a given I/O object (file, comm port, socket ...) */
+struct async
+{
+ struct async_request *readers; /* List of read requests */
+ struct async_request *writers; /* List of write requests */
+ struct async_request *others; /* List of requests that dont't read or write */
+ unsigned int flags; /* Misc flags */
+ struct wait_queue_entry wait; /* wait queue entry */
+ async_poll_event_t poll_event; /* Function: react on poll event */
+ async_get_poll_events_t get_poll_events; /* Function: get events to react on */
+ async_setup_request_t setup_request; /* Function: Setup an event (timeout, events) at queue time */
+};
+
+/* Default handlers, can be overwritten */
+int async_get_poll_events_1 (struct object*);
+void async_poll_event_1 (struct object*, int);
+int async_setup_request (struct async*, struct async_request*);
+
+/* Utility functions */
+struct async* alloc_async (struct object*);
+
+#endif /* #ifndef __WINE_SERVER_ASYNC_H */
diff -ruN -X diffignore CVS/wine/server/atom.c MW/wine/server/atom.c
--- CVS/wine/server/atom.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/atom.c Mon Dec 3 13:09:03 2001
@@ -59,6 +59,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
atom_table_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/change.c MW/wine/server/change.c
--- CVS/wine/server/change.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/change.c Mon Dec 3 13:09:03 2001
@@ -37,6 +37,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
no_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/console.c MW/wine/server/console.c
--- CVS/wine/server/console.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/console.c Mon Dec 3 13:43:50 2001
@@ -40,6 +40,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
console_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
console_input_destroy /* destroy */
};
@@ -68,6 +69,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
console_input_events_destroy /* destroy */
};
@@ -108,6 +110,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
console_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
screen_buffer_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/debugger.c MW/wine/server/debugger.c
--- CVS/wine/server/debugger.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/debugger.c Mon Dec 3 13:09:04 2001
@@ -58,6 +58,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
debug_event_destroy /* destroy */
};
@@ -78,6 +79,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
debug_ctx_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/device.c MW/wine/server/device.c
--- CVS/wine/server/device.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/device.c Mon Dec 3 13:09:04 2001
@@ -44,6 +44,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
device_get_info, /* get_file_info */
+ no_get_async, /* get_async */
no_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/event.c MW/wine/server/event.c
--- CVS/wine/server/event.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/event.c Mon Dec 3 13:09:04 2001
@@ -38,6 +38,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
no_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/file.c MW/wine/server/file.c
--- CVS/wine/server/file.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/file.c Tue Dec 4 17:57:22 2001
@@ -29,6 +29,7 @@
#include "handle.h"
#include "thread.h"
#include "request.h"
+#include "async.h"
struct file
{
@@ -39,6 +40,7 @@
unsigned int flags; /* flags (FILE_FLAG_*) */
unsigned int sharing; /* file sharing mode */
int drive_type; /* type of drive the file is on */
+ struct async *async; /* Control structure for async IO */
};
#define NAME_HASH_SIZE 37
@@ -47,10 +49,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 *,int);
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 int file_get_async(struct object *,struct async **);
static const struct object_ops file_ops =
{
@@ -61,10 +65,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 */
+ file_get_async, /* get_async */
file_destroy /* destroy */
};
@@ -115,6 +120,7 @@
file->flags = attrs;
file->sharing = sharing;
file->drive_type = drive_type;
+ file->async = NULL;
}
return file;
}
@@ -247,11 +253,27 @@
struct file *file = (struct file *)obj;
int events = 0;
assert( obj->ops == &file_ops );
+
+ if (file->flags & FILE_FLAG_OVERLAPPED && file->async)
+ return async_get_poll_events_1 (obj);
+
if (file->access & GENERIC_READ) events |= POLLIN;
if (file->access & GENERIC_WRITE) events |= POLLOUT;
return events;
}
+static void file_poll_event(struct object *obj, int events)
+{
+ struct file *file = (struct file *)obj;
+ assert( obj->ops == &file_ops );
+
+ if (file->flags & FILE_FLAG_OVERLAPPED && file->async)
+ /* FIXME: Could we have non-async requests waiting for input also ? */
+ async_poll_event_1 (obj, events);
+ else
+ default_poll_event (obj, events);
+}
+
static int file_get_fd( struct object *obj )
{
struct file *file = (struct file *)obj;
@@ -307,7 +329,23 @@
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 int file_get_async(struct object *obj,struct async **pasync)
+{
+ struct file *file = (struct file *)obj;
+ assert (obj->ops == &file_ops);
+ if (!file->async)
+ {
+ file->async = alloc_async (obj);
+ }
+ *pasync = file->async;
+ return (file->async ? 0 : STATUS_NO_MEMORY);
}
static void file_destroy( struct object *obj )
diff -ruN -X diffignore CVS/wine/server/handle.c MW/wine/server/handle.c
--- CVS/wine/server/handle.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/handle.c Mon Dec 3 13:09:04 2001
@@ -92,6 +92,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
handle_table_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/mapping.c MW/wine/server/mapping.c
--- CVS/wine/server/mapping.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/mapping.c Mon Dec 3 13:09:04 2001
@@ -51,6 +51,7 @@
mapping_get_fd, /* get_fd */
no_flush, /* flush */
mapping_get_info, /* get_file_info */
+ no_get_async, /* get_async */
mapping_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/mutex.c MW/wine/server/mutex.c
--- CVS/wine/server/mutex.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/mutex.c Mon Dec 3 13:09:04 2001
@@ -42,6 +42,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
mutex_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/named_pipe.c MW/wine/server/named_pipe.c
--- CVS/wine/server/named_pipe.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/named_pipe.c Mon Dec 3 13:09:04 2001
@@ -80,6 +80,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
named_pipe_destroy /* destroy */
};
@@ -101,6 +102,7 @@
pipe_user_get_fd, /* get_fd */
no_flush, /* flush */
pipe_user_get_info, /* get_file_info */
+ no_get_async, /* get_async */
pipe_user_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/object.c MW/wine/server/object.c
--- CVS/wine/server/object.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/object.c Mon Dec 3 13:09:04 2001
@@ -269,6 +269,13 @@
return FD_TYPE_INVALID;
}
+int no_get_async(struct object *obj,struct async **as)
+{
+ if (as)
+ *as = NULL;
+ return STATUS_OBJECT_TYPE_MISMATCH;
+}
+
void no_destroy( struct object *obj )
{
}
diff -ruN -X diffignore CVS/wine/server/object.h MW/wine/server/object.h
--- CVS/wine/server/object.h Mon Dec 3 13:08:11 2001
+++ MW/wine/server/object.h Mon Dec 3 13:16:09 2001
@@ -48,6 +48,8 @@
int (*flush)(struct object *);
/* get file information */
int (*get_file_info)(struct object *,struct get_file_info_reply *);
+ /* fill in object-type specific async information */
+ int (*get_async)(struct object *,struct async **);
/* destroy on refcount == 0 */
void (*destroy)(struct object *);
};
@@ -90,6 +92,7 @@
extern int no_get_fd( struct object *obj );
extern int no_flush( struct object *obj );
extern int no_get_file_info( struct object *obj, struct get_file_info_reply *info );
+extern int no_get_async(struct object *,struct async **);
extern void no_destroy( struct object *obj );
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 );
diff -ruN -X diffignore CVS/wine/server/pipe.c MW/wine/server/pipe.c
--- CVS/wine/server/pipe.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/pipe.c Mon Dec 3 13:09:04 2001
@@ -53,6 +53,7 @@
pipe_get_fd, /* get_fd */
no_flush, /* flush */
pipe_get_info, /* get_file_info */
+ no_get_async, /* get_async */
pipe_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/process.c MW/wine/server/process.c
--- CVS/wine/server/process.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/process.c Mon Dec 3 13:09:06 2001
@@ -53,6 +53,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
process_destroy /* destroy */
};
@@ -92,6 +93,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
startup_info_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/protocol.def MW/wine/server/protocol.def
--- CVS/wine/server/protocol.def Mon Dec 3 13:08:11 2001
+++ MW/wine/server/protocol.def Tue Dec 4 13:54:13 2001
@@ -1541,6 +1541,21 @@
#define ASYNC_TYPE_WRITE 0x02
#define ASYNC_TYPE_WAIT 0x03
+/* Schedule an ansync I/O request */
+@REQ(create_async_request)
+ handle_t io_handle; /* Handle to IO object that is capable of async IO */
+ unsigned int type; /* READ / WRITE / WAIT */
+ int count; /* Number of bytes to transfer, -1 if unknown */
+ void* func; /* IO handler function */
+ void* data; /* pointer to arbitrary data object, handled by func */
+@REPLY
+ handle_t handle; /* handle to request object for later use */
+@END
+
+/* Reschedule an unfinished async I/O request */
+@REQ(requeue_async_request)
+ handle_t handle; /* handle to the request object to be rescheduled */
+@END
/* Create a named pipe */
@REQ(create_named_pipe)
diff -ruN -X diffignore CVS/wine/server/queue.c MW/wine/server/queue.c
--- CVS/wine/server/queue.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/queue.c Mon Dec 3 13:09:08 2001
@@ -112,6 +112,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
msg_queue_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/registry.c MW/wine/server/registry.c
--- CVS/wine/server/registry.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/registry.c Mon Dec 3 13:09:09 2001
@@ -142,6 +142,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
key_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/request.c MW/wine/server/request.c
--- CVS/wine/server/request.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/request.c Mon Dec 3 13:09:09 2001
@@ -66,6 +66,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
master_socket_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/request.h MW/wine/server/request.h
--- CVS/wine/server/request.h Mon Dec 3 13:08:11 2001
+++ MW/wine/server/request.h Tue Dec 4 16:20:02 2001
@@ -210,6 +210,8 @@
DECL_HANDLER(get_serial_info);
DECL_HANDLER(set_serial_info);
DECL_HANDLER(create_async);
+DECL_HANDLER(create_async_request);
+DECL_HANDLER(requeue_async_request);
DECL_HANDLER(create_named_pipe);
DECL_HANDLER(open_named_pipe);
DECL_HANDLER(connect_named_pipe);
@@ -364,6 +366,8 @@
(req_handler)req_get_serial_info,
(req_handler)req_set_serial_info,
(req_handler)req_create_async,
+ (req_handler)req_create_async_request,
+ (req_handler)req_requeue_async_request,
(req_handler)req_create_named_pipe,
(req_handler)req_open_named_pipe,
(req_handler)req_connect_named_pipe,
diff -ruN -X diffignore CVS/wine/server/semaphore.c MW/wine/server/semaphore.c
--- CVS/wine/server/semaphore.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/semaphore.c Mon Dec 3 13:09:09 2001
@@ -38,6 +38,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
no_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/serial.c MW/wine/server/serial.c
--- CVS/wine/server/serial.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/serial.c Mon Dec 3 13:09:09 2001
@@ -74,6 +74,7 @@
serial_get_fd, /* get_fd */
no_flush, /* flush */
serial_get_info, /* get_file_info */
+ no_get_async, /* get_async */
no_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/snapshot.c MW/wine/server/snapshot.c
--- CVS/wine/server/snapshot.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/snapshot.c Mon Dec 3 13:09:09 2001
@@ -50,6 +50,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
snapshot_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/sock.c MW/wine/server/sock.c
--- CVS/wine/server/sock.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/sock.c Mon Dec 3 13:09:09 2001
@@ -77,6 +77,7 @@
sock_get_fd, /* get_fd */
no_flush, /* flush */
sock_get_info, /* get_file_info */
+ no_get_async, /* get_async */
sock_destroy /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/thread.c MW/wine/server/thread.c
--- CVS/wine/server/thread.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/thread.c Mon Dec 3 13:09:09 2001
@@ -76,6 +76,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
destroy_thread /* destroy */
};
diff -ruN -X diffignore CVS/wine/server/timer.c MW/wine/server/timer.c
--- CVS/wine/server/timer.c Mon Dec 3 13:08:11 2001
+++ MW/wine/server/timer.c Mon Dec 3 13:09:09 2001
@@ -45,6 +45,7 @@
no_get_fd, /* get_fd */
no_flush, /* flush */
no_get_file_info, /* get_file_info */
+ no_get_async, /* get_async */
timer_destroy /* destroy */
};