Personal tools
You are here: Home Members tiran's Home completion-
Document Actions

completion-

by Christian Heimes last modified 2007-06-16 15:20

Andrey Turkin's IOCompletionPort patch Modified by Christian Heimes for GIT master (git clone git://source.winehq.org/git/wine.git wine)

Click here to get the file

Size 39.0 kB - File type text/plain

File contents

diff --git a/dlls/kernel32/sync.c b/dlls/kernel32/sync.c
index 07bd9b0..b9c51d4 100644
--- a/dlls/kernel32/sync.c
+++ b/dlls/kernel32/sync.c
@@ -1328,13 +1328,18 @@ BOOL WINAPI ConnectNamedPipe(HANDLE hPipe, LPOVERLAPPED overlapped)
 {
     NTSTATUS status;
     IO_STATUS_BLOCK status_block;
+    PVOID apc_context = NULL;
 
     TRACE("(%p,%p)\n", hPipe, overlapped);
 
     if(overlapped)
+    {
         overlapped->Internal = STATUS_PENDING;
+        if (!(1 & (ULONG_PTR)overlapped->hEvent))
+            apc_context = (PVOID)overlapped; /* yes, native does just the same! */
+    }
 
-    status = NtFsControlFile(hPipe, overlapped ? overlapped->hEvent : NULL, NULL, NULL,
+    status = NtFsControlFile(hPipe, overlapped ? overlapped->hEvent : NULL, NULL, apc_context,
                              overlapped ? (IO_STATUS_BLOCK *)overlapped : &status_block,
                              FSCTL_PIPE_LISTEN, NULL, 0, NULL, 0);
 
diff --git a/dlls/kernel32/vxd.c b/dlls/kernel32/vxd.c
index 875aeed..2589c73 100644
--- a/dlls/kernel32/vxd.c
+++ b/dlls/kernel32/vxd.c
@@ -348,14 +348,15 @@ BOOL WINAPI DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode,
 
     if (lpOverlapped)
     {
+        PVOID apc_context = (1 & (ULONG_PTR)lpOverlapped->hEvent)?NULL:(PVOID)lpOverlapped;
         if (HIWORD(dwIoControlCode) == FILE_DEVICE_FILE_SYSTEM)
             status = NtFsControlFile(hDevice, lpOverlapped->hEvent,
-                                     NULL, NULL, (PIO_STATUS_BLOCK)lpOverlapped,
+                                     NULL, apc_context, (PIO_STATUS_BLOCK)lpOverlapped,
                                      dwIoControlCode, lpvInBuffer, cbInBuffer,
                                      lpvOutBuffer, cbOutBuffer);
         else
             status = NtDeviceIoControlFile(hDevice, lpOverlapped->hEvent,
-                                           NULL, NULL, (PIO_STATUS_BLOCK)lpOverlapped,
+                                           NULL, apc_context, (PIO_STATUS_BLOCK)lpOverlapped,
                                            dwIoControlCode, lpvInBuffer, cbInBuffer,
                                            lpvOutBuffer, cbOutBuffer);
         if (lpcbBytesReturned) *lpcbBytesReturned = lpOverlapped->InternalHigh;
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c
index de779f2..5029165 100644
--- a/dlls/ntdll/file.c
+++ b/dlls/ntdll/file.c
@@ -378,6 +378,7 @@ static NTSTATUS FILE_AsyncReadService(void *user, PIO_STATUS_BLOCK iosb, NTSTATU
     }
     if (status != STATUS_PENDING)
     {
+        NTDLL_FireCompletion( fileio->io.handle, (ULONG_PTR) iosb, status, fileio->already );
         iosb->u.Status = status;
         iosb->Information = fileio->already;
     }
@@ -584,6 +585,7 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile, HANDLE hEvent,
     {
         if ((result = read( unix_handle, (char *)buffer + total, length - total )) >= 0)
         {
+            TRACE("read %d bytes\n", result);
             total += result;
             if (!result || total == length)
             {
@@ -604,6 +606,7 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile, HANDLE hEvent,
             }
         }
 
+        TRACE("Going to asynchronous part\n");
         if (!(options & (FILE_SYNCHRONOUS_IO_ALERT | FILE_SYNCHRONOUS_IO_NONALERT)))
         {
             async_fileio_read *fileio;
@@ -630,6 +633,7 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile, HANDLE hEvent,
             fileio->buffer = buffer;
             fileio->avail_mode = avail_mode;
 
+            TRACE("register_async!\n");
             SERVER_START_REQ( register_async )
             {
                 req->handle = hFile;
@@ -652,6 +656,7 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile, HANDLE hEvent,
             struct pollfd pfd;
             int ret, timeout;
 
+            TRACE("go to synchronous\n");
             if (!timeout_init_done)
             {
                 timeout_init_done = 1;
@@ -683,6 +688,8 @@ NTSTATUS WINAPI NtReadFile(HANDLE hFile, HANDLE hEvent,
 
 done:
     if (needs_close) close( unix_handle );
+    TRACE("Going out: %x\n", status);
+    if (offset && status != STATUS_PENDING) NTDLL_FireCompletion( hFile, (ULONG_PTR) io_status, status, total );
     if (status == STATUS_SUCCESS)
     {
         io_status->u.Status = status;
@@ -746,6 +753,7 @@ static NTSTATUS FILE_AsyncWriteService(void *user, IO_STATUS_BLOCK *iosb, NTSTAT
     }
     if (status != STATUS_PENDING)
     {
+        NTDLL_FireCompletion( fileio->io.handle, (ULONG_PTR) iosb, status, fileio->already );
         iosb->u.Status = status;
         iosb->Information = fileio->already;
     }
@@ -913,6 +921,7 @@ NTSTATUS WINAPI NtWriteFile(HANDLE hFile, HANDLE hEvent,
 
 done:
     if (needs_close) close( unix_handle );
+    if (offset && status != STATUS_PENDING) NTDLL_FireCompletion( hFile, (ULONG_PTR) io_status, status, total );
     if (status == STATUS_SUCCESS)
     {
         io_status->u.Status = status;
@@ -957,7 +966,10 @@ static NTSTATUS ioctl_completion( void *arg, IO_STATUS_BLOCK *io, NTSTATUS statu
         }
         SERVER_END_REQ;
     }
-    if (status != STATUS_PENDING) io->u.Status = status;
+    if (status != STATUS_PENDING) {
+        io->u.Status = status;
+        NTDLL_FireCompletion( async->handle, 0 /* FIXME howto? */, status, io->Information );
+    }
     return status;
 }
 
@@ -1083,7 +1095,11 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event,
                                     in_buffer, in_size, out_buffer, out_size );
         break;
     }
-    if (status != STATUS_PENDING) io->u.Status = status;
+    if (status != STATUS_PENDING) 
+    {
+        io->u.Status = status;
+        if (apc_context) NTDLL_FireCompletion( handle, (ULONG_PTR)apc_context, status, io->Information );
+    }
     return status;
 }
 
@@ -1213,7 +1229,11 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc
         break;
     }
 
-    if (status != STATUS_PENDING) io->u.Status = status;
+    if (status != STATUS_PENDING) 
+    {
+        io->u.Status = status;
+        if (apc_context) NTDLL_FireCompletion( handle, (ULONG_PTR)apc_context, status, io->Information );
+    }
     return status;
 }
 
@@ -1665,6 +1685,22 @@ NTSTATUS WINAPI NtSetInformationFile(HANDLE handle, PIO_STATUS_BLOCK io,
         }
         break;
 
+    case FileCompletionInformation:
+        if (len >= sizeof(FILE_COMPLETION_INFORMATION))
+        {
+            FILE_COMPLETION_INFORMATION *info = (FILE_COMPLETION_INFORMATION *)ptr;
+
+            SERVER_START_REQ( set_completion_info )
+            {
+                req->handle  = handle;
+                req->chandle = info->CompletionPort;
+                req->ckey    = info->CompletionKey;
+                io->u.Status = wine_server_call( req );
+            }
+            SERVER_END_REQ;
+        } else io->u.Status = STATUS_INVALID_PARAMETER_3;
+        break;
+
     default:
         FIXME("Unsupported class (%d)\n", class);
         io->u.Status = STATUS_NOT_IMPLEMENTED;
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index c7eb5ed..f7e9a93 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -181,4 +181,7 @@ static inline struct ntdll_thread_regs *ntdll_get_thread_regs(void)
     return (struct ntdll_thread_regs *)NtCurrentTeb()->SpareBytes1;
 }
 
+/* Completion */
+extern NTSTATUS NTDLL_FireCompletion( HANDLE hFile, ULONG_PTR CompletionValue, NTSTATUS Status, DWORD Information );
+
 #endif
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index eb4e16d..bd58567 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -1025,9 +1025,32 @@ NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeou
 NTSTATUS WINAPI NtCreateIoCompletion( PHANDLE CompletionPort, ACCESS_MASK DesiredAccess,
                                       POBJECT_ATTRIBUTES ObjectAttributes, ULONG NumberOfConcurrentThreads )
 {
-    FIXME("(%p, %x, %p, %d)\n", CompletionPort, DesiredAccess,
+    NTSTATUS status;
+    HANDLE port;
+
+    TRACE("(%p, %x, %p, %d)\n", CompletionPort, DesiredAccess,
           ObjectAttributes, NumberOfConcurrentThreads);
-    return STATUS_NOT_IMPLEMENTED;
+
+    if (!CompletionPort)
+        return STATUS_INVALID_PARAMETER;
+
+    SERVER_START_REQ( create_completion )
+    {
+        req->access     = DesiredAccess;
+        req->attributes = ObjectAttributes ? ObjectAttributes->Attributes : 0;
+        req->rootdir    = ObjectAttributes ? ObjectAttributes->RootDirectory : NULL;
+        req->concurrent = NumberOfConcurrentThreads;
+        if (ObjectAttributes && ObjectAttributes->ObjectName)
+            wine_server_add_data( req, ObjectAttributes->ObjectName->Buffer,
+                                       ObjectAttributes->ObjectName->Length );
+        status = wine_server_call( req );
+        port = reply->handle;
+    }
+    SERVER_END_REQ;
+    
+    if (status == STATUS_SUCCESS)
+        *CompletionPort = port;
+    return status;
 }
 
 /******************************************************************
@@ -1045,11 +1068,24 @@ NTSTATUS WINAPI NtCreateIoCompletion( PHANDLE CompletionPort, ACCESS_MASK Desire
  */
 NTSTATUS WINAPI NtSetIoCompletion( HANDLE CompletionPort, ULONG_PTR CompletionKey,
                                    ULONG_PTR CompletionValue, NTSTATUS Status,
-                                   ULONG NumberOfBytesToTransfer )
+                                   ULONG NumberOfBytesTransferred )
 {
-    FIXME("(%p, %lx, %lx, %x, %d)\n", CompletionPort, CompletionKey,
-          CompletionValue, Status, NumberOfBytesToTransfer);
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS status;
+
+    TRACE("(%p, %lx, %lx, %x, %d)\n", CompletionPort, CompletionKey,
+          CompletionValue, Status, NumberOfBytesTransferred);
+
+    SERVER_START_REQ( insert_completion )
+    {
+        req->handle      = CompletionPort;
+        req->ckey        = CompletionKey;
+        req->cvalue      = CompletionValue;
+        req->status      = Status;
+        req->information = NumberOfBytesTransferred;
+        status = wine_server_call( req );
+    }
+    SERVER_END_REQ;
+    return status;
 }
 
 /******************************************************************
@@ -1070,9 +1106,41 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE CompletionPort, PULONG_PTR Completi
                                       PULONG_PTR CompletionValue, PIO_STATUS_BLOCK iosb,
                                       PLARGE_INTEGER WaitTime )
 {
-    FIXME("(%p, %p, %p, %p, %p)\n", CompletionPort, CompletionKey,
+    NTSTATUS status;
+    ULONG_PTR ckey, cvalue;
+    ULONG_PTR information;
+    NTSTATUS iosb_status;
+
+    TRACE("(%p, %p, %p, %p, %p)\n", CompletionPort, CompletionKey,
           CompletionValue, iosb, WaitTime);
-    return STATUS_NOT_IMPLEMENTED;
+
+    for(;;)
+    {
+        SERVER_START_REQ( remove_completion )
+        {
+            req->handle = CompletionPort;
+            status = wine_server_call( req );
+            ckey        = reply->ckey;
+            cvalue      = reply->cvalue;
+            information = reply->information;
+            iosb_status = reply->status;
+        }
+        SERVER_END_REQ;
+        if (status != STATUS_PENDING) break;
+
+        status = NTDLL_wait_for_multiple_objects( 1, &CompletionPort, SELECT_INTERRUPTIBLE | SELECT_ALERTABLE, WaitTime, 0 ); /* FIXME should decrease timeout in case of restart */
+        if (status != WAIT_OBJECT_0) break;
+    }
+
+    if (status == STATUS_SUCCESS)
+    {
+        *CompletionKey = ckey;
+        *CompletionValue = cvalue;
+        iosb->Information = information;
+        iosb->u.Status = iosb_status;
+    }
+
+    return status;
 }
 
 /******************************************************************
@@ -1090,8 +1158,28 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE CompletionPort, PULONG_PTR Completi
 NTSTATUS WINAPI NtOpenIoCompletion( PHANDLE CompletionPort, ACCESS_MASK DesiredAccess,
                                     POBJECT_ATTRIBUTES ObjectAttributes )
 {
-    FIXME("(%p, 0x%x, %p)\n", CompletionPort, DesiredAccess, ObjectAttributes);
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS status;
+    HANDLE port;
+
+    TRACE("(%p, 0x%x, %p)\n", CompletionPort, DesiredAccess, ObjectAttributes);
+
+    if (!CompletionPort || !ObjectAttributes || !ObjectAttributes->ObjectName)
+        return STATUS_INVALID_PARAMETER;
+
+    SERVER_START_REQ( open_completion )
+    {
+        req->access     = DesiredAccess;
+        req->rootdir    = ObjectAttributes->RootDirectory;
+        wine_server_add_data( req, ObjectAttributes->ObjectName->Buffer,
+                                   ObjectAttributes->ObjectName->Length );
+        status = wine_server_call( req );
+        port = reply->handle;
+    }
+    SERVER_END_REQ;
+    
+    if (status == STATUS_SUCCESS)
+        *CompletionPort = port;
+    return status;
 }
 
 /******************************************************************
@@ -1111,7 +1199,59 @@ NTSTATUS WINAPI NtOpenIoCompletion( PHANDLE CompletionPort, ACCESS_MASK DesiredA
 NTSTATUS WINAPI NtQueryIoCompletion( HANDLE CompletionPort, IO_COMPLETION_INFORMATION_CLASS InformationClass,
                                      PVOID CompletionInformation, ULONG BufferLength, PULONG RequiredLength )
 {
-    FIXME("(%p, %d, %p, 0x%x, %p)\n", CompletionPort, InformationClass, CompletionInformation,
+    NTSTATUS status;
+
+    TRACE("(%p, %d, %p, 0x%x, %p)\n", CompletionPort, InformationClass, CompletionInformation,
             BufferLength, RequiredLength);
-    return STATUS_NOT_IMPLEMENTED;
+
+    if (!CompletionInformation) return STATUS_INVALID_PARAMETER;
+    switch( InformationClass )
+    {
+        case IoCompletionBasicInformation:
+            {
+                ULONG info;
+
+                if (RequiredLength) *RequiredLength = sizeof(info);
+                if (BufferLength != sizeof(info))
+                    status = STATUS_INFO_LENGTH_MISMATCH;
+                else 
+                {
+                    SERVER_START_REQ( query_completion )
+                    {
+                        req->handle = CompletionPort;
+                        status = wine_server_call( req );
+                        info = reply->depth;
+                    }
+                    SERVER_END_REQ;
+                }
+                if (status == STATUS_SUCCESS) 
+                {
+                    *(ULONG*)CompletionInformation = info;
+                    TRACE("got %d\n", info );
+                }
+            }
+            break;
+        default:
+            status = STATUS_INVALID_PARAMETER;
+            break;
+    }
+    return status;
+}
+
+NTSTATUS NTDLL_FireCompletion( HANDLE hFile, ULONG_PTR CompletionValue, NTSTATUS Status, DWORD Information )
+{
+    NTSTATUS status;
+
+    TRACE( "(%p, %lx, %x, %d)\n", hFile, CompletionValue, Status, Information );
+
+    SERVER_START_REQ( fire_completion )
+    {
+        req->handle      = hFile;
+        req->cvalue      = CompletionValue;
+        req->status      = Status;
+        req->information = Information;
+        status = wine_server_call( req );
+    }
+    SERVER_END_REQ;
+    return status;
 }
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index 56574b0..ad7b4d6 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -4066,6 +4066,106 @@ struct make_process_system_reply
 };
 
 
+struct create_completion_request
+{
+    struct request_header __header;
+    unsigned int access;
+    unsigned int attributes;
+    unsigned int concurrent;
+    obj_handle_t rootdir;
+    /* VARARG(filename,string); */
+};
+struct create_completion_reply
+{
+    struct reply_header __header;
+    obj_handle_t handle;
+};
+
+
+struct open_completion_request
+{
+    struct request_header __header;
+    unsigned int access;
+    unsigned int attributes;
+    obj_handle_t rootdir;
+    /* VARARG(filename,string); */
+};
+struct open_completion_reply
+{
+    struct reply_header __header;
+    obj_handle_t handle;
+};
+
+
+struct insert_completion_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+    unsigned long ckey;
+    unsigned long cvalue;
+    unsigned int  status;
+    unsigned int information;
+};
+struct insert_completion_reply
+{
+    struct reply_header __header;
+};
+
+
+struct remove_completion_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+};
+struct remove_completion_reply
+{
+    struct reply_header __header;
+    unsigned long ckey;
+    unsigned long cvalue;
+    unsigned int  status;
+    unsigned int information;
+};
+
+
+struct query_completion_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+};
+struct query_completion_reply
+{
+    struct reply_header __header;
+    unsigned long depth;
+};
+
+
+struct set_completion_info_request
+{
+    struct request_header __header;
+    obj_handle_t  handle;
+    obj_handle_t  chandle;
+    unsigned long ckey;
+};
+struct set_completion_info_reply
+{
+    struct reply_header __header;
+};
+
+
+struct fire_completion_request
+{
+    struct request_header __header;
+    obj_handle_t handle;
+    unsigned long cvalue;
+    unsigned int status;
+    unsigned int information;
+};
+struct fire_completion_reply
+{
+    struct reply_header __header;
+};
+
+
 enum request
 {
     REQ_new_process,
@@ -4288,6 +4388,13 @@ enum request
     REQ_delete_device,
     REQ_get_next_device_request,
     REQ_make_process_system,
+    REQ_create_completion,
+    REQ_open_completion,
+    REQ_insert_completion,
+    REQ_remove_completion,
+    REQ_query_completion,
+    REQ_set_completion_info,
+    REQ_fire_completion,
     REQ_NB_REQUESTS
 };
 
