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 | 19 ++++ include/winternl.h | 1 + server/async.c | 2 +- server/completion.c | 217 +++++++++++++++++++++++++++++++++++- server/fd.c | 2 +- server/file.h | 3 +- server/object.c | 1 + server/object.h | 1 + server/process.c | 2 +- server/protocol.def | 14 +++ server/thread.c | 16 +++ server/thread.h | 2 + 16 files changed, 301 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index c089b4129a3..05f40a8bdf3 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -153,6 +153,7 @@ @ stub -syscall=0x004c NtApphelpCacheControl @ stdcall -syscall NtAreMappedFilesTheSame(ptr ptr) @ stdcall -syscall NtAssignProcessToJobObject(long long) +@ stdcall -syscall NtAssociateWaitCompletionPacket(ptr ptr ptr ptr ptr long ptr ptr) @ stdcall -syscall=0x0005 NtCallbackReturn(ptr long long) @ stdcall -syscall=0x005d NtCancelIoFile(long ptr) @ stdcall -syscall NtCancelIoFileEx(long ptr ptr) diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index af6c11d3cea..f942a6c966c 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -358,6 +358,7 @@ DEFINE_WRAPPED_SYSCALL(NtAllocateVirtualMemoryEx, (HANDLE process, PVOID *ret, S DEFINE_SYSCALL(NtApphelpCacheControl, (ULONG class, void *context)) 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 db954e9087c..3304bf1cc88 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -7001,7 +7001,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 a5d9f830c2e..d19428e9cbf 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2872,6 +2872,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\n", packet, completion, target, key_context, + apc_context, (int)status, information, already_signaled ); + + SERVER_START_REQ( associate_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->already_signaled; + } + SERVER_END_REQ; + return ret; +} + /*********************************************************************** * NtCreateWaitCompletionPacket (NTDLL.@) */ diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index 4cf4d3b6f2a..1920c956790 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -1858,6 +1858,25 @@ NTSTATUS WINAPI wow64_NtCreateTransaction( UINT *args ) }
+/********************************************************************** + * 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 2137965ebef..26a0598ee24 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4459,6 +4459,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 1ed241dcb65..110c2e183ca 100644 --- a/server/async.c +++ b/server/async.c @@ -482,7 +482,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 4bf264f379d..6baaaacdb33 100644 --- a/server/completion.c +++ b/server/completion.c @@ -57,6 +57,7 @@ struct comp_msg apc_param_t cvalue; apc_param_t information; unsigned int status; + struct completion_packet *packet; /* the completion packet this msg is from or NULL */ };
struct completion_wait @@ -81,8 +82,20 @@ struct completion struct completion_packet { struct object obj; /* object header */ + struct list entry; /* list entry in the target object wait completion packet queue */ + struct object *target; /* target object this packet waits for */ + 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_target_packet_queue: 1; /* whether the packet is in the wait queue of the target */ + unsigned int in_completion_queue: 1; /* whether the packet is in the completion queue */ + unsigned int pad: 30; /* padding */ };
+static void remove_completion_packet_msg( struct comp_msg *msg ); + 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 ); @@ -156,6 +169,7 @@ static void completion_wait_satisfied( struct object *obj, struct wait_queue_ent list_remove( &msg->queue_entry ); if (wait->msg) free( wait->msg ); wait->msg = msg; + remove_completion_packet_msg( msg ); }
static void completion_dump( struct object*, int ); @@ -300,7 +314,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 completion_packet *packet ) { struct comp_msg *msg = mem_alloc( sizeof( *msg ) ); struct completion_wait *wait; @@ -308,6 +322,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; @@ -338,6 +353,7 @@ struct type_descr completion_packet_type = };
static void completion_packet_dump( struct object *, int ); +static void completion_packet_destroy( struct object * );
static const struct object_ops completion_packet_ops = { @@ -361,13 +377,26 @@ static const struct object_ops completion_packet_ops = no_open_file, /* open_file */ no_kernel_obj_list, /* get_kernel_obj_list */ no_close_handle, /* close_handle */ - no_destroy /* destroy */ + completion_packet_destroy /* destroy */ };
static void completion_packet_dump( struct object *obj, int verbose ) { + struct completion_packet *packet = (struct completion_packet *)obj; + assert( obj->ops == &completion_packet_ops ); - fprintf( stderr, "WaitCompletionPacket\n" ); + fprintf( stderr, "WaitCompletionPacket target=%p completion=%p ckey=%llx cvalue=%llx " + "information=%llx status=%#x in_target_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_target_packet_queue, packet->in_completion_queue ); +} + +static struct completion_packet *get_completion_packet_obj( struct process *process, + obj_handle_t handle, + unsigned int access ) +{ + return (struct completion_packet *)get_handle_obj( process, handle, access, &completion_packet_ops ); }
static struct completion_packet *create_completion_packet( struct object *root, @@ -375,7 +404,110 @@ static struct completion_packet *create_completion_packet( struct object *root, unsigned int attr, const struct security_descriptor *sd ) { - return create_named_object( root, &completion_packet_ops, name, attr, sd ); + struct completion_packet *packet; + + if ((packet = create_named_object( root, &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_target_packet_queue = 0; + packet->in_completion_queue = 0; + } + return packet; +} + +/* try to wake up completion packets in the object when it's signaled */ +void wake_up_completion_packets( struct object *obj ) +{ + struct completion_packet *packet; + + if (list_empty( &obj->completion_packet_queue )) + return; + + if (!is_obj_signaled( obj )) + return; + + LIST_FOR_EACH_ENTRY( packet, &obj->completion_packet_queue, struct completion_packet, entry ) + { + assert( packet->in_target_packet_queue ); + assert( packet->target ); + assert( !packet->in_completion_queue ); + assert( packet->completion ); + + list_remove( &packet->entry ); + release_object( packet->target ); + packet->in_target_packet_queue = 0; + packet->target = NULL; + packet->in_completion_queue = 1; + add_completion( packet->completion, packet->ckey, packet->cvalue, packet->status, + packet->information, packet ); + } +} + +static void cancel_completion_packet( struct completion_packet *packet ) +{ + struct comp_msg *comp_msg; + + if (packet->in_target_packet_queue) + { + assert( packet->target ); + list_remove( &packet->entry ); + packet->in_target_packet_queue = 0; + } + + if (packet->in_completion_queue) + { + assert( packet->completion ); + 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; + } + } + + packet->in_completion_queue = 0; + } + + if (packet->target) + { + release_object( packet->target ); + packet->target = NULL; + } + + if (packet->completion) + { + release_object( packet->completion ); + packet->completion = NULL; + } +} + +static void completion_packet_destroy( struct object *obj ) +{ + struct completion_packet *packet = (struct completion_packet *)obj; + + cancel_completion_packet( packet ); +} + +static void remove_completion_packet_msg( struct comp_msg *msg ) +{ + if (!msg->packet) + return; + + assert( msg->packet->in_completion_queue ); + assert( msg->packet->completion ); + release_object( msg->packet->completion ); + msg->packet->completion = NULL; + msg->packet->in_completion_queue = 0; + msg->packet = NULL; }
/* create a completion */ @@ -426,7 +558,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 ); @@ -474,6 +606,7 @@ DECL_HANDLER(remove_completion) reply->cvalue = msg->cvalue; reply->status = msg->status; reply->information = msg->information; + remove_completion_packet_msg( msg ); free( msg ); reply->wait_handle = 0; if (list_empty( &completion->queue )) reset_sync( completion->sync ); @@ -536,3 +669,77 @@ DECL_HANDLER(create_completion_packet)
if (root) release_object( root ); } + +/* associate a wait completion packet */ +DECL_HANDLER(associate_completion_packet) +{ + struct completion_packet *packet; + struct completion *completion; + struct object *target, *sync; + + packet = get_completion_packet_obj( current->process, req->packet, WAIT_COMPLETION_PACKET_QUERY_STATE ); + if (!packet) + return; + + if (packet->in_target_packet_queue || packet->in_completion_queue) + { + release_object( packet ); + set_error( STATUS_INVALID_PARAMETER_1 ); + return; + } + + target = get_handle_obj( current->process, req->target, SYNCHRONIZE, NULL ); + if (!target) + { + release_object( packet ); + return; + } + + /* mutexs and keyed events are not allowed for associating with wait completion packets */ + if (target->ops->type == &mutex_type || target->ops->type == &keyed_event_type) + { + release_object( target ); + release_object( packet ); + set_error( STATUS_INVALID_PARAMETER_3 ); + return; + } + + completion = get_completion_obj( current->process, req->completion, IO_COMPLETION_MODIFY_STATE ); + if (!completion) + { + release_object( target ); + release_object( packet ); + return; + } + + assert( !packet->completion ); + assert( !packet->in_completion_queue ); + assert( !packet->target ); + assert( !packet->in_target_packet_queue ); + + 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 )) + { + packet->in_completion_queue = 1; + add_completion( packet->completion, packet->ckey, packet->cvalue, packet->status, + packet->information, packet ); + reply->already_signaled = 1; + } + else + { + packet->target = grab_object( target ); + sync = target->ops->get_sync( target ); + list_add_tail( &sync->completion_packet_queue, &packet->entry ); + packet->in_target_packet_queue = 1; + release_object( sync ); + reply->already_signaled = 0; + } + release_object( completion ); + release_object( target ); + release_object( packet ); +} diff --git a/server/fd.c b/server/fd.c index 663f497b7a9..b3af7c1b063 100644 --- a/server/fd.c +++ b/server/fd.c @@ -3017,7 +3017,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 567194bf00a..8cdb78700c9 100644 --- a/server/file.h +++ b/server/file.h @@ -29,6 +29,7 @@ struct fd; struct mapping; struct async_queue; struct completion; +struct completion_packet; struct reserve;
/* server-side representation of I/O status block */ @@ -240,7 +241,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 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 28bda0db77b..75afe8cdd2e 100644 --- a/server/object.c +++ b/server/object.c @@ -311,6 +311,7 @@ void *alloc_object( const struct object_ops *ops ) obj->name = NULL; obj->sd = NULL; list_init( &obj->wait_queue ); + list_init( &obj->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 5df9b380f0c..f93466d369a 100644 --- a/server/object.h +++ b/server/object.h @@ -117,6 +117,7 @@ struct object unsigned int handle_count;/* handle count */ const struct object_ops *ops; struct list wait_queue; + struct list 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 9b3b0b8fc6c..f5dbf8bbdf4 100644 --- a/server/process.c +++ b/server/process.c @@ -277,7 +277,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 3b7e8efbde4..35ae85e619a 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3893,6 +3893,20 @@ struct handle_info @END
+/* Associate a wait completion packet */ +@REQ(associate_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 already_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 05ec6a4ec00..cd4b459638d 100644 --- a/server/thread.c +++ b/server/thread.c @@ -1336,6 +1336,20 @@ static int select_on( const union select_op *select_op, data_size_t op_size, cli return 0; }
+/* check if an object is signaled for wait completion packets */ +int is_obj_signaled( struct object *obj ) +{ + struct object *sync; + int signaled; + + sync = obj->ops->get_sync( obj ); + signaled = sync->ops->signaled( sync, NULL ); + if (signaled) + sync->ops->satisfied( sync, NULL ); + release_object( sync ); + return signaled; +} + /* attempt to wake threads sleeping on the object wait queue */ void wake_up( struct object *obj, int max ) { @@ -1350,6 +1364,8 @@ void wake_up( struct object *obj, int max ) /* restart at the head of the list since a wake up can change the object wait queue */ ptr = &obj->wait_queue; } + + wake_up_completion_packets( obj ); }
/* return the apc queue to use for a given apc type */ diff --git a/server/thread.h b/server/thread.h index 58081be7481..53f1c624c66 100644 --- a/server/thread.h +++ b/server/thread.h @@ -118,6 +118,8 @@ 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_completion_packets( 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 );