completion-
Andrey Turkin's IOCompletionPort patch Modified by Christian Heimes for GIT master (git clone git://source.winehq.org/git/wine.git wine)
Size 39.0 kB - File type text/plainFile 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
Click here to get the file