Hi, This has been hanging around in my tree for a while. This is useful for RPC and for eventually implementing QueueUserWorkItem. Changelog: Implement I/O completion ports
? wine/server/iocompletion.c Index: wine/server/Makefile.in =================================================================== RCS file: /home/wine/wine/server/Makefile.in,v retrieving revision 1.47 diff -u -r1.47 Makefile.in --- wine/server/Makefile.in 24 Jul 2003 00:07:00 -0000 1.47 +++ wine/server/Makefile.in 6 Nov 2003 14:00:40 -0000 @@ -21,6 +21,7 @@ file.c \ handle.c \ hook.c \ + iocompletion.c \ main.c \ mapping.c \ mutex.c \ Index: wine/server/protocol.def =================================================================== RCS file: /home/wine/wine/server/protocol.def,v retrieving revision 1.87 diff -u -r1.87 protocol.def --- wine/server/protocol.def 14 Oct 2003 01:30:42 -0000 1.87 +++ wine/server/protocol.def 6 Nov 2003 14:00:43 -0000 @@ -2123,6 +2123,43 @@ @END +/* Create an I/O completion port */ +@REQ(create_io_completion) + unsigned int access; /* access desired by client */ + unsigned int concurrent_threads; /* the number of concurrent threads processing a request */ +@REPLY + user_handle_t handle; /* handle to the i/o completion port */ +@END + +/* Post data to an I/O completion port */ +@REQ(set_io_completion) + user_handle_t handle; /* handle to the i/o completion port */ + unsigned int bytes_transferred; /* number of bytes transferred */ + void* completion_key; /* user data to send to waiting client */ + void* overlapped; /* overlapped structure to send to client */ +@END + +/* Get or wait for data posted to an I/O completion port */ +@REQ(remove_io_completion) + user_handle_t handle; /* handle to the i/o completion port */ + void* cookie; /* magic cookie to return to client */ + abs_time_t timeout; /* absolute timeout */ +@REPLY + unsigned int bytes_transferred; /* number of bytes transferred */ + void* completion_key; /* user data to send to waiting client */ + void* overlapped; /* overlapped structure to send to client */ +@END + +/* Get data posted to an I/O completion port after waiting for it */ +@REQ(remove_io_completion_assigned) + user_handle_t handle; /* handle to the i/o completion port */ +@REPLY + unsigned int bytes_transferred; /* number of bytes transferred */ + void* completion_key; /* user data to send to waiting client */ + void* overlapped; /* overlapped structure to send to client */ +@END + + /* Set/get clipboard information */ @REQ(set_clipboard_info) unsigned int flags; /* flags for fields to set (see below) */ Index: wine/server/request.h =================================================================== RCS file: /home/wine/wine/server/request.h,v retrieving revision 1.89 diff -u -r1.89 request.h --- wine/server/request.h 7 Oct 2003 03:40:23 -0000 1.89 +++ wine/server/request.h 6 Nov 2003 14:00:43 -0000 @@ -279,6 +279,10 @@ DECL_HANDLER(start_hook_chain); DECL_HANDLER(finish_hook_chain); DECL_HANDLER(get_next_hook); +DECL_HANDLER(create_io_completion); +DECL_HANDLER(set_io_completion); +DECL_HANDLER(remove_io_completion); +DECL_HANDLER(remove_io_completion_assigned); DECL_HANDLER(set_clipboard_info); DECL_HANDLER(open_token); DECL_HANDLER(set_global_windows); @@ -464,6 +468,10 @@ (req_handler)req_start_hook_chain, (req_handler)req_finish_hook_chain, (req_handler)req_get_next_hook, + (req_handler)req_create_io_completion, + (req_handler)req_set_io_completion, + (req_handler)req_remove_io_completion, + (req_handler)req_remove_io_completion_assigned, (req_handler)req_set_clipboard_info, (req_handler)req_open_token, (req_handler)req_set_global_windows, Index: wine/server/thread.c =================================================================== RCS file: /home/wine/wine/server/thread.c,v retrieving revision 1.103 diff -u -r1.103 thread.c --- wine/server/thread.c 27 Oct 2003 22:10:22 -0000 1.103 +++ wine/server/thread.c 6 Nov 2003 14:00:45 -0000 @@ -521,7 +521,7 @@ } /* select on a list of handles */ -static void select_on( int count, void *cookie, const obj_handle_t *handles, +void select_on( int count, void *cookie, const obj_handle_t *handles, int flags, const abs_time_t *timeout ) { int ret, i; Index: wine/server/trace.c =================================================================== RCS file: /home/wine/wine/server/trace.c,v retrieving revision 1.185 diff -u -r1.185 trace.c --- wine/server/trace.c 14 Oct 2003 01:30:42 -0000 1.185 +++ wine/server/trace.c 6 Nov 2003 14:00:48 -0000 @@ -2498,6 +2498,52 @@ dump_varargs_unicode_str( cur_size ); } +static void dump_create_io_completion_request( const struct create_io_completion_request *req ) +{ + fprintf( stderr, " access=%08x,", req->access ); + fprintf( stderr, " concurrent_threads=%08x", req->concurrent_threads ); +} + +static void dump_create_io_completion_reply( const struct create_io_completion_reply *req ) +{ + fprintf( stderr, " handle=%p", req->handle ); +} + +static void dump_set_io_completion_request( const struct set_io_completion_request *req ) +{ + fprintf( stderr, " handle=%p,", req->handle ); + fprintf( stderr, " bytes_transferred=%08x,", req->bytes_transferred ); + fprintf( stderr, " completion_key=%p,", req->completion_key ); + fprintf( stderr, " overlapped=%p", req->overlapped ); +} + +static void dump_remove_io_completion_request( const struct remove_io_completion_request *req ) +{ + fprintf( stderr, " handle=%p,", req->handle ); + fprintf( stderr, " cookie=%p,", req->cookie ); + fprintf( stderr, " timeout=" ); + dump_abs_time( &req->timeout ); +} + +static void dump_remove_io_completion_reply( const struct remove_io_completion_reply *req ) +{ + fprintf( stderr, " bytes_transferred=%08x,", req->bytes_transferred ); + fprintf( stderr, " completion_key=%p,", req->completion_key ); + fprintf( stderr, " overlapped=%p", req->overlapped ); +} + +static void dump_remove_io_completion_assigned_request( const struct remove_io_completion_assigned_request *req ) +{ + fprintf( stderr, " handle=%p", req->handle ); +} + +static void dump_remove_io_completion_assigned_reply( const struct remove_io_completion_assigned_reply *req ) +{ + fprintf( stderr, " bytes_transferred=%08x,", req->bytes_transferred ); + fprintf( stderr, " completion_key=%p,", req->completion_key ); + fprintf( stderr, " overlapped=%p", req->overlapped ); +} + static void dump_set_clipboard_info_request( const struct set_clipboard_info_request *req ) { fprintf( stderr, " flags=%08x,", req->flags ); @@ -2721,6 +2767,10 @@ (dump_func)dump_start_hook_chain_request, (dump_func)dump_finish_hook_chain_request, (dump_func)dump_get_next_hook_request, + (dump_func)dump_create_io_completion_request, + (dump_func)dump_set_io_completion_request, + (dump_func)dump_remove_io_completion_request, + (dump_func)dump_remove_io_completion_assigned_request, (dump_func)dump_set_clipboard_info_request, (dump_func)dump_open_token_request, (dump_func)dump_set_global_windows_request, @@ -2903,6 +2953,10 @@ (dump_func)dump_start_hook_chain_reply, (dump_func)0, (dump_func)dump_get_next_hook_reply, + (dump_func)dump_create_io_completion_reply, + (dump_func)0, + (dump_func)dump_remove_io_completion_reply, + (dump_func)dump_remove_io_completion_assigned_reply, (dump_func)dump_set_clipboard_info_reply, (dump_func)dump_open_token_reply, (dump_func)dump_set_global_windows_reply, @@ -3085,6 +3139,10 @@ "start_hook_chain", "finish_hook_chain", "get_next_hook", + "create_io_completion", + "set_io_completion", + "remove_io_completion", + "remove_io_completion_assigned", "set_clipboard_info", "open_token", "set_global_windows", --- /dev/null Mon Jun 24 01:53:01 2002 +++ wine/server/iocompletion.c Thu Nov 6 13:24:08 2003 @@ -0,0 +1,282 @@ +/* + * I/O Completion Ports + * + * Copyright (C) 2003 Robert Shearman + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include <stdio.h> + +#include "windef.h" + +#include "handle.h" +#include "thread.h" +#include "request.h" +#include "wine/list.h" + +static void io_completion_dump( struct object *obj, int verbose ); +static void io_completion_destroy( struct object *obj ); +static int io_completion_signaled( struct object * obj, struct thread * thread ); +static int io_completion_satisfied( struct object * obj, struct thread * thread ); + +extern void select_on( int count, void *cookie, const obj_handle_t *handles, + int flags, const abs_time_t *timeout ); + +struct io_completion_data +{ + struct list entry; + unsigned int bytes_transferred; + void * completion_key; + void * overlapped; +}; + +struct io_completion_assigned_data +{ + struct list entry; + struct thread * thread; + struct io_completion_data * data; +}; + +struct io_completion_port +{ + struct object obj; + unsigned int concurrent_threads; + unsigned int max_concurrent_threads; /* FIXME: should we honour this? */ + struct list data; /* fifo queue for io_completion_data */ + + struct list assigned_data; /* io_completion_assigned_data */ + + /* Used to determine whether we have initiated the select() + * through GetQueuedCompletionStatus or whether the client + * has done WaitForSingleObject */ + int satisfied; +}; + +static const struct object_ops io_completion_ops = +{ + sizeof(struct io_completion_port), /* size */ + io_completion_dump, /* dump */ + add_queue, /* add_queue */ + remove_queue, /* remove_queue */ + io_completion_signaled, /* signaled */ + io_completion_satisfied, /* satisfied */ + no_get_fd, /* get_fd */ + io_completion_destroy /* destroy */ +}; + +static void io_completion_dump( struct object *obj, int verbose ) +{ + struct io_completion_port *port = (struct io_completion_port *)obj; + assert( obj->ops == &io_completion_ops ); + fprintf( stderr, "I/O completion port max_threads=%d\n", + port->max_concurrent_threads); +} + +static void io_completion_destroy( struct object *obj ) +{ + struct list * current; + struct io_completion_port *port = (struct io_completion_port *)obj; + assert( obj->ops == &io_completion_ops ); + + /* free data queue */ + for (current = list_head(&port->data); + !list_empty(&port->data); current = list_head(&port->data)) + { + list_remove(current); + free( LIST_ENTRY(current, struct io_completion_data, entry) ); + } + + /* free assigned data queue */ + for (current = list_head(&port->assigned_data); + !list_empty(&port->assigned_data); current = list_head(&port->assigned_data)) + { + list_remove(current); + free( LIST_ENTRY(current, struct io_completion_assigned_data, entry)->data ); + free( LIST_ENTRY(current, struct io_completion_assigned_data, entry) ); + } +} + +static int io_completion_signaled( struct object * obj, struct thread * thread ) +{ + struct io_completion_port * port = (struct io_completion_port *)obj; + assert( obj->ops == &io_completion_ops ); + return !list_empty(&port->data); +} + +static int io_completion_satisfied( struct object * obj, struct thread * thread ) +{ + struct io_completion_port * port = (struct io_completion_port *)obj; + assert( obj->ops == &io_completion_ops ); + return port->satisfied; +} + +static struct object * create_io_completion(unsigned int concurrent_threads) +{ + struct io_completion_port * port; + if (!(port = alloc_object( &io_completion_ops ))) + { + return NULL; + } + + list_init(&port->data); + list_init(&port->assigned_data); + port->concurrent_threads = 0; + port->max_concurrent_threads = concurrent_threads; + port->satisfied = 1; /* abandon any waits on the port immediately */ + + return &port->obj; +} + +static void assign_data(struct io_completion_port * port, struct thread * thread) +{ + struct io_completion_data * data = + LIST_ENTRY(list_head(&port->data), struct io_completion_data, entry); + struct io_completion_assigned_data * assigned_data; + if ((assigned_data = mem_alloc(sizeof(*data))) != NULL) + { + list_init(&assigned_data->entry); + assigned_data->data = data; + assigned_data->thread = thread; + + list_add_head( &port->assigned_data, &assigned_data->entry ); + + list_remove( &data->entry ); + } +} + +DECL_HANDLER(create_io_completion) +{ + struct object * obj; + + reply->handle = 0; + if ((obj = create_io_completion(req->concurrent_threads)) != NULL) + { + reply->handle = alloc_handle(current->process, obj, req->access | SYNCHRONIZE, FALSE /*inherit flag*/); + release_object( obj ); + } +} + +DECL_HANDLER(remove_io_completion) +{ + struct io_completion_port * port = (struct io_completion_port *)get_handle_obj( + current->process, + req->handle, + GENERIC_READ, + &io_completion_ops); + + if (!port) + { + reply->bytes_transferred = 0; + reply->completion_key = NULL; + reply->overlapped = NULL; + return; + } + + if (!list_empty(&port->data)) /* there is waiting data */ + { + struct io_completion_data * data = + LIST_ENTRY(list_head(&port->data), struct io_completion_data, entry); + reply->bytes_transferred = data->bytes_transferred; + reply->completion_key = data->completion_key; + reply->overlapped = data->overlapped; + /* remove the data from the completion port */ + list_remove( &data->entry ); + free( data ); + } + else /* there is no waiting data */ + { + port->satisfied = 0; /* don't abandon wait on the port */ + select_on(1, req->cookie, &req->handle, SELECT_TIMEOUT, &req->timeout); + port->satisfied = 1; /* abandon any waits on the port immediately */ + reply->bytes_transferred = 0; + reply->completion_key = NULL; + reply->overlapped = NULL; + } + release_object( &port->obj ); +} + +DECL_HANDLER(set_io_completion) +{ + struct io_completion_data * data; + struct io_completion_port * port = (struct io_completion_port *)get_handle_obj( + current->process, + req->handle, + GENERIC_WRITE, + &io_completion_ops); + + if (!port) + return; + + if ((data = mem_alloc(sizeof(*data))) != NULL) + { + list_init(&data->entry); + data->bytes_transferred = req->bytes_transferred; + data->completion_key = req->completion_key; + data->overlapped = req->overlapped; + + list_add_tail(&port->data, &data->entry); + + if (port->obj.tail != NULL) /* there is a waiting thread */ + { + struct wait_queue_entry * waiting = port->obj.tail; + assign_data( port, waiting->thread ); + wake_thread( waiting->thread ); + } + } + release_object( &port->obj ); +} + +DECL_HANDLER(remove_io_completion_assigned) +{ + struct io_completion_assigned_data * assigned_data; + struct list * cursor; + struct io_completion_port * port = (struct io_completion_port *)get_handle_obj( + current->process, + req->handle, + GENERIC_WRITE, + &io_completion_ops); + + if (!port) + { + reply->bytes_transferred = 0; + reply->completion_key = NULL; + reply->overlapped = NULL; + return; + } + + LIST_FOR_EACH(cursor, &port->assigned_data) + { + assigned_data = LIST_ENTRY(cursor, struct io_completion_assigned_data, entry); + if (assigned_data->thread == current) + { + reply->bytes_transferred = assigned_data->data->bytes_transferred; + reply->completion_key = assigned_data->data->completion_key; + reply->overlapped = assigned_data->data->overlapped; + list_remove( &assigned_data->entry ); + free( assigned_data->data ); + free( assigned_data ); + return; + } + } + + set_error(STATUS_INVALID_PARAMETER); + reply->bytes_transferred = 0; + reply->completion_key = NULL; + reply->overlapped = NULL; +} Index: wine/include/winbase.h =================================================================== RCS file: /home/wine/wine/include/winbase.h,v retrieving revision 1.199 diff -u -r1.199 winbase.h --- wine/include/winbase.h 4 Nov 2003 04:52:54 -0000 1.199 +++ wine/include/winbase.h 6 Nov 2003 14:06:41 -0000 @@ -1149,6 +1149,7 @@ HANDLE WINAPI CreateFileMappingA(HANDLE,LPSECURITY_ATTRIBUTES,DWORD,DWORD,DWORD,LPCSTR); HANDLE WINAPI CreateFileMappingW(HANDLE,LPSECURITY_ATTRIBUTES,DWORD,DWORD,DWORD,LPCWSTR); #define CreateFileMapping WINELIB_NAME_AW(CreateFileMapping) +HANDLE WINAPI CreateIoCompletionPort(HANDLE,HANDLE,ULONG_PTR,DWORD); HANDLE WINAPI CreateMailslotA(LPCSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES); HANDLE WINAPI CreateMailslotW(LPCWSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES); #define CreateMailslot WINELIB_NAME_AW(CreateMailslot) @@ -1290,6 +1291,7 @@ BOOL WINAPI GetProcessAffinityMask(HANDLE,PDWORD,PDWORD); BOOL WINAPI GetProcessTimes(HANDLE,LPFILETIME,LPFILETIME,LPFILETIME,LPFILETIME); DWORD WINAPI GetProcessVersion(DWORD); +BOOL WINAPI GetQueuedCompletionStatus(HANDLE,LPDWORD,PULONG_PTR,LPOVERLAPPED*,DWORD); BOOL WINAPI GetSecurityDescriptorControl(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR_CONTROL,LPDWORD); BOOL WINAPI GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR,LPBOOL,PACL *,LPBOOL); BOOL WINAPI GetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR,PSID *,LPBOOL); @@ -1391,6 +1393,7 @@ HANDLE WINAPI OpenWaitableTimerW(DWORD,BOOL,LPCWSTR); #define OpenWaitableTimer WINELIB_NAME_AW(OpenWaitableTimer) BOOL WINAPI PeekNamedPipe(HANDLE,PVOID,DWORD,PDWORD,PDWORD,PDWORD); +BOOL WINAPI PostQueuedCompletionStatus(HANDLE,DWORD,ULONG_PTR,LPOVERLAPPED); DWORD WINAPI PrepareTape(HANDLE,DWORD,BOOL); BOOL WINAPI PulseEvent(HANDLE); BOOL WINAPI PurgeComm(HANDLE,DWORD); Index: wine/include/winternl.h =================================================================== RCS file: /home/wine/wine/include/winternl.h,v retrieving revision 1.63 diff -u -r1.63 winternl.h --- wine/include/winternl.h 31 Oct 2003 00:16:20 -0000 1.63 +++ wine/include/winternl.h 6 Nov 2003 14:06:44 -0000 @@ -1367,7 +1367,7 @@ /************************************************************************* * Loader functions and structures. * - * Those are not part of standard Winternl.h + * These are not part of standard Winternl.h */ typedef struct _LDR_MODULE { @@ -1428,6 +1428,23 @@ NTSTATUS WINAPI LdrQueryProcessModuleInformation(SYSTEM_MODULE_INFORMATION*, ULONG, ULONG*); NTSTATUS WINAPI LdrUnloadDll(HMODULE); NTSTATUS WINAPI LdrUnlockLoaderLock(ULONG,ULONG); + +/************************************************************************* + * I/O completion functions and structures. + * + * These are not part of standard Winternl.h + */ +typedef struct _FILE_COMPLETION_INFORMATION { + HANDLE CompletionPort; + ULONG_PTR CompletionKey; +} FILE_COMPLETION_INFORMATION; +typedef FILE_COMPLETION_INFORMATION *PFILE_COMPLETION_INFORMATION; + +NTSTATUS WINAPI NtCreateIoCompletion(PHANDLE,ACCESS_MASK,ULONG_PTR,ULONG); +NTSTATUS WINAPI NtSetIoCompletion(HANDLE,ULONG_PTR,LPOVERLAPPED,ULONG,ULONG); +NTSTATUS WINAPI NtRemoveIoCompletion(HANDLE,PULONG_PTR,LPOVERLAPPED*,PIO_STATUS_BLOCK,PLARGE_INTEGER); +NTSTATUS WINAPI NtSetInformationFile(HANDLE,PIO_STATUS_BLOCK,PVOID,ULONG,FILE_INFORMATION_CLASS); + /* list manipulation macros */ #define InitializeListHead(le) (void)((le)->Flink = (le)->Blink = (le)) Index: wine/include/wine/server_protocol.h =================================================================== RCS file: /home/wine/wine/include/wine/server_protocol.h,v retrieving revision 1.88 diff -u -r1.88 server_protocol.h --- wine/include/wine/server_protocol.h 14 Oct 2003 01:30:42 -0000 1.88 +++ wine/include/wine/server_protocol.h 6 Nov 2003 14:06:47 -0000 @@ -3046,6 +3046,64 @@ +struct create_io_completion_request +{ + struct request_header __header; + unsigned int access; + unsigned int concurrent_threads; +}; +struct create_io_completion_reply +{ + struct reply_header __header; + user_handle_t handle; +}; + + +struct set_io_completion_request +{ + struct request_header __header; + user_handle_t handle; + unsigned int bytes_transferred; + void* completion_key; + void* overlapped; +}; +struct set_io_completion_reply +{ + struct reply_header __header; +}; + + +struct remove_io_completion_request +{ + struct request_header __header; + user_handle_t handle; + void* cookie; + abs_time_t timeout; +}; +struct remove_io_completion_reply +{ + struct reply_header __header; + unsigned int bytes_transferred; + void* completion_key; + void* overlapped; +}; + + +struct remove_io_completion_assigned_request +{ + struct request_header __header; + user_handle_t handle; +}; +struct remove_io_completion_assigned_reply +{ + struct reply_header __header; + unsigned int bytes_transferred; + void* completion_key; + void* overlapped; +}; + + + struct set_clipboard_info_request { struct request_header __header; @@ -3292,6 +3350,10 @@ REQ_start_hook_chain, REQ_finish_hook_chain, REQ_get_next_hook, + REQ_create_io_completion, + REQ_set_io_completion, + REQ_remove_io_completion, + REQ_remove_io_completion_assigned, REQ_set_clipboard_info, REQ_open_token, REQ_set_global_windows, @@ -3478,6 +3540,10 @@ struct start_hook_chain_request start_hook_chain_request; struct finish_hook_chain_request finish_hook_chain_request; struct get_next_hook_request get_next_hook_request; + struct create_io_completion_request create_io_completion_request; + struct set_io_completion_request set_io_completion_request; + struct remove_io_completion_request remove_io_completion_request; + struct remove_io_completion_assigned_request remove_io_completion_assigned_request; struct set_clipboard_info_request set_clipboard_info_request; struct open_token_request open_token_request; struct set_global_windows_request set_global_windows_request; @@ -3662,11 +3728,15 @@ struct start_hook_chain_reply start_hook_chain_reply; struct finish_hook_chain_reply finish_hook_chain_reply; struct get_next_hook_reply get_next_hook_reply; + struct create_io_completion_reply create_io_completion_reply; + struct set_io_completion_reply set_io_completion_reply; + struct remove_io_completion_reply remove_io_completion_reply; + struct remove_io_completion_assigned_reply remove_io_completion_assigned_reply; struct set_clipboard_info_reply set_clipboard_info_reply; struct open_token_reply open_token_reply; struct set_global_windows_reply set_global_windows_reply; }; -#define SERVER_PROTOCOL_VERSION 126 +#define SERVER_PROTOCOL_VERSION 127 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ ? wine/dlls/kernel/iocompletion.c Index: wine/dlls/kernel/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/kernel/Makefile.in,v retrieving revision 1.98 diff -u -r1.98 Makefile.in --- wine/dlls/kernel/Makefile.in 6 Nov 2003 01:09:58 -0000 1.98 +++ wine/dlls/kernel/Makefile.in 6 Nov 2003 14:08:31 -0000 @@ -44,6 +44,7 @@ file.c \ file16.c \ format_msg.c \ + iocompletion.c \ global16.c \ heap.c \ instr.c \ Index: wine/dlls/kernel/kernel32.spec =================================================================== RCS file: /home/wine/wine/dlls/kernel/kernel32.spec,v retrieving revision 1.116 diff -u -r1.116 kernel32.spec --- wine/dlls/kernel/kernel32.spec 3 Nov 2003 22:12:15 -0000 1.116 +++ wine/dlls/kernel/kernel32.spec 6 Nov 2003 14:08:33 -0000 @@ -645,7 +645,7 @@ @ stdcall PeekConsoleInputA(ptr ptr long ptr) @ stdcall PeekConsoleInputW(ptr ptr long ptr) @ stdcall PeekNamedPipe(long ptr long ptr ptr ptr) -@ stub PostQueuedCompletionStatus +@ stdcall PostQueuedCompletionStatus(ptr long long ptr) @ stdcall PrepareTape(ptr long long) @ stub PrivMoveFileIdentityW @ stdcall Process32First (ptr ptr) Index: wine/dlls/kernel/sync.c =================================================================== RCS file: /home/wine/wine/dlls/kernel/sync.c,v retrieving revision 1.44 diff -u -r1.44 sync.c --- wine/dlls/kernel/sync.c 3 Nov 2003 22:12:15 -0000 1.44 +++ wine/dlls/kernel/sync.c 6 Nov 2003 14:08:34 -0000 @@ -1529,31 +1529,6 @@ /****************************************************************************** - * CreateIoCompletionPort (KERNEL32.@) - */ -HANDLE WINAPI CreateIoCompletionPort(HANDLE hFileHandle, HANDLE hExistingCompletionPort, - DWORD dwCompletionKey, DWORD dwNumberOfConcurrentThreads) -{ - FIXME("(%p, %p, %08lx, %08lx): stub.\n", - hFileHandle, hExistingCompletionPort, dwCompletionKey, dwNumberOfConcurrentThreads); - return NULL; -} - - -/****************************************************************************** - * GetQueuedCompletionStatus (KERNEL32.@) - */ -BOOL WINAPI GetQueuedCompletionStatus( HANDLE CompletionPort, LPDWORD lpNumberOfBytesTransferred, - LPDWORD lpCompletionKey, LPOVERLAPPED *lpOverlapped, - DWORD dwMilliseconds ) -{ - FIXME("(%p,%p,%p,%p,%ld), stub!\n", - CompletionPort,lpNumberOfBytesTransferred,lpCompletionKey,lpOverlapped,dwMilliseconds); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; -} - -/****************************************************************************** * CreateJobObjectW (KERNEL32.@) */ HANDLE WINAPI CreateJobObjectW( LPSECURITY_ATTRIBUTES attr, LPCWSTR name ) --- /dev/null Mon Jun 24 01:53:01 2002 +++ wine/dlls/kernel/iocompletion.c Thu Nov 6 14:31:21 2003 @@ -0,0 +1,173 @@ +/* + * I/O Completion Ports + * + * Copyright (C) 2003 Robert Shearman + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winnt.h" +#include "winreg.h" +#include "winternl.h" +#include "ntstatus.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(iocompletion); + +HANDLE WINAPI CreateIoCompletionPort( + HANDLE FileHandle, + HANDLE ExistingCompletionPort, + ULONG_PTR CompletionKey, + DWORD NumberOfConcurrentThreads) +{ + HANDLE CompletionPort; + NTSTATUS Status; + + TRACE("(%p, %p, %lx, %ld)\n", + FileHandle, + ExistingCompletionPort, + CompletionKey, + NumberOfConcurrentThreads); + + if (((FileHandle == INVALID_HANDLE_VALUE) && (ExistingCompletionPort != NULL)) || + (ExistingCompletionPort == INVALID_HANDLE_VALUE)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + if (ExistingCompletionPort == NULL) + { + Status = NtCreateIoCompletion( + &CompletionPort, + GENERIC_ALL, 0, + NumberOfConcurrentThreads); + + if (Status != STATUS_SUCCESS) + { + SetLastError(RtlNtStatusToDosError(Status)); + return INVALID_HANDLE_VALUE; + } + } + else + CompletionPort = ExistingCompletionPort; + + if (FileHandle != INVALID_HANDLE_VALUE) + { + IO_STATUS_BLOCK IoStatusBlock; + FILE_COMPLETION_INFORMATION CompletionInfo; + CompletionInfo.CompletionKey = CompletionKey; + CompletionInfo.CompletionPort = CompletionPort; + + Status = NtSetInformationFile( + FileHandle, + &IoStatusBlock, + (PVOID)&CompletionInfo, + sizeof(FILE_COMPLETION_INFORMATION), + FileCompletionInformation); + + if (Status != STATUS_SUCCESS) + { + SetLastError(RtlNtStatusToDosError(Status)); + return INVALID_HANDLE_VALUE; + } + } + return CompletionPort; +} + +BOOL WINAPI GetQueuedCompletionStatus( + HANDLE CompletionPort, + LPDWORD lpNumberOfBytesTransferred, + PULONG_PTR lpCompletionKey, + LPOVERLAPPED * lplpOverlapped, + DWORD dwMilliseconds) +{ + IO_STATUS_BLOCK CompletionBlock; + NTSTATUS Status; + + TRACE("(CompletionPort %p, %p, %p, %p, %ld)\n", + CompletionPort, + lpNumberOfBytesTransferred, + lpCompletionKey, + lplpOverlapped, + dwMilliseconds); + + if (dwMilliseconds == INFINITE) + Status = NtRemoveIoCompletion( + CompletionPort, + lpCompletionKey, + lplpOverlapped, + &CompletionBlock, + NULL); + else + { + LARGE_INTEGER WaitTime; + /* multiplying two LONGLONGs with at least one LONGLONG having its + * higher long part not zero makes the multiplying a bit harder, + * therefore we do an easy multiply and negate afterwards rather than + * making it a hard multiply by doing "* -10000" + */ + WaitTime.QuadPart = (LONGLONG)dwMilliseconds * (LONGLONG)10000; + WaitTime.QuadPart = -WaitTime.QuadPart; + Status = NtRemoveIoCompletion( + CompletionPort, + lpCompletionKey, + lplpOverlapped, + &CompletionBlock, + &WaitTime); + } + if (Status == STATUS_SUCCESS) + { + *lpNumberOfBytesTransferred = CompletionBlock.Information; + return TRUE; + } + else + { + SetLastError(RtlNtStatusToDosError(Status)); + return FALSE; + } +} + +BOOL WINAPI PostQueuedCompletionStatus( + HANDLE CompletionPort, + DWORD dwNumberOfBytesTransferred, + ULONG_PTR dwCompletionKey, + LPOVERLAPPED lpOverlapped) +{ + NTSTATUS Status; + + TRACE("(CompletionPort %p, %ld, %lx, %p)\n", + CompletionPort, + dwNumberOfBytesTransferred, + dwCompletionKey, + lpOverlapped); + + Status = NtSetIoCompletion( + CompletionPort, + dwCompletionKey, + lpOverlapped, 0, + dwNumberOfBytesTransferred); + + if (Status == STATUS_SUCCESS) + return TRUE; + else + { + SetLastError(RtlNtStatusToDosError(Status)); + return FALSE; + } +} Index: wine/dlls/ntdll/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/ntdll/Makefile.in,v retrieving revision 1.92 diff -u -r1.92 Makefile.in --- wine/dlls/ntdll/Makefile.in 5 Nov 2003 23:31:11 -0000 1.92 +++ wine/dlls/ntdll/Makefile.in 6 Nov 2003 14:25:00 -0000 @@ -15,6 +15,7 @@ exception.c \ file.c \ heap.c \ + iocompletion.c \ large_int.c \ loader.c \ loadorder.c \ Index: wine/dlls/ntdll/sync.c =================================================================== RCS file: /home/wine/wine/dlls/ntdll/sync.c,v retrieving revision 1.33 diff -u -r1.33 sync.c --- wine/dlls/ntdll/sync.c 6 Nov 2003 00:08:05 -0000 1.33 +++ wine/dlls/ntdll/sync.c 6 Nov 2003 14:25:01 -0000 @@ -462,11 +462,11 @@ /*********************************************************************** - * wait_reply + * NTDLL_wait_reply * * Wait for a reply on the waiting pipe of the current thread. */ -static int wait_reply( void *cookie ) +int NTDLL_wait_reply( void *cookie ) { int signaled; struct wake_up_reply reply; @@ -479,7 +479,7 @@ if (!reply.cookie) break; /* thread got killed */ if (reply.cookie == cookie) return reply.signaled; /* we stole another reply, wait for the real one */ - signaled = wait_reply( cookie ); + signaled = NTDLL_wait_reply( cookie ); /* and now put the wrong one back in the pipe */ for (;;) { @@ -575,7 +575,7 @@ ret = wine_server_call( req ); } SERVER_END_REQ; - if (ret == STATUS_PENDING) ret = wait_reply( &cookie ); + if (ret == STATUS_PENDING) ret = NTDLL_wait_reply( &cookie ); if (ret != STATUS_USER_APC) break; call_apcs( (flags & SELECT_ALERTABLE) != 0 ); if (flags & SELECT_ALERTABLE) break; Index: wine/dlls/ntdll/ntdll.spec =================================================================== RCS file: /home/wine/wine/dlls/ntdll/ntdll.spec,v retrieving revision 1.137 diff -u -r1.137 ntdll.spec --- wine/dlls/ntdll/ntdll.spec 5 Nov 2003 23:31:11 -0000 1.137 +++ wine/dlls/ntdll/ntdll.spec 6 Nov 2003 14:25:03 -0000 @@ -79,7 +79,7 @@ @ stdcall NtCreateEvent(long long long long long) @ stub NtCreateEventPair @ stdcall NtCreateFile(ptr long ptr ptr long long long ptr long long ptr) -@ stub NtCreateIoCompletion +@ stdcall NtCreateIoCompletion(ptr long long long) @ stdcall NtCreateKey(long long long long long long long) @ stdcall NtCreateMailslotFile(long long long long long long long long) @ stub NtCreateMutant @@ -192,7 +192,7 @@ @ stub NtReleaseMutant @ stub NtReleaseProcessMutant @ stdcall NtReleaseSemaphore(long long ptr) -@ stub NtRemoveIoCompletion +@ stdcall NtRemoveIoCompletion(ptr ptr ptr ptr ptr) @ stdcall NtReplaceKey(ptr long ptr) @ stub NtReplyPort @ stdcall NtReplyWaitReceivePort(ptr ptr ptr ptr) @@ -220,7 +220,7 @@ @ stdcall NtSetInformationThread(long long ptr long) @ stub NtSetInformationToken @ stdcall NtSetIntervalProfile(long long) -@ stub NtSetIoCompletion +@ stdcall NtSetIoCompletion(ptr long ptr long long) @ stub NtSetLdtEntries @ stub NtSetLowEventPair @ stub NtSetLowWaitHighEventPair --- /dev/null Mon Jun 24 01:53:01 2002 +++ wine/dlls/ntdll/iocompletion.c Thu Nov 6 13:24:58 2003 @@ -0,0 +1,215 @@ +/* + * I/O Completion Ports + * + * Copyright (C) 2003 Robert Shearman + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdarg.h> + +#include "windef.h" +#include "winbase.h" +#include "winnt.h" +#include "winreg.h" +#include "winternl.h" + +#include "config.h" + +#include "ntdll_misc.h" +#include "wine/server.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(ntdll); + +extern int NTDLL_wait_reply( void *cookie ); + +/************************************************************************** + * NtCreateIoCompletion (NTDLL.@) + * + * Params: + * CompletionPort [O]: the handle created + * DesiredAccess [I}: the access desired (e.g. GENERIC_ALL) + * Reserved [I}: unknown + * NumberOfConcurrentThreads [I]: the desired number of concurrent + * threads + * Returns: + * Status + * Notes: + * It is effectively a FIFO queue for data and + * a LIFO queue for threads to "minimize context switches". + * The aim is to keep a small number of threads constantly + * active. + * See: + * MSDN for CreateIoCompletionPort spec and + * the article "Inside I/O Completion Ports" + * (http://www.sysinternals.com/ntw2k/info/comport.shtml) + */ +NTSTATUS WINAPI NtCreateIoCompletion ( + OUT PHANDLE CompletionPort, + IN ACCESS_MASK DesiredAccess, + IN ULONG_PTR Reserved, + IN ULONG NumberOfConcurrentThreads + ) +{ + NTSTATUS ret; + + TRACE("(%p, %lx, %lx, %ld)\n", + CompletionPort, + DesiredAccess, + Reserved, + NumberOfConcurrentThreads); + + if (Reserved != 0) + { + FIXME("Reserved != 0 not supported\n"); + return STATUS_INVALID_PARAMETER; + } + + if (DesiredAccess && GENERIC_ALL) + DesiredAccess |= GENERIC_READ | GENERIC_WRITE; + + SERVER_START_REQ( create_io_completion ) + { + req->access = DesiredAccess; + req->concurrent_threads = NumberOfConcurrentThreads; + ret = wine_server_call( req ); + *CompletionPort = reply->handle; + } + SERVER_END_REQ; + + TRACE("returning %lx\n", ret); + return ret; +} + +/************************************************************************** + * NtSetIoCompletion (NTDLL.@) + * + * Params: + * CompletionPort [I]: port to send data to + * CompletionKey [I}: user key to identify this set of data + * lpOverlapped [I}: OVERLAPPED structure to send to port + * NumberOfBytesTransferred [I}: unknown - seems to be set to zero always + * NumberOfBytesToTransfer [I]: Bytes to transfer in this packet of data + * Returns: + * Status + * See: + * MSDN for PostQueuedCompletionStatus spec and + * the article "Inside I/O Completion Ports" + * (http://www.sysinternals.com/ntw2k/info/comport.shtml) + */ +NTSTATUS WINAPI NtSetIoCompletion( + IN HANDLE CompletionPort, + IN ULONG_PTR CompletionKey, + IN LPOVERLAPPED lpOverlapped, + IN ULONG NumberOfBytesTransferred, /* normally set to 0 */ + IN ULONG NumberOfBytesToTransfer /* will become number of bytes transferred in the io operation */ + ) +{ + NTSTATUS ret; + + TRACE("(%p, %lx, %p, %ld, %ld)\n", + CompletionPort, + CompletionKey, + lpOverlapped, + NumberOfBytesTransferred, + NumberOfBytesToTransfer); + + if (NumberOfBytesTransferred != 0) + { + FIXME("NumberOfBytesTransferred != 0 not supported\n"); + return STATUS_INVALID_PARAMETER; + } + + SERVER_START_REQ( set_io_completion ) + { + req->handle = CompletionPort; + req->completion_key = (void *)CompletionKey; + req->overlapped = lpOverlapped; + req->bytes_transferred = NumberOfBytesToTransfer; + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + TRACE("returning %lx\n", ret); + return ret; +} + +/************************************************************************** + * NtRemoveIoCompletion (NTDLL.@) + * + * See: MSDN for GetQueuedCompletionStatus spec and + * the article "Inside I/O Completion Ports" + * (http://www.sysinternals.com/ntw2k/info/comport.shtml) + */ +NTSTATUS WINAPI NtRemoveIoCompletion ( + IN HANDLE CompletionPort, + OUT PULONG_PTR CompletionKey, + OUT LPOVERLAPPED * lplpOverlapped, + OUT PIO_STATUS_BLOCK CompletionStatus, + IN PLARGE_INTEGER WaitTime + ) +{ + NTSTATUS ret; + int cookie; + + TRACE("(%p, %p, %p, %p, %p)\n", + CompletionPort, + CompletionKey, + lplpOverlapped, + CompletionStatus, + WaitTime); + + for (;;) + { + SERVER_START_REQ( remove_io_completion ) + { + req->handle = CompletionPort; + req->cookie = &cookie; + NTDLL_get_server_timeout( &req->timeout, WaitTime ); + ret = wine_server_call( req ); + if (ret == STATUS_SUCCESS) + { + *CompletionKey = (ULONG_PTR)reply->completion_key; + *lplpOverlapped = reply->overlapped; + CompletionStatus->u.Status = STATUS_SUCCESS; + CompletionStatus->Information = reply->bytes_transferred; + } + } + SERVER_END_REQ; + if (ret == STATUS_PENDING) + ret = NTDLL_wait_reply( &cookie ); + if (ret == STATUS_ABANDONED) + { + SERVER_START_REQ( remove_io_completion_assigned ) + { + req->handle = CompletionPort; + ret = wine_server_call( req ); + if (ret == STATUS_SUCCESS) + { + *CompletionKey = (ULONG_PTR)reply->completion_key; + *lplpOverlapped = reply->overlapped; + CompletionStatus->u.Status = STATUS_SUCCESS; + CompletionStatus->Information = reply->bytes_transferred; + } + } + SERVER_END_REQ; + } + break; + } + + TRACE("returning %lx\n", ret); + return ret; +}