A new async patch - feedback, please :-)

Martin Wilck Martin.Wilck at fujitsu-siemens.com
Wed Jan 9 15:12:00 CST 2002


Hi Mike, all,

Unfortunately this patch has become big, because i came across so many
problems today and I just needed to go forward.
I am sorry for this, but it has become very late tonight, and I want
people to see this stuff to stop me before it's too late
(if I'm on the wrong track).

It compiles without warnings, but I have not tested it - may crash wine
right away, I don't know.

However it should illustrate my ideas well:

* Full separation of async handling and file io-specific stuff.
* Do not rely on LPOVERLAPPED for status data in generic code, it may be
  NULL. File IO code may rely on it, here it is required to be non-NULL.
* Use GetOverlappedResult() to check for immediate completion (UNTESTED!)

Please give me comments telling me which parts you don't like, so that I
can adjust for further development.

Mike, can you tell me about those apps that were causing trouble in the
past ? Where can I obtain them? Do they need special hardware (modem,
mouse) for test runs ?

Cheers,
Martin

-- 
Martin Wilck                Phone: +49 5251 8 15113
Fujitsu Siemens Computers   Fax:   +49 5251 8 20409
Heinz-Nixdorf-Ring 1	    mailto:Martin.Wilck at Fujitsu-Siemens.com
D-33106 Paderborn           http://www.fujitsu-siemens.com/primergy

ChangeLog:

server/protocol.def:
        cancel_async(): Define new reqest type.

server/async.c:
        cancel_async(): Implement new reqest type.

include/file.h:
        struct async_ops: 4 methods for async_private: get_status, set_status, get_count, call_completion.
        struct async_private: contains only those elements which are absolutely necessary for queueing and server interaction
        struct async_fileio: contains all other fields.

scheduler/synchro.c:
        register_async(): replacement for FILE_StartAsync(). Uses STATUS_USER_APC as (pseudo-) indicator
                to queue a new request. Hooks new requests in NtCurrentTeb()->pending_list (formerly done
                in calling functions). Not used by CancelIo() anymore. Finishes requests that are not in
                PENDING state after scheduling.
        finish_async(): Assume status is already set correctly. Queue call_completion unconditionally
                (No completion_func check needed)
        check_async(): Use register_async() for rescheduling.

files/file.c:
        GetOverlappedResult(): Return ERROR_IO_INCOMPLETE if IO still pending (MSDN docs)
        FILE_StartAsync(): -> scheduler/synchro.c: register_async
        CancelIo(): Use new cancel_async server request (I felt former StartAsync() was too overloaded).
                Skip requests with wrong handle.
        fileio_call_completion_func(): Now called unconditionally, check if completion_func is non-NULL.
        FILE_AsyncReadService(): type adjustments according to new API
        FILE_AsyncWriteService(): dito
        FILE_ReadFileEx(): use new async API. Call GetOverlappedResult() to check for immediate completion.
        FILE_WriteFileEx(): dito. Now exactly equivalent to FILE_ReadFileEx()

kernel/comm.c:
        COMM_WaitCommEventService(): Use new async API.

diff -ruX diffignore CVS/wine/dlls/kernel/comm.c MW/wine/dlls/kernel/comm.c
--- CVS/wine/dlls/kernel/comm.c	Tue Jan  8 18:53:41 2002
+++ MW/wine/dlls/kernel/comm.c	Wed Jan  9 21:20:37 2002
@@ -1514,12 +1514,13 @@
  */
 static void COMM_WaitCommEventService(async_private *ovp)
 {
-    LPOVERLAPPED lpOverlapped = ovp->lpOverlapped;
+    async_fileio *fileio = (async_fileio*) ovp;
+    LPOVERLAPPED lpOverlapped = fileio->lpOverlapped;

     TRACE("overlapped %p\n",lpOverlapped);

     /* FIXME: detect other events */
-            *ovp->buffer = EV_RXCHAR;
+    *fileio->buffer = EV_RXCHAR;

     lpOverlapped->Internal = STATUS_SUCCESS;
 }
@@ -1536,7 +1537,7 @@
     LPOVERLAPPED lpOverlapped) /* [in/out] for Asynchronous waiting */
 {
     int fd,ret;
-    async_private *ovp;
+    async_fileio *ovp;

     if(!lpOverlapped)
     {
@@ -1547,52 +1548,35 @@
     if(NtResetEvent(lpOverlapped->hEvent,NULL))
         return FALSE;

-    lpOverlapped->Internal = STATUS_PENDING;
-    lpOverlapped->InternalHigh = 0;
-    lpOverlapped->Offset = 0;
-    lpOverlapped->OffsetHigh = 0;
-
     fd = FILE_GetUnixHandle( hFile, GENERIC_WRITE );
     if(fd<0)
 	return FALSE;

-    ovp = (async_private *) HeapAlloc(GetProcessHeap(), 0, sizeof (async_private));
+    ovp = (async_fileio *) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio));
     if(!ovp)
     {
         close(fd);
         return FALSE;
     }