@@ -4515,6 +4622,13 @@ union generic_request
     struct delete_device_request delete_device_request;
     struct get_next_device_request_request get_next_device_request_request;
     struct make_process_system_request make_process_system_request;
+    struct create_completion_request create_completion_request;
+    struct open_completion_request open_completion_request;
+    struct insert_completion_request insert_completion_request;
+    struct remove_completion_request remove_completion_request;
+    struct query_completion_request query_completion_request;
+    struct set_completion_info_request set_completion_info_request;
+    struct fire_completion_request fire_completion_request;
 };
 union generic_reply
 {
@@ -4740,8 +4854,15 @@ union generic_reply
     struct delete_device_reply delete_device_reply;
     struct get_next_device_request_reply get_next_device_request_reply;
     struct make_process_system_reply make_process_system_reply;
+    struct create_completion_reply create_completion_reply;
+    struct open_completion_reply open_completion_reply;
+    struct insert_completion_reply insert_completion_reply;
+    struct remove_completion_reply remove_completion_reply;
+    struct query_completion_reply query_completion_reply;
+    struct set_completion_info_reply set_completion_info_reply;
+    struct fire_completion_reply fire_completion_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 306
+#define SERVER_PROTOCOL_VERSION 307
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/Makefile.in b/server/Makefile.in
index 7648281..41e1233 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -12,6 +12,7 @@ C_SRCS = \
 	change.c \
 	class.c \
 	clipboard.c \
+	completion.c \
 	console.c \
 	context_alpha.c \
 	context_i386.c \
diff --git a/server/completion.c b/server/completion.c
new file mode 100644
index 0000000..7fbef0c
--- /dev/null
+++ b/server/completion.c
@@ -0,0 +1,287 @@
+/*
+ * Server-side IO completion ports implementation
+ *
+ * Copyright (C) 2007 Andrey Turkin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+/* FIXMEs: 
+ *  - built-in wait queues used which means:
+ *    + threads are awaken FIFO and not LIFO as native does
+ *    + "max concurrent active threads" parameter not used
+ *    + completion handle is waitable, while native isn't
+ *
+ *  - messages on async completion put by client
+ */
+
+#include "config.h"
+#include "wine/port.h"
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "wine/unicode.h"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#include "windef.h"
+#include "winternl.h"
+
+#include "object.h"
+#include "handle.h"
+#include "request.h"
+struct completion
+{
+    struct object  obj;
+    struct list    queue;
+    unsigned long  depth;
+};
+
+static void completion_dump( struct object*, int );
+static void completion_destroy( struct object * );
+static int completion_signaled( struct object *obj, struct thread *thread );
+
+static const struct object_ops completion_ops =
+{
+    sizeof(struct completion), /* size */
+    completion_dump,           /* dump */
+    add_queue,                 /* add_queue */
+    remove_queue,              /* remove_queue */
+    completion_signaled,       /* signaled */
+    no_satisfied,              /* satisfied */
+    no_signal,                 /* signal */
+    no_get_fd,                 /* get_fd */
+    no_map_access,             /* map_access */
+    no_lookup_name,            /* lookup_name */
+    no_open_file,              /* open_file */
+    no_close_handle,           /* close_handle */
+    completion_destroy         /* destroy */
+};
+
+struct comp_msg {
+    struct list queue_entry;
+    unsigned long ckey;
+    unsigned long cvalue;
+    unsigned int  status;
+    unsigned int  information;
+};
+
+static void completion_destroy( struct object *obj)
+{
+    struct completion *completion = (struct completion *) obj;
+    struct comp_msg *tmp, *next;
+
+    LIST_FOR_EACH_ENTRY_SAFE( tmp, next, &completion->queue, struct comp_msg, queue_entry )
+    {
+        free( tmp );
+    }
+}
+
+static void completion_dump( struct object *obj, int verbose )
+{
+    struct completion *completion = (struct completion *) obj;
+
+    assert( obj->ops == &completion_ops );
+    fprintf( stderr, "Completion " );
+    dump_object_name( &completion->obj );
+    fprintf( stderr, " (%ld packets pending)\n", completion->depth );
+}
+
+static int completion_signaled( struct object *obj, struct thread *thread )
+{
+    struct completion *completion = (struct completion *)obj;
+
+    return !list_empty( &completion->queue );
+}
+
+static struct completion *create_completion( struct directory *root, const struct unicode_str *name, unsigned int attr, unsigned int concurrent )
+{
+    struct completion *completion;
+
+    if ((completion = create_named_object_dir( root, name, attr, &completion_ops )))
+    {
+        if (get_error() != STATUS_OBJECT_NAME_EXISTS)
+        {
+            list_init( &completion->queue );
+            completion->depth = 0;
+        }
+    }
+
+    return completion;
+}
+
+/* create a completion */
+DECL_HANDLER(create_completion)
+{
+    struct completion *completion;
+    struct unicode_str name;
+    struct directory *root = NULL;
+
+    reply->handle = 0;
+
+    get_req_unicode_str( &name );
+    if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
+        return;
+
+    if ( (completion = create_completion( root, &name, req->attributes, req->concurrent )) != NULL )
+    {
+        reply->handle = alloc_handle( current->process, completion, req->access, req->attributes );
+        release_object( completion );
+    }
+
+    if (root) release_object( root );
+}
+
+/* open a completion */
+DECL_HANDLER(open_completion)
+{
+    struct completion *completion;
+    struct unicode_str name;
+    struct directory *root = NULL;
+
+    reply->handle = 0;
+
+    get_req_unicode_str( &name );
+    if (req->rootdir && !(root = get_directory_obj( current->process, req->rootdir, 0 )))
+        return;
+
+    if ( (completion = open_object_dir( root, &name, req->attributes, &completion_ops )) != NULL )
+    {
+        reply->handle = alloc_handle( current->process, completion, req->access, req->attributes );
+        release_object( completion );
+    }
+
+    if (root) release_object( root );
+}
+
+static struct completion *get_completion_obj( struct process *process, obj_handle_t handle, unsigned int access )
+{
+    return (struct completion *) get_handle_obj( process, handle, access, &completion_ops );
+}
+
+struct compl_map {
+    struct completion *completion;
+    unsigned long completion_key;
+};
+
+struct compl_map *create_completion_map ( obj_handle_t handle, unsigned long completion_key )
+{
+    struct completion *completion;
+    struct compl_map *map;
+
+    if ((completion = get_completion_obj( current->process, handle, 0 /* FIXME access */ )) == NULL)
+        return NULL;
+    if ((map = mem_alloc( sizeof(*map) )) == NULL)
+    {
+        release_object( completion );
+        return NULL;
+    }
+    map->completion = completion;
+    map->completion_key = completion_key;
+    return map;
+}
+
+void delete_completion_map ( struct compl_map *map )
+{
+    release_object( map->completion );
+    free( map );
+}
+
+static void add_completion( struct completion *completion, unsigned long ckey, unsigned long cvalue, unsigned int status, unsigned int information )
+{
+    struct comp_msg *msg = mem_alloc( sizeof( *msg ) );
+
+    if (!msg) return;
+
+    msg->ckey = ckey;
+    msg->cvalue = cvalue;
+    msg->status = status;
+    msg->information = information;
+
+    list_add_tail( &completion->queue, &msg->queue_entry );
+    completion->depth++;
+    wake_up( &completion->obj, 1 );
+}
+
+void fire_completion_map( struct compl_map *map, unsigned long cvalue, unsigned int status, unsigned int information )
+{
+    add_completion( map->completion, map->completion_key, cvalue, status, information );
+}
+
+/* add completion to completion port */
+DECL_HANDLER(insert_completion)
+{
+    struct completion* completion = get_completion_obj( current->process, req->handle, 0 /* FIXME access! */ );
+    
+    if (!completion) return;
+
+    add_completion( completion, req->ckey, req->cvalue, req->status, req->information );
+
+    release_object( completion );
+}
+
+/* get completion from completion port */
+DECL_HANDLER(remove_completion)
+{
+    struct completion* completion = get_completion_obj( current->process, req->handle, 0 /* FIXME access! */ );
+    struct list *entry;
+    struct comp_msg *msg;
+    
+    if (!completion) return;
+
+    entry = list_head( &completion->queue );
+    if (!entry)
+        set_error( STATUS_PENDING );
+    else {
+        list_remove( entry );
+        completion->depth--;
+        msg = LIST_ENTRY( entry, struct comp_msg, queue_entry );
+        reply->ckey = msg->ckey;
+        reply->cvalue = msg->cvalue;
+        reply->status = msg->status;
+        reply->information = msg->information;
+        free( msg );
+    }
+
+    release_object( completion );
+}
+
+/* get queue depth for completion port */
+DECL_HANDLER(query_completion)
+{
+    struct completion* completion = get_completion_obj( current->process, req->handle, 0 /* FIXME access! */ );
+    
+    if (!completion) return;
+
+    reply->depth = completion->depth;
+
+    release_object( completion );
+}
diff --git a/server/fd.c b/server/fd.c
index 11cf294..568f573 100644
--- a/server/fd.c
+++ b/server/fd.c
@@ -177,6 +177,7 @@ struct fd
     struct async_queue  *read_q;      /* async readers of this fd */
     struct async_queue  *write_q;     /* async writers of this fd */
     struct async_queue  *wait_q;      /* other async waiters of this fd */
+    struct compl_map    *completion;  /* completion object attached to fd */
 };
 
 static void fd_dump( struct object *obj, int verbose );
