From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/signal_arm64ec.c | 1 + dlls/ntdll/tests/file.c | 1 - dlls/ntdll/unix/sync.c | 28 +++++++ dlls/wow64/sync.c | 20 +++++ include/winternl.h | 1 + server/async.c | 2 +- server/completion.c | 162 +++++++++++++++++++++++++++++++----- server/fd.c | 2 +- server/file.h | 12 ++- server/object.c | 1 + server/object.h | 1 + server/process.c | 2 +- server/protocol.def | 14 ++++ server/thread.c | 32 +++++++ server/thread.h | 1 + 16 files changed, 253 insertions(+), 28 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index e529bd623c8..2085be4aaf7 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -152,6 +152,7 @@ @ stdcall -syscall NtAllocateVirtualMemoryEx(long ptr ptr long long ptr long) @ stdcall -syscall NtAreMappedFilesTheSame(ptr ptr) @ stdcall -syscall NtAssignProcessToJobObject(long long) +@ stdcall -syscall NtAssociateWaitCompletionPacket(ptr ptr ptr ptr ptr long ptr ptr) @ stdcall -syscall NtCallbackReturn(ptr long long) # @ stub NtCancelDeviceWakeupRequest @ stdcall -syscall NtCancelIoFile(long ptr) diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index 00195b41e88..c33757b7ec0 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -257,6 +257,7 @@ DEFINE_WRAPPED_SYSCALL(NtAllocateVirtualMemory, (HANDLE process, PVOID *ret, ULO DEFINE_WRAPPED_SYSCALL(NtAllocateVirtualMemoryEx, (HANDLE process, PVOID *ret, SIZE_T *size_ptr, ULONG type, ULONG protect, MEM_EXTENDED_PARAMETER *parameters, ULONG count)) DEFINE_SYSCALL(NtAreMappedFilesTheSame, (PVOID addr1, PVOID addr2)) DEFINE_SYSCALL(NtAssignProcessToJobObject, (HANDLE job, HANDLE process)) +DEFINE_SYSCALL(NtAssociateWaitCompletionPacket,(HANDLE packet, HANDLE completion, HANDLE target, void *key_context, void *apc_context, NTSTATUS io_status, ULONG_PTR io_status_information, BOOLEAN *already_signaled)) DEFINE_SYSCALL(NtCallbackReturn, (void *ret_ptr, ULONG ret_len, NTSTATUS status)) DEFINE_SYSCALL(NtCancelIoFile, (HANDLE handle, IO_STATUS_BLOCK *io_status)) DEFINE_SYSCALL(NtCancelIoFileEx, (HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status)) diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 4a308f3e577..056167b7309 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -6100,7 +6100,6 @@ static void test_associate_wait_completion_packet(void)
if (!pNtAssociateWaitCompletionPacket) { - todo_wine win_skip("NtAssociateWaitCompletionPacket is unavailable.\n"); return; } diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index b9b883205d8..16337a64daa 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2794,6 +2794,34 @@ NTSTATUS WINAPI NtCreateTransaction( HANDLE *handle, ACCESS_MASK mask, OBJECT_AT return STATUS_SUCCESS; }
+/*********************************************************************** + * NtAssociateWaitCompletionPacket (NTDLL.@) + */ +NTSTATUS WINAPI NtAssociateWaitCompletionPacket( HANDLE packet, HANDLE completion, HANDLE target, + void *key_context, void *apc_context, NTSTATUS status, ULONG_PTR information, + BOOLEAN *already_signaled ) +{ + NTSTATUS ret; + + TRACE( "%p, %p, %p, %p, %p, %#x, %ld, %p stub.\n", packet, completion, target, key_context, + apc_context, (int)status, information, already_signaled ); + + SERVER_START_REQ( associate_wait_completion_packet ) + { + req->packet = wine_server_obj_handle( packet ); + req->completion = wine_server_obj_handle( completion ); + req->target = wine_server_obj_handle( target ); + req->ckey = wine_server_client_ptr( key_context ); + req->cvalue = wine_server_client_ptr( apc_context ); + req->information = information; + req->status = status; + if (!(ret = wine_server_call( req )) && already_signaled) + *already_signaled = reply->signaled; + } + SERVER_END_REQ; + return ret; +} + /*********************************************************************** * NtCreateWaitCompletionPacket (NTDLL.@) */ diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index 2f516b8f40c..d9bea95fd5e 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -1781,6 +1781,26 @@ NTSTATUS WINAPI wow64_NtCreateTransaction( UINT *args ) return status; }
+ +/********************************************************************** + * wow64_NtAssociateWaitCompletionPacket + */ +NTSTATUS WINAPI wow64_NtAssociateWaitCompletionPacket( UINT *args ) +{ + HANDLE packet = get_handle( &args ); + HANDLE completion = get_handle( &args ); + HANDLE target = get_handle( &args ); + void *key_context = get_ptr( &args ); + void *apc_context = get_ptr( &args ); + LONG io_status = get_ulong( &args ); + ULONG_PTR io_status_information = get_ulong( &args ); + BOOLEAN *already_signaled = get_ptr( &args ); + + return NtAssociateWaitCompletionPacket( packet, completion, target, key_context, apc_context, + io_status, io_status_information, already_signaled ); +} + + /********************************************************************** * wow64_NtCreateWaitCompletionPacket */ diff --git a/include/winternl.h b/include/winternl.h index aa2427b0f6a..6105cef9528 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4487,6 +4487,7 @@ NTSYSAPI NTSTATUS WINAPI NtAllocateVirtualMemory(HANDLE,PVOID*,ULONG_PTR,SIZE_T NTSYSAPI NTSTATUS WINAPI NtAllocateVirtualMemoryEx(HANDLE,PVOID*,SIZE_T*,ULONG,ULONG,MEM_EXTENDED_PARAMETER*,ULONG); NTSYSAPI NTSTATUS WINAPI NtAreMappedFilesTheSame(PVOID,PVOID); NTSYSAPI NTSTATUS WINAPI NtAssignProcessToJobObject(HANDLE,HANDLE); +NTSYSAPI NTSTATUS WINAPI NtAssociateWaitCompletionPacket(HANDLE,HANDLE,HANDLE,void *,void *,NTSTATUS,ULONG_PTR,BOOLEAN *); NTSYSAPI NTSTATUS WINAPI NtCallbackReturn(PVOID,ULONG,NTSTATUS); NTSYSAPI NTSTATUS WINAPI NtCancelIoFile(HANDLE,PIO_STATUS_BLOCK); NTSYSAPI NTSTATUS WINAPI NtCancelIoFileEx(HANDLE,PIO_STATUS_BLOCK,PIO_STATUS_BLOCK); diff --git a/server/async.c b/server/async.c index d2d929c9709..67f7e6f47c9 100644 --- a/server/async.c +++ b/server/async.c @@ -481,7 +481,7 @@ static void add_async_completion( struct async *async, apc_param_t cvalue, unsig apc_param_t information ) { if (async->fd && !async->completion) async->completion = fd_get_completion( async->fd, &async->comp_key ); - if (async->completion) add_completion( async->completion, async->comp_key, cvalue, status, information ); + if (async->completion) add_completion( async->completion, async->comp_key, cvalue, status, information, NULL ); }
/* store the result of the client-side async callback */ diff --git a/server/completion.c b/server/completion.c index 9edc3d0e041..0c5b98851a9 100644 --- a/server/completion.c +++ b/server/completion.c @@ -36,6 +36,25 @@ #include "handle.h" #include "request.h"
+struct comp_msg +{ + struct wait_completion_packet *packet; /* if this is from a wait completion packet, store its pointer here */ + struct list queue_entry; + apc_param_t ckey; + apc_param_t cvalue; + apc_param_t information; + unsigned int status; +}; + +struct completion +{ + struct object obj; + struct list queue; + struct list wait_queue; + unsigned int depth; + int closed; +}; + static const WCHAR wait_completion_packet_name[] = {'W','a','i','t','C','o','m','p','l','e','t','i','o','n','P','a','c','k','e','t'};
struct type_descr wait_completion_packet_type = @@ -51,6 +70,7 @@ struct type_descr wait_completion_packet_type = };
static void wait_completion_packet_dump( struct object *, int ); +static void wait_completion_packet_destroy( struct object * );
static const struct object_ops wait_completion_packet_ops = { @@ -73,7 +93,7 @@ static const struct object_ops wait_completion_packet_ops = no_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ no_close_handle, /* close_handle */ - no_destroy /* destroy */ + wait_completion_packet_destroy /* destroy */ };
static void wait_completion_packet_dump( struct object *obj, int verbose ) @@ -81,7 +101,16 @@ static void wait_completion_packet_dump( struct object *obj, int verbose ) struct wait_completion_packet *packet = (struct wait_completion_packet *)obj;
assert( obj->ops == &wait_completion_packet_ops ); - fprintf( stderr, "WaitCompletionPacket\n" ); + fprintf( stderr, "WaitCompletionPacket target=%p completion=%p ckey=%llx cvalue=%llx " + "information=%llx status=%#x in_object_packet_queue=%d in_completion_queue=%d\n", + packet->target, packet->completion, (long long unsigned int)packet->ckey, + (long long unsigned int)packet->cvalue, (long long unsigned int)packet->information, + packet->status, packet->in_object_packet_queue, packet->in_completion_queue ); +} + +struct wait_completion_packet *get_wait_completion_packet_obj( struct process *process, obj_handle_t handle, unsigned int access ) +{ + return (struct wait_completion_packet *)get_handle_obj( process, handle, access, &wait_completion_packet_ops ); }
static struct wait_completion_packet *create_wait_completion_packet( struct object *root, @@ -89,7 +118,46 @@ static struct wait_completion_packet *create_wait_completion_packet( struct obje unsigned int attr, const struct security_descriptor *sd ) { - return create_named_object( root, &wait_completion_packet_ops, name, attr, sd ); + struct wait_completion_packet *packet; + + if ((packet = create_named_object( root, &wait_completion_packet_ops, name, attr, sd )) + && get_error() != STATUS_OBJECT_NAME_EXISTS) + { + packet->target = NULL; + packet->completion = NULL; + packet->ckey = 0; + packet->cvalue = 0; + packet->information = 0; + packet->status = 0; + packet->in_object_packet_queue = 0; + packet->in_completion_queue = 0; + } + return packet; +} + +static void wait_completion_packet_destroy( struct object *obj ) +{ + struct wait_completion_packet *packet = (struct wait_completion_packet *)obj; + struct comp_msg *comp_msg; + + if (packet->in_object_packet_queue) list_remove( &packet->entry ); + + if (packet->in_completion_queue) + { + LIST_FOR_EACH_ENTRY( comp_msg, &packet->completion->queue, struct comp_msg, queue_entry ) + { + if (comp_msg->packet == packet) + { + list_remove( &comp_msg->queue_entry ); + free( comp_msg ); + packet->completion->depth--; + break; + } + } + } + + if (packet->target) release_object( packet->target ); + if (packet->completion) release_object( packet->completion ); }
static const WCHAR completion_name[] = {'I','o','C','o','m','p','l','e','t','i','o','n'}; @@ -106,15 +174,6 @@ struct type_descr completion_type = }, };
-struct comp_msg -{ - struct list queue_entry; - apc_param_t ckey; - apc_param_t cvalue; - apc_param_t information; - unsigned int status; -}; - struct completion_wait { struct object obj; @@ -125,15 +184,6 @@ struct completion_wait struct list wait_queue_entry; };
-struct completion -{ - struct object obj; - struct list queue; - struct list wait_queue; - unsigned int depth; - int closed; -}; - static void completion_wait_dump( struct object*, int ); static int completion_wait_signaled( struct object *obj, struct wait_queue_entry *entry ); static void completion_wait_satisfied( struct object *obj, struct wait_queue_entry *entry ); @@ -342,7 +392,7 @@ struct completion *get_completion_obj( struct process *process, obj_handle_t han }
void add_completion( struct completion *completion, apc_param_t ckey, apc_param_t cvalue, - unsigned int status, apc_param_t information ) + unsigned int status, apc_param_t information, struct wait_completion_packet *packet ) { struct comp_msg *msg = mem_alloc( sizeof( *msg ) ); struct completion_wait *wait; @@ -350,6 +400,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_ if (!msg) return;
+ msg->packet = packet; msg->ckey = ckey; msg->cvalue = cvalue; msg->status = status; @@ -385,6 +436,65 @@ DECL_HANDLER(create_wait_completion_packet) if (root) release_object( root ); }
+/* associate a wait completion packet */ +DECL_HANDLER(associate_wait_completion_packet) +{ + struct wait_completion_packet *packet; + struct completion *completion; + struct object *target; + + packet = get_wait_completion_packet_obj( current->process, req->packet, WAIT_COMPLETION_PACKET_QUERY_STATE ); + if (!packet) + return; + + if (packet->in_object_packet_queue || packet->in_completion_queue) + { + release_object( packet ); + set_error( STATUS_INVALID_PARAMETER_1 ); + return; + } + + completion = get_completion_obj( current->process, req->completion, IO_COMPLETION_MODIFY_STATE ); + if (!completion) + { + release_object( packet ); + return; + } + + target = get_handle_obj( current->process, req->target, 0, NULL ); + if (!target) + { + release_object( completion ); + release_object( packet ); + return; + } + + packet->completion = (struct completion *)grab_object( completion ); + packet->ckey = req->ckey; + packet->cvalue = req->cvalue; + packet->information = req->information; + packet->status = req->status; + + if (is_obj_signaled( target )) + { + add_completion( packet->completion, packet->ckey, packet->cvalue, packet->status, + packet->information, packet ); + packet->in_completion_queue = 1; + reply->signaled = 1; + } + else + { + packet->target = grab_object( target ); + list_add_tail( &target->wait_completion_packet_queue, &packet->entry ); + packet->in_object_packet_queue = 1; + reply->signaled = 0; + } + + release_object( packet ); + release_object( target ); + release_object( completion ); +} + /* create a completion */ DECL_HANDLER(create_completion) { @@ -429,7 +539,7 @@ DECL_HANDLER(add_completion) return; }
- add_completion( completion, req->ckey, req->cvalue, req->status, req->information ); + add_completion( completion, req->ckey, req->cvalue, req->status, req->information, NULL );
if (reserve) release_object( reserve ); release_object( completion ); @@ -477,6 +587,12 @@ DECL_HANDLER(remove_completion) reply->cvalue = msg->cvalue; reply->status = msg->status; reply->information = msg->information; + if (msg->packet) + { + release_object( msg->packet->completion ); + msg->packet->completion = NULL; + msg->packet->in_completion_queue = 0; + } free( msg ); reply->wait_handle = 0; } diff --git a/server/fd.c b/server/fd.c index dc2475b2d28..f5fbab6c224 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2925,7 +2925,7 @@ DECL_HANDLER(add_fd_completion) if (fd) { if (fd->completion && (req->async || !(fd->comp_flags & FILE_SKIP_COMPLETION_PORT_ON_SUCCESS))) - add_completion( fd->completion, fd->comp_key, req->cvalue, req->status, req->information ); + add_completion( fd->completion, fd->comp_key, req->cvalue, req->status, req->information, NULL ); release_object( fd ); } } diff --git a/server/file.h b/server/file.h index 5e988376e55..557edaefc88 100644 --- a/server/file.h +++ b/server/file.h @@ -51,6 +51,16 @@ struct async_queue struct wait_completion_packet { struct object obj; /* object header */ + struct list entry; /* list entry in the target object packet list */ + struct object *target; /* target object */ + struct completion *completion; /* completion object */ + apc_param_t ckey; /* key context */ + apc_param_t cvalue; /* apc context */ + apc_param_t information; /* IO_STATUS_BLOCK information */ + unsigned int status; /* completion status */ + unsigned int in_object_packet_queue: 1; /* whether the packet is in the target object queue */ + unsigned int in_completion_queue: 1; /* whether the packet is in the completion queue */ + unsigned int pad: 30; /* padding */ };
/* operations valid on file descriptor objects */ @@ -244,7 +254,7 @@ extern struct dir *get_dir_obj( struct process *process, obj_handle_t handle, un extern struct completion *get_completion_obj( struct process *process, obj_handle_t handle, unsigned int access ); extern struct reserve *get_completion_reserve_obj( struct process *process, obj_handle_t handle, unsigned int access ); extern void add_completion( struct completion *completion, apc_param_t ckey, apc_param_t cvalue, - unsigned int status, apc_param_t information ); + unsigned int status, apc_param_t information, struct wait_completion_packet *packet ); extern void cleanup_thread_completion( struct thread *thread );
/* serial port functions */ diff --git a/server/object.c b/server/object.c index cd368ef724a..8c9708abf7d 100644 --- a/server/object.c +++ b/server/object.c @@ -308,6 +308,7 @@ void *alloc_object( const struct object_ops *ops ) obj->name = NULL; obj->sd = NULL; list_init( &obj->wait_queue ); + list_init( &obj->wait_completion_packet_queue ); #ifdef DEBUG_OBJECTS list_add_head( &object_list, &obj->obj_list ); #endif diff --git a/server/object.h b/server/object.h index e02dbdc6b71..d29aa4c346a 100644 --- a/server/object.h +++ b/server/object.h @@ -115,6 +115,7 @@ struct object unsigned int handle_count;/* handle count */ const struct object_ops *ops; struct list wait_queue; + struct list wait_completion_packet_queue; struct object_name *name; struct security_descriptor *sd; unsigned int is_permanent:1; diff --git a/server/process.c b/server/process.c index 737b37d9441..c0ab5ed7663 100644 --- a/server/process.c +++ b/server/process.c @@ -266,7 +266,7 @@ static struct job *get_job_obj( struct process *process, obj_handle_t handle, un static void add_job_completion( struct job *job, apc_param_t msg, apc_param_t pid ) { if (job->completion_port) - add_completion( job->completion_port, job->completion_key, pid, STATUS_SUCCESS, msg ); + add_completion( job->completion_port, job->completion_key, pid, STATUS_SUCCESS, msg, NULL ); }
static void add_job_completion_existing_processes( struct job *job, struct job *completion_job ) diff --git a/server/protocol.def b/server/protocol.def index 5f9431d9517..1c5ac39cce9 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3858,6 +3858,20 @@ struct handle_info @END
+/* Associate a wait completion packet */ +@REQ(associate_wait_completion_packet) + obj_handle_t packet; /* wait completion packet handle */ + obj_handle_t completion; /* completion handle */ + obj_handle_t target; /* target object handle */ + apc_param_t ckey; /* completion key */ + apc_param_t cvalue; /* completion value */ + apc_param_t information; /* IO_STATUS_BLOCK information */ + unsigned int status; /* completion status */ +@REPLY + int signaled; /* whether the object is already signaled */ +@END + + /* check for associated completion and push msg */ @REQ(add_fd_completion) obj_handle_t handle; /* async' object */ diff --git a/server/thread.c b/server/thread.c index b3ce5d9ac95..2f25859d7a1 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1059,12 +1059,44 @@ static int select_on( const union select_op *select_op, data_size_t op_size, cli return 0; }
+int is_obj_signaled( struct object *obj ) +{ + struct wait_queue_entry wait_entry; + struct thread_wait wait = {0}; + + if (!obj->ops->signaled) + return 0; + + wait.thread = current; + list_init( &wait_entry.entry ); + wait_entry.obj = obj; + wait_entry.wait = &wait; + return obj->ops->signaled( obj, &wait_entry ); +} + /* attempt to wake threads sleeping on the object wait queue */ void wake_up( struct object *obj, int max ) { struct list *ptr; int ret;
+ if (is_obj_signaled( obj )) + { + struct wait_completion_packet *packet; + + LIST_FOR_EACH_ENTRY( packet, &obj->wait_completion_packet_queue, struct wait_completion_packet, entry ) + { + list_remove( &packet->entry ); + release_object( packet->target ); + packet->in_object_packet_queue = 0; + packet->target = NULL; + + add_completion( packet->completion, packet->ckey, packet->cvalue, packet->status, + packet->information, packet ); + packet->in_completion_queue = 1; + } + } + LIST_FOR_EACH( ptr, &obj->wait_queue ) { struct wait_queue_entry *entry = LIST_ENTRY( ptr, struct wait_queue_entry, entry ); diff --git a/server/thread.h b/server/thread.h index 754e617b484..26eb17866e4 100644 --- a/server/thread.h +++ b/server/thread.h @@ -116,6 +116,7 @@ extern int wake_thread_queue_entry( struct wait_queue_entry *entry ); extern int add_queue( struct object *obj, struct wait_queue_entry *entry ); extern void remove_queue( struct object *obj, struct wait_queue_entry *entry ); extern void kill_thread( struct thread *thread, int violent_death ); +extern int is_obj_signaled( struct object *obj ); extern void wake_up( struct object *obj, int max ); extern int thread_queue_apc( struct process *process, struct thread *thread, struct object *owner, const union apc_call *call_data ); extern void thread_cancel_apc( struct thread *thread, struct object *owner, enum apc_type type );