+
+    ovp->async.ops = &fileio_async_ops;
+    ovp->async.handle = hFile;
+    ovp->async.fd = fd;
+    ovp->async.type = ASYNC_TYPE_WAIT;
+    ovp->async.func = COMM_WaitCommEventService;
+
     ovp->lpOverlapped = lpOverlapped;
-    ovp->func = COMM_WaitCommEventService;
     ovp->buffer = (char *)lpdwEvents;
-    ovp->fd = fd;
     ovp->count = 0;
     ovp->completion_func = 0;
-    ovp->type = ASYNC_TYPE_WAIT;
-    ovp->handle = hFile;
-
-    ovp->next = NtCurrentTeb()->pending_list;
-    ovp->prev = NULL;
-    if(ovp->next)
-        ovp->next->prev=ovp;
-    NtCurrentTeb()->pending_list = ovp;

-    /* start an ASYNCHRONOUS WaitCommEvent */
-    SERVER_START_REQ( register_async )
-    {
-        req->handle = hFile;
-        req->overlapped = lpOverlapped;
-        req->type = ASYNC_TYPE_WAIT;
-        req->count = 0;
-        req->func = check_async_list;
-        req->status = STATUS_PENDING;
-
-        ret=wine_server_call_err(req);
-    }
-    SERVER_END_REQ;
+    lpOverlapped->Internal = STATUS_USER_APC;
+    lpOverlapped->InternalHigh = 0;
+    lpOverlapped->Offset = 0;
+    lpOverlapped->OffsetHigh = 0;

-    if (!ret)
-    SetLastError(ERROR_IO_PENDING);
+    if (register_async (&ovp->async))
+        SetLastError(ERROR_IO_PENDING);

     return FALSE;
 }
diff -ruX diffignore CVS/wine/files/file.c MW/wine/files/file.c
--- CVS/wine/files/file.c	Tue Jan  8 18:57:47 2002
+++ MW/wine/files/file.c	Wed Jan  9 20:55:09 2002
@@ -60,6 +60,20 @@

 static HANDLE dos_handles[DOS_TABLE_SIZE];

+/* Async operations struct (see file.h) */
+
+static DWORD fileio_get_async_status (async_private *ovp);
+static DWORD fileio_get_async_count (async_private *ovp);
+static void fileio_set_async_status (async_private *ovp, DWORD status);
+static void CALLBACK fileio_call_completion_func (ULONG_PTR data);
+
+async_ops fileio_async_ops =
+{
+    fileio_get_async_status,       /* get_status */
+    fileio_set_async_status,       /* set_status */
+    fileio_get_async_count,        /* get_count */
+    fileio_call_completion_func    /* call_completion */
+};

 /***********************************************************************
  *              FILE_ConvertOFMode
@@ -1244,35 +1258,13 @@
     if(lpTransferred)
         *lpTransferred = lpOverlapped->InternalHigh;

-    SetLastError(lpOverlapped->Internal);
+    /* This is what the function should return accirding to MSDN specs */
+    if ( lpOverlapped->Internal == STATUS_PENDING )
+        SetLastError (ERROR_IO_INCOMPLETE);

     return (r==WAIT_OBJECT_0);
 }