@@ -1320,6 +1321,8 @@ static void fd_destroy( struct object *obj )
     free_async_queue( fd->write_q );
     free_async_queue( fd->wait_q );
 
+    if (fd->completion) delete_completion_map( fd->completion );
+
     remove_fd_locks( fd );
     list_remove( &fd->inode_entry );
     if (fd->poll_index != -1) remove_poll_user( fd, fd->poll_index );
@@ -1397,6 +1400,7 @@ static struct fd *alloc_fd_object(void)
     fd->read_q     = NULL;
     fd->write_q    = NULL;
     fd->wait_q     = NULL;
+    fd->completion = NULL;
     list_init( &fd->inode_entry );
     list_init( &fd->locks );
 
@@ -1429,6 +1433,7 @@ struct fd *alloc_pseudo_fd( const struct fd_ops *fd_user_ops, struct object *use
     fd->read_q     = NULL;
     fd->write_q    = NULL;
     fd->wait_q     = NULL;
+    fd->completion = NULL;
     fd->no_fd_status = STATUS_BAD_DEVICE_TYPE;
     list_init( &fd->inode_entry );
     list_init( &fd->locks );
@@ -2000,3 +2005,36 @@ DECL_HANDLER(cancel_async)
         release_object( fd );
     }
 }
+
+/* set completion object */
+DECL_HANDLER(set_completion_info)
+{
+    struct fd *fd = get_handle_fd_obj( current->process, req->handle, 0 );
+    if (fd)
+    {
+        struct compl_map *completion;
+        
+        if (req->handle == NULL || req->handle == INVALID_HANDLE_VALUE)
+            completion = NULL;
+        else if ((completion = create_completion_map( req->chandle, req->ckey )) == NULL)
+        {
+            release_object( fd );
+            return;
+        }
+
+        if (fd->completion) delete_completion_map( fd->completion );
+        fd->completion = completion;
+        release_object( fd );
+    }
+}
+
+/* check for associated completion and push msg */
+DECL_HANDLER(fire_completion)
+{
+    struct fd *fd = get_handle_fd_obj( current->process, req->handle, 0 );
+    if (fd)
+    {
+        if (fd->completion) fire_completion_map( fd->completion, req->cvalue, req->status, req->information );
+        release_object( fd );
+    }
+}
diff --git a/server/object.h b/server/object.h
index ffb2486..d43fcda 100644
--- a/server/object.h
+++ b/server/object.h
@@ -45,6 +45,7 @@ struct async;
 struct async_queue;
 struct winstation;
 struct directory;