-
-/***********************************************************************
- *              FILE_StartAsync                (INTERNAL)
- *
- * type==ASYNC_TYPE_NONE means cancel the indicated overlapped operation
- * lpOverlapped==NULL means all overlappeds match
- */
-BOOL FILE_StartAsync(HANDLE hFile, LPOVERLAPPED lpOverlapped, DWORD type, DWORD count, DWORD status)
-{
-    BOOL ret;
-    SERVER_START_REQ(register_async)
-    {
-        req->handle = hFile;
-        req->overlapped = lpOverlapped;
-        req->type = type;
-        req->count = count;
-        req->func = check_async_list;
-        req->status = status;
-        ret = wine_server_call( req );
-    }
-    SERVER_END_REQ;
-    return !ret;
-}
-
 /***********************************************************************
  *             CancelIo                   (KERNEL32.@)
  */
@@ -1282,21 +1274,60 @@

     TRACE("handle = %x\n",handle);

-    ovp = NtCurrentTeb()->pending_list;
-    while(ovp)
+
+    for (ovp = NtCurrentTeb()->pending_list; ovp; ovp = t)
     {
         t = ovp->next;
-        if(FILE_StartAsync(handle, ovp->lpOverlapped, ovp->type, 0, STATUS_CANCELLED))
+        if (ovp->handle != handle) continue;
+
+        SERVER_START_REQ ( cancel_async )
         {
-            TRACE("overlapped = %p\n",ovp->lpOverlapped);
-            finish_async(ovp, STATUS_CANCELLED);
+            req->handle = handle;
+            req->type = ovp->type;
+            req->overlapped = ovp;
+            wine_server_call ( req );
+            if (reply->cancelled)
+            {
+                TRACE ("cancelling request: %p\n", ovp);
+                ovp->ops->set_status ( ovp, STATUS_CANCELLED );
+                finish_async (ovp);
+            }
         }
-        ovp = t;
+        SERVER_END_REQ;
     }
     WaitForMultipleObjectsEx(0,NULL,FALSE,1,TRUE);
     return TRUE;
 }

+static DWORD fileio_get_async_status (async_private *ovp)
+{
+    return ((async_fileio*) ovp)->lpOverlapped->Internal;
+}
+
+static void fileio_set_async_status (async_private *ovp, DWORD status)
+{
+    ((async_fileio*) ovp)->lpOverlapped->Internal = status;
+}
+
+static DWORD fileio_get_async_count (async_private *ovp)
+{
+    async_fileio *fileio = (async_fileio*) ovp;
+    DWORD ret = fileio->count - fileio->lpOverlapped->InternalHigh;
+    return (ret < 0 ? 0 : ret);
+}
+
+static void CALLBACK fileio_call_completion_func (ULONG_PTR data)
+{
+    async_fileio *ovp = (async_fileio*) data;
+    TRACE ("data: %p\n", ovp);
+
+    if (ovp->completion_func)
+        ovp->completion_func(ovp->lpOverlapped->Internal,
+                             ovp->lpOverlapped->InternalHigh,
+                             ovp->lpOverlapped);
+    HeapFree(GetProcessHeap(), 0, ovp);
+}
+
 /***********************************************************************
  *             FILE_AsyncReadService      (INTERNAL)
  *
@@ -1305,18 +1336,19 @@
  */
 static void FILE_AsyncReadService(async_private *ovp)
 {
-    LPOVERLAPPED lpOverlapped = ovp->lpOverlapped;
+    async_fileio *fileio = (async_fileio*) ovp;
+    LPOVERLAPPED lpOverlapped = fileio->lpOverlapped;
     int result, r;
     int already = lpOverlapped->InternalHigh;

-    TRACE("%p %p\n", lpOverlapped, ovp->buffer );
+    TRACE("%p %p\n", lpOverlapped, fileio->buffer );

     /* check to see if the data is ready (non-blocking) */

-    result = pread (ovp->fd, &ovp->buffer[already], ovp->count - already,
+    result = pread (ovp->fd, &fileio->buffer[already], fileio->count - already,
                     OVERLAPPED_OFFSET (lpOverlapped) + already);
     if ((result < 0) && (errno == ESPIPE))
-        result = read (ovp->fd, &ovp->buffer[already], ovp->count - already);
+        result = read (ovp->fd, &fileio->buffer[already], fileio->count - already);

     if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR)))
     {
@@ -1334,9 +1366,9 @@
     }

     lpOverlapped->InternalHigh += result;
-    TRACE("read %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,ovp->count);
+    TRACE("read %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,fileio->count);

-    if(lpOverlapped->InternalHigh < ovp->count)
+    if(lpOverlapped->InternalHigh < fileio->count)
         r = STATUS_PENDING;
     else
         r = STATUS_SUCCESS;
@@ -1352,7 +1384,7 @@
 			 LPOVERLAPPED overlapped,
 			 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
 {
-    async_private *ovp;
+    async_fileio *ovp;
     int fd;

     TRACE("file %d to buf %p num %ld %p func %p\n",
@@ -1369,10 +1401,11 @@
     if(fd<0)
     {
         TRACE("Couldn't get FD\n");
+        SetLastError( ERROR_INVALID_HANDLE );
         return FALSE;
     }

-    ovp = (async_private *) HeapAlloc(GetProcessHeap(), 0, sizeof (async_private));
+    ovp = (async_fileio *) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio));
     if(!ovp)
     {
         TRACE("HeapAlloc Failed\n");
@@ -1380,30 +1413,23 @@
         close(fd);
         return FALSE;
     }
+
+    ovp->async.ops = &fileio_async_ops;
+    ovp->async.handle = hFile;
+    ovp->async.fd = fd;
+    ovp->async.type = ASYNC_TYPE_READ;
+    ovp->async.func = FILE_AsyncReadService;
+
     ovp->lpOverlapped = overlapped;
-    ovp->count = bytesToRead;
     ovp->completion_func = lpCompletionRoutine;
-    ovp->func = FILE_AsyncReadService;
     ovp->buffer = buffer;
-    ovp->fd = fd;
-    ovp->type = ASYNC_TYPE_READ;
-    ovp->handle = hFile;
-
-    /* hook this overlap into the pending async operation list */
-    ovp->next = NtCurrentTeb()->pending_list;
-    ovp->prev = NULL;
-    if(ovp->next)
-        ovp->next->prev = ovp;
-    NtCurrentTeb()->pending_list = ovp;
+    ovp->count = bytesToRead;

-    if ( !FILE_StartAsync(hFile, overlapped, ASYNC_TYPE_READ, bytesToRead, STATUS_PENDING) )
-    {
-        /* FIXME: remove async_private and release memory */
-        ERR("FILE_StartAsync failed\n");
-        return FALSE;
-    }
+    /* Tell register_async that this is a new request */
+    overlapped->Internal = STATUS_USER_APC;

-    return TRUE;
+    /* The request is hooked into the pending list by register_async() */
+    return register_async (&ovp->async);
 }

 /***********************************************************************
@@ -1413,7 +1439,6 @@
 			 LPOVERLAPPED overlapped,
 			 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
 {
-    overlapped->Internal     = STATUS_PENDING;
     overlapped->InternalHigh = 0;
     return FILE_ReadFileEx(hFile,buffer,bytesToRead,overlapped,lpCompletionRoutine);
 }
@@ -1470,41 +1495,21 @@
             return FALSE;
         }

-        /* see if we can read some data already (this shouldn't block) */
-        result = pread( unix_handle, buffer, bytesToRead, OVERLAPPED_OFFSET(overlapped) );
-        if ((result < 0) && (errno == ESPIPE))
-            result = read( unix_handle, buffer, bytesToRead );
         close(unix_handle);
-
-        if(result<0)
-        {
-            if( (errno!=EAGAIN) && (errno!=EINTR) &&
-                ((errno != EFAULT) || IsBadWritePtr( buffer, bytesToRead )) )
-            {
-                FILE_SetDosError();
-                return FALSE;
-            }
-            else
-                result = 0;
-        }
+        overlapped->InternalHigh = 0;

-        /* if we read enough to keep the app happy, then return now */
-        if(result>=bytesToRead)
-        {
-            *bytesRead = result;
-            return TRUE;
-        }
+        if(!FILE_ReadFileEx(hFile, buffer, bytesToRead, overlapped, FILE_OverlappedComplete))
+            return FALSE;

-        /* at last resort, do an overlapped read */
-        overlapped->Internal     = STATUS_PENDING;
-        overlapped->InternalHigh = result;

-        if(!FILE_ReadFileEx(hFile, buffer, bytesToRead, overlapped, FILE_OverlappedComplete))
+        if ( !GetOverlappedResult (hFile, overlapped, bytesRead, FALSE) )
+        {
+            if ( GetLastError() == ERROR_IO_INCOMPLETE )
+                SetLastError ( ERROR_IO_PENDING );
             return FALSE;
+        }

-        /* fail on return, with ERROR_IO_PENDING */
-        SetLastError(ERROR_IO_PENDING);
-        return FALSE;
+        return TRUE;
     }

     else if ( FD_TYPE (type) == FD_TYPE_CONSOLE )
@@ -1554,18 +1559,19 @@
  */
 static void FILE_AsyncWriteService(struct async_private *ovp)
 {
-    LPOVERLAPPED lpOverlapped = ovp->lpOverlapped;
+    async_fileio *fileio = (async_fileio *) ovp;
+    LPOVERLAPPED lpOverlapped = fileio->lpOverlapped;
     int result, r;
     int already = lpOverlapped->InternalHigh;

-    TRACE("(%p %p)\n",lpOverlapped,ovp->buffer);
+    TRACE("(%p %p)\n",lpOverlapped,fileio->buffer);

     /* write some data (non-blocking) */

-    result = pwrite(ovp->fd, &ovp->buffer[already], ovp->count - already,
+    result = pwrite(ovp->fd, &fileio->buffer[already], fileio->count - already,
                     OVERLAPPED_OFFSET (lpOverlapped) + already);
     if ((result < 0) && (errno == ESPIPE))
-        result = write(ovp->fd, &ovp->buffer[already], ovp->count - already);
+        result = write(ovp->fd, &fileio->buffer[already], fileio->count - already);

     if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR)))
     {
@@ -1582,9 +1588,9 @@

     lpOverlapped->InternalHigh += result;

-    TRACE("wrote %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,ovp->count);
+    TRACE("wrote %d more bytes %ld/%d so far\n",result,lpOverlapped->InternalHigh,fileio->count);

-    if(lpOverlapped->InternalHigh < ovp->count)
+    if(lpOverlapped->InternalHigh < fileio->count)
         r = STATUS_PENDING;
     else
         r = STATUS_SUCCESS;
@@ -1600,7 +1606,8 @@
 			 LPOVERLAPPED overlapped,
 			 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
 {
-    async_private *ovp;
+    async_fileio *ovp;
+    int fd;

     TRACE("file %d to buf %p num %ld %p func %p stub\n",
 	  hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
@@ -1611,45 +1618,39 @@
         return FALSE;
     }

-    overlapped->Internal     = STATUS_PENDING;
-    overlapped->InternalHigh = 0;
-
-    if (!FILE_StartAsync(hFile, overlapped, ASYNC_TYPE_WRITE, bytesToWrite, STATUS_PENDING ))
+    fd = FILE_GetUnixHandle( hFile, GENERIC_WRITE );
+    if(fd<0)
     {
-        TRACE("FILE_StartAsync failed\n");
+        TRACE("Couldn't get FD\n");
+        SetLastError( ERROR_INVALID_HANDLE );
         return FALSE;
     }

-    ovp = (async_private*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_private));
+    ovp = (async_fileio*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio));
     if(!ovp)
     {
         TRACE("HeapAlloc Failed\n");
         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        close(fd);
         return FALSE;
     }
+
+    ovp->async.ops = &fileio_async_ops;
+    ovp->async.handle = hFile;
+    ovp->async.fd = FILE_GetUnixHandle( hFile, GENERIC_WRITE );
+    ovp->async.type = ASYNC_TYPE_WRITE;
+    ovp->async.func = FILE_AsyncWriteService;
+
     ovp->lpOverlapped = overlapped;
-    ovp->func = FILE_AsyncWriteService;
     ovp->buffer = (LPVOID) buffer;
-    ovp->count = bytesToWrite;
     ovp->completion_func = lpCompletionRoutine;
-    ovp->fd = FILE_GetUnixHandle( hFile, GENERIC_WRITE );
-    ovp->type = ASYNC_TYPE_WRITE;
-    ovp->handle = hFile;
-
-    if(ovp->fd <0)
-    {
-        HeapFree(GetProcessHeap(), 0, ovp);
-        return FALSE;
-    }
+    ovp->count = bytesToWrite;

-    /* hook this overlap into the pending async operation list */
-    ovp->next = NtCurrentTeb()->pending_list;
-    ovp->prev = NULL;
-    if(ovp->next)
-        ovp->next->prev = ovp;
-    NtCurrentTeb()->pending_list = ovp;
+    /* Tell register_async that this is a new request */
+    overlapped->Internal = STATUS_USER_APC;

-    return TRUE;
+    /* The request is hooked into the pending list by register_async() */
+    return register_async (&ovp->async);
 }

 /***********************************************************************
@@ -1659,7 +1660,6 @@
 			 LPOVERLAPPED overlapped,
 			 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
 {
-    overlapped->Internal     = STATUS_PENDING;
     overlapped->InternalHigh = 0;

     return FILE_WriteFileEx(hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
@@ -1693,44 +1693,20 @@
             return FALSE;
         }

-        /* see if we can write some data already (this shouldn't block) */
-
-        result = pwrite( unix_handle, buffer, bytesToWrite, OVERLAPPED_OFFSET (overlapped) );
-        if ((result < 0) && (errno == ESPIPE))
-            result = write( unix_handle, buffer, bytesToWrite );
-
         close(unix_handle);
-
-        if(result<0)
-        {
-            if( (errno!=EAGAIN) && (errno!=EINTR) &&
-                ((errno != EFAULT) || IsBadReadPtr( buffer, bytesToWrite )) )
-            {
-                FILE_SetDosError();
-                return FALSE;
-            }
-            else
-                result = 0;
-        }
-
-        /* if we wrote enough to keep the app happy, then return now */
-        if(result>=bytesToWrite)
-        {
-            *bytesWritten = result;
-            return TRUE;
-        }
-
-        /* at last resort, do an overlapped read */
-        overlapped->Internal     = STATUS_PENDING;
-        overlapped->InternalHigh = result;
+        overlapped->InternalHigh = 0;

-        if(!FILE_WriteFileEx(hFile, buffer, bytesToWrite, overlapped, FILE_OverlappedComplete))
+        if (!FILE_WriteFileEx(hFile, buffer, bytesToWrite, overlapped, FILE_OverlappedComplete))
             return FALSE;
+
+        if ( !GetOverlappedResult (hFile, overlapped, bytesWritten, FALSE) )
+        {
+            if ( GetLastError() == ERROR_IO_INCOMPLETE )
+                SetLastError ( ERROR_IO_PENDING );
+            return FALSE;
+        }

-        /* fail on return, with ERROR_IO_PENDING */
-        SetLastError(ERROR_IO_PENDING);
-        return FALSE;
-
+        return TRUE;
     }

     else if ( FD_TYPE (type) == FD_TYPE_CONSOLE )
diff -ruX diffignore CVS/wine/include/file.h MW/wine/include/file.h
--- CVS/wine/include/file.h	Wed Jan  9 21:27:50 2002
+++ MW/wine/include/file.h	Wed Jan  9 20:25:05 2002
@@ -33,23 +33,46 @@

 /* overlapped private structure */
 struct async_private;
-typedef void (*async_handler)(struct async_private *ovp);
+
+typedef void (*async_handler) (struct async_private *ovp);
+typedef void CALLBACK (*async_call_completion_func) (ULONG_PTR data);
+typedef DWORD (*async_get_status) (struct async_private *ovp);
+typedef DWORD (*async_get_count) (struct async_private *ovp);
+typedef void (*async_set_status) (struct async_private *ovp, DWORD status);
+
+typedef struct async_ops
+{
+    async_get_status            get_status;
+    async_set_status            set_status;
+    async_get_count             get_count;
+    async_call_completion_func  call_completion;
+} async_ops;
+
+extern async_ops fileio_async_ops;
+
 typedef struct async_private
 {
-     LPOVERLAPPED  lpOverlapped;
-     HANDLE        handle;
-     int           fd;
-     char         *buffer;
-     async_handler func;
-     int           count;
-     int           type;
-     LPOVERLAPPED_COMPLETION_ROUTINE completion_func;
-     struct async_private *next;
-     struct async_private *prev;
+    async_ops                        *ops;
+    HANDLE                           handle;
+    int                              fd;
+    int                              type;
+    async_handler                    func;
+    struct async_private             *next;
+    struct async_private             *prev;
 } async_private;

-extern void WINAPI check_async_list(LPOVERLAPPED ov, DWORD status);
-extern void finish_async(struct async_private *ovp, DWORD status);
+typedef struct async_fileio
+{
+    async_private                    async;
+    LPOVERLAPPED                     lpOverlapped;
+    LPOVERLAPPED_COMPLETION_ROUTINE  completion_func;
+    char                             *buffer;
+    int                              count;
+} async_fileio;
+
+extern BOOL register_async (async_private *ovp);
+extern void WINAPI check_async_list(async_private *ovp, DWORD status);
+extern void finish_async(async_private *ovp);

 /* locale-independent case conversion */
 inline static char FILE_tolower( char c )
@@ -83,7 +106,6 @@
                                DWORD attributes, HANDLE template, BOOL fail_read_only,
                                UINT drive_type );
 extern HANDLE FILE_CreateDevice( int client_id, DWORD access, LPSECURITY_ATTRIBUTES sa );
-extern BOOL FILE_StartAsync(HANDLE handle, LPOVERLAPPED lpOverlapped, DWORD type, DWORD count, DWORD status);

 extern LONG WINAPI WIN16_hread(HFILE16,SEGPTR,LONG);

diff -ruX diffignore CVS/wine/scheduler/synchro.c MW/wine/scheduler/synchro.c
--- CVS/wine/scheduler/synchro.c	Tue Jan  8 18:53:41 2002
+++ MW/wine/scheduler/synchro.c	Wed Jan  9 20:27:16 2002
@@ -36,27 +36,60 @@
     }
 }

-static void CALLBACK call_completion_routine(ULONG_PTR data)
+/***********************************************************************
+ *           register_async  (INTERNAL)
+ *
+ * Manipulate async request queues.
+ */
+BOOL register_async (async_private *ovp)
 {
-    async_private* ovp = (async_private*)data;
+    BOOL ret;
+    DWORD status = ovp->ops->get_status (ovp);

-    ovp->completion_func(ovp->lpOverlapped->Internal,
-                         ovp->lpOverlapped->InternalHigh,
-                         ovp->lpOverlapped);
-    ovp->completion_func=NULL;
-    HeapFree(GetProcessHeap(), 0, ovp);
-}
+    /* STATUS_USER_APC means this is a new request */
+    /* STATUS_PENDING means this request is rescheduled (IO incomplete) */

-void finish_async(async_private *ovp, DWORD status)
-{
-    ovp->lpOverlapped->Internal=status;
+    if (status == STATUS_USER_APC)
+    {
+        status = STATUS_PENDING;
+        ovp->ops->set_status (ovp, status);
+
+        /* hook this overlap into the pending async operation list */
+        ovp->next = NtCurrentTeb()->pending_list;
+        ovp->prev = NULL;
+        if (ovp->next) ovp->next->prev = ovp;
+        NtCurrentTeb()->pending_list = ovp;
+    }

-    /* call ReadFileEx/WriteFileEx's overlapped completion function */
-    if(ovp->completion_func)
+    /* The server call will destroy all except PENDING requests */
+    SERVER_START_REQ(register_async)
     {
-        QueueUserAPC(call_completion_routine,GetCurrentThread(),(ULONG_PTR)ovp);
+        req->handle = ovp->handle;
+        req->overlapped = ovp;
+        req->type = ovp->type;
+        req->count = ovp->ops->get_count (ovp);
+        req->func = check_async_list;
+        req->status = status;
+        ret = wine_server_call( req );
     }
+    SERVER_END_REQ;
+
+    if (ret) ovp->ops->set_status ( ovp, GetLastError() );
+
+    if ( ovp->ops->get_status (ovp) != STATUS_PENDING )
+        finish_async (ovp);

+    return !ret;
+}
+
+/***********************************************************************
+ *           finish_async  (INTERNAL)
+ *
+ * Called after completion or cancellation of an async request.
+ * Note: The completion status must be set already.
+ */
+void finish_async(async_private *ovp)
+{
     /* remove it from the active list */
     if(ovp->prev)
         ovp->prev->next = ovp->next;
@@ -70,7 +103,9 @@
     ovp->prev=NULL;

     close(ovp->fd);
-    if(!ovp->completion_func) HeapFree(GetProcessHeap(), 0, ovp);
+
+    /* Queue completion function unconditionally */
+    QueueUserAPC(ovp->ops->call_completion, GetCurrentThread(), (ULONG_PTR)ovp);
 }

 /***********************************************************************
@@ -78,30 +113,33 @@
  *
  * Process a status event from the server.
  */
-void WINAPI check_async_list(LPOVERLAPPED overlapped, DWORD status)
+void WINAPI check_async_list(async_private *asp, DWORD status)
 {
-    async_private *ovp;
+    async_private *ovp = NULL;
+    DWORD ovp_status;

     /* fprintf(stderr,"overlapped %p status %x\n",overlapped,status); */

-    for(ovp = NtCurrentTeb()->pending_list; ovp; ovp = ovp->next)
-        if(ovp->lpOverlapped == overlapped)
-            break;
+    for( ovp = NtCurrentTeb()->pending_list; ovp && ovp != asp; ovp = ovp->next );

-    if(!ovp)
-            return;
+    if(!ovp) return;

     if(status != STATUS_ALERTED)
-        ovp->lpOverlapped->Internal = status;
+    {
+        ovp_status = status;
+        ovp->ops->set_status (ovp, status);
+    }
+    else ovp_status = ovp->ops->get_status (ovp);

-    if(ovp->lpOverlapped->Internal==STATUS_PENDING)
-        {
+    if( ovp_status == STATUS_PENDING)
+    {
         ovp->func(ovp);
-        FILE_StartAsync(ovp->handle, ovp->lpOverlapped, ovp->type, 0, ovp->lpOverlapped->Internal);
-        }
+        register_async (ovp);
+        ovp_status = ovp->ops->get_status (ovp);
+    }

-    if(ovp->lpOverlapped->Internal!=STATUS_PENDING)
-        finish_async(ovp,ovp->lpOverlapped->Internal);
+    if( ovp_status != STATUS_PENDING )
+        finish_async(ovp);
 }


diff -ruX diffignore CVS/wine/server/async.c MW/wine/server/async.c
--- CVS/wine/server/async.c	Wed Jan  2 12:50:04 2002
+++ MW/wine/server/async.c	Wed Jan  9 20:04:53 2002
@@ -177,3 +177,28 @@
     release_object(obj);
 }

+DECL_HANDLER(cancel_async)
+{
+    struct object *obj;
+
+    if (!(obj = get_handle_obj( current->process, req->handle, 0, NULL)) )
+        return;
+
+    if(obj->ops->queue_async)
+    {
+        struct async_queue *q = obj->ops->queue_async(obj, NULL, req->type, 0);
+        struct async *async;
+
+        async = find_async(q, current, req->overlapped);
+        if (async)
+        {
+            destroy_async(async);
+            reply->cancelled = 1;
+            set_select_events(obj,obj->ops->get_poll_events(obj));
+        }
+        else reply->cancelled = 0;
+    }
+
+    release_object(obj);
+}
+
diff -ruX diffignore CVS/wine/server/protocol.def MW/wine/server/protocol.def
--- CVS/wine/server/protocol.def	Tue Jan  8 18:58:28 2002
+++ MW/wine/server/protocol.def	Wed Jan  9 19:52:33 2002
@@ -1535,7 +1535,7 @@
 #define SERIALINFO_SET_ERROR     0x04


-/* Create/Destroy an async I/O */
+/* Create / reschedule an async I/O */
 @REQ(register_async)
     handle_t     handle;  /* handle to comm port, socket or file */
     void*        func;
@@ -1549,6 +1549,14 @@
 #define ASYNC_TYPE_WRITE 0x02
 #define ASYNC_TYPE_WAIT  0x03

+/* Cancel an async I/O */
+ at REQ(cancel_async)
+    handle_t     handle;
+    int          type;
+    void*        overlapped;
+ at REPLY
+    int          cancelled;
+ at END

 /* Create a named pipe */
 @REQ(create_named_pipe)





More information about the wine-devel mailing list