+struct compl_map;
 
 
 struct unicode_str
@@ -210,6 +211,11 @@ extern struct symlink *create_symlink( struct directory *root, const struct unic
 extern void create_named_pipe_device( struct directory *root, const struct unicode_str *name );
 extern void create_mailslot_device( struct directory *root, const struct unicode_str *name );
 
+/* completion map */
+extern struct compl_map *create_completion_map( obj_handle_t handle, unsigned long completion_key );
+extern void delete_completion_map( struct compl_map *map);
+extern void fire_completion_map( struct compl_map *map, unsigned long cvalue, unsigned int status, unsigned int information );
+
 /* global variables */
 
   /* command-line options */
diff --git a/server/protocol.def b/server/protocol.def
index df6b8d3..6611a84 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -2919,3 +2919,65 @@ enum message_type
 @REPLY
     obj_handle_t event;           /* event signaled when all user processes have exited */
 @END
+
+/* Create I/O completion port */
+@REQ(create_completion)
+    unsigned int access;          /* desired access to a port */
+    unsigned int attributes;      /* object attributes */
+    unsigned int concurrent;      /* max number of concurrent active threads */
+    obj_handle_t rootdir;         /* root directory */
+    VARARG(filename,string);      /* port name */
+@REPLY
+    obj_handle_t handle;          /* port handle */
+@END
+
+/* Open I/O completion port */
+@REQ(open_completion)
+    unsigned int access;          /* desired access to a port */
+    unsigned int attributes;      /* object attributes */
+    obj_handle_t rootdir;         /* root directory */
+    VARARG(filename,string);      /* port name */
+@REPLY
+    obj_handle_t handle;          /* port handle */
+@END
+
+/* add completion to completion port */
+@REQ(insert_completion)
+    obj_handle_t handle;          /* port handle */
+    unsigned long ckey;           /* completion key */
+    unsigned long cvalue;         /* completion value */
+    unsigned int  status;         /* completion result */
+    unsigned int information;     /* IO_STATUS_BLOCK Information */
+@END
+
+/* get completion get completion port */
+@REQ(remove_completion)
+    obj_handle_t handle;          /* port handle */
+@REPLY
+    unsigned long ckey;           /* completion key */
+    unsigned long cvalue;         /* completion value */
+    unsigned int  status;         /* completion result */
+    unsigned int information;     /* IO_STATUS_BLOCK Information */
+@END
+
+/* get completion queue depth */
+@REQ(query_completion)
+    obj_handle_t handle;          /* port handle */
+@REPLY
+    unsigned long depth;          /* completion queue depth */
+@END
+
+/* associate object with completion port */
+@REQ(set_completion_info)
+    obj_handle_t  handle;         /* object handle */
+    obj_handle_t  chandle;        /* port handle */
+    unsigned long ckey;           /* completion key */
+@END
+
+/* check for associated completion and push msg */
+@REQ(fire_completion)
+    obj_handle_t handle;          /* async' object */
+    unsigned long cvalue;         /* completion value */
+    unsigned int status;          /* completion status */
+    unsigned int information;     /* IO_STATUS_BLOCK Information */
+@END
diff --git a/server/request.h b/server/request.h
index 10b6cbe..1f94bcd 100644
--- a/server/request.h
+++ b/server/request.h
@@ -330,6 +330,13 @@ DECL_HANDLER(create_device);
 DECL_HANDLER(delete_device);
 DECL_HANDLER(get_next_device_request);
 DECL_HANDLER(make_process_system);
+DECL_HANDLER(create_completion);
+DECL_HANDLER(open_completion);
+DECL_HANDLER(insert_completion);
+DECL_HANDLER(remove_completion);
+DECL_HANDLER(query_completion);
+DECL_HANDLER(set_completion_info);
+DECL_HANDLER(fire_completion);
 
 #ifdef WANT_REQUEST_HANDLERS
 
@@ -556,6 +563,13 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
     (req_handler)req_delete_device,
     (req_handler)req_get_next_device_request,
     (req_handler)req_make_process_system,
+    (req_handler)req_create_completion,
+    (req_handler)req_open_completion,
+    (req_handler)req_insert_completion,
+    (req_handler)req_remove_completion,
+    (req_handler)req_query_completion,
+    (req_handler)req_set_completion_info,
+    (req_handler)req_fire_completion,
 };
 #endif  /* WANT_REQUEST_HANDLERS */
 
diff --git a/server/trace.c b/server/trace.c
index 312b68d..12844d6 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -3539,6 +3539,82 @@ static void dump_make_process_system_reply( const struct make_process_system_rep
     fprintf( stderr, " event=%p", req->event );
 }
 
+static void dump_create_completion_request( const struct create_completion_request *req )
+{
+    fprintf( stderr, " access=%08x,", req->access );
+    fprintf( stderr, " attributes=%08x,", req->attributes );
+    fprintf( stderr, " concurrent=%08x,", req->concurrent );
+    fprintf( stderr, " rootdir=%p,", req->rootdir );
+    fprintf( stderr, " filename=" );
+    dump_varargs_string( cur_size );
+}
+
+static void dump_create_completion_reply( const struct create_completion_reply *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_open_completion_request( const struct open_completion_request *req )
+{
+    fprintf( stderr, " access=%08x,", req->access );
+    fprintf( stderr, " attributes=%08x,", req->attributes );
+    fprintf( stderr, " rootdir=%p,", req->rootdir );
+    fprintf( stderr, " filename=" );
+    dump_varargs_string( cur_size );
+}
+
+static void dump_open_completion_reply( const struct open_completion_reply *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_insert_completion_request( const struct insert_completion_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " ckey=%lx,", req->ckey );
+    fprintf( stderr, " cvalue=%lx,", req->cvalue );
+    fprintf( stderr, " status=%08x,", req->status );
+    fprintf( stderr, " information=%08x", req->information );
+}
+
+static void dump_remove_completion_request( const struct remove_completion_request *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_remove_completion_reply( const struct remove_completion_reply *req )
+{
+    fprintf( stderr, " ckey=%lx,", req->ckey );
+    fprintf( stderr, " cvalue=%lx,", req->cvalue );
+    fprintf( stderr, " status=%08x,", req->status );
+    fprintf( stderr, " information=%08x", req->information );
+}
+
+static void dump_query_completion_request( const struct query_completion_request *req )
+{
+    fprintf( stderr, " handle=%p", req->handle );
+}
+
+static void dump_query_completion_reply( const struct query_completion_reply *req )
+{
+    fprintf( stderr, " depth=%lx", req->depth );
+}
+
+static void dump_set_completion_info_request( const struct set_completion_info_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " chandle=%p,", req->chandle );
+    fprintf( stderr, " ckey=%lx", req->ckey );
+}
+
+static void dump_fire_completion_request( const struct fire_completion_request *req )
+{
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " cvalue=%lx,", req->cvalue );
+    fprintf( stderr, " status=%08x,", req->status );
+    fprintf( stderr, " information=%08x", req->information );
+}
+
 static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_new_process_request,
     (dump_func)dump_get_new_process_info_request,
@@ -3760,6 +3836,13 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)dump_delete_device_request,
     (dump_func)dump_get_next_device_request_request,
     (dump_func)dump_make_process_system_request,
+    (dump_func)dump_create_completion_request,
+    (dump_func)dump_open_completion_request,
+    (dump_func)dump_insert_completion_request,
+    (dump_func)dump_remove_completion_request,
+    (dump_func)dump_query_completion_request,
+    (dump_func)dump_set_completion_info_request,
+    (dump_func)dump_fire_completion_request,
 };
 
 static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
@@ -3983,6 +4066,13 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
     (dump_func)0,
     (dump_func)dump_get_next_device_request_reply,
     (dump_func)dump_make_process_system_reply,
+    (dump_func)dump_create_completion_reply,
+    (dump_func)dump_open_completion_reply,
+    (dump_func)0,
+    (dump_func)dump_remove_completion_reply,
+    (dump_func)dump_query_completion_reply,
+    (dump_func)0,
+    (dump_func)0,
 };
 
 static const char * const req_names[REQ_NB_REQUESTS] = {
@@ -4206,6 +4296,13 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
     "delete_device",
     "get_next_device_request",
     "make_process_system",
+    "create_completion",
+    "open_completion",
+    "insert_completion",
+    "remove_completion",
+    "query_completion",
+    "set_completion_info",
+    "fire_completion",
 };
 
 static const struct

Powered by Plone CMS, the Open Source Content Management System