ntdll: Introduce a separate per-thread object for internal completion waits.

This commit is contained in:
Paul Gofman 2024-10-07 18:19:57 -06:00 committed by Alexandre Julliard
parent 18844cef20
commit e01b70851f
Notes: Alexandre Julliard 2024-10-23 23:32:39 +02:00
Approved-by: Elizabeth Figura (@zfigura)
Approved-by: Alexandre Julliard (@julliard)
Merge-Request: https://gitlab.winehq.org/wine/wine/merge_requests/6630
9 changed files with 133 additions and 5 deletions

View file

@ -1999,6 +1999,7 @@ NTSTATUS WINAPI NtSetIoCompletion( HANDLE handle, ULONG_PTR key, ULONG_PTR value
NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE handle, ULONG_PTR *key, ULONG_PTR *value,
IO_STATUS_BLOCK *io, LARGE_INTEGER *timeout )
{
HANDLE wait_handle = NULL;
unsigned int status;
TRACE( "(%p, %p, %p, %p, %p)\n", handle, key, value, io, timeout );
@ -2015,10 +2016,11 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE handle, ULONG_PTR *key, ULONG_PTR *
io->Information = reply->information;
io->Status = reply->status;
}
else wait_handle = wine_server_ptr_handle( reply->wait_handle );
}
SERVER_END_REQ;
if (status != STATUS_PENDING) return status;
status = NtWaitForSingleObject( handle, FALSE, timeout );
status = NtWaitForSingleObject( wait_handle, FALSE, timeout );
if (status != WAIT_OBJECT_0) return status;
}
}
@ -2030,6 +2032,7 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE handle, ULONG_PTR *key, ULONG_PTR *
NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORMATION *info, ULONG count,
ULONG *written, LARGE_INTEGER *timeout, BOOLEAN alertable )
{
HANDLE wait_handle = NULL;
unsigned int status;
ULONG i = 0;
@ -2049,6 +2052,7 @@ NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORM
info[i].IoStatusBlock.Information = reply->information;
info[i].IoStatusBlock.Status = reply->status;
}
else wait_handle = wine_server_ptr_handle( reply->wait_handle );
}
SERVER_END_REQ;
if (status != STATUS_SUCCESS) break;
@ -2059,7 +2063,7 @@ NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORM
if (status == STATUS_PENDING) status = STATUS_SUCCESS;
break;
}
status = NtWaitForSingleObject( handle, alertable, timeout );
status = NtWaitForSingleObject( wait_handle, alertable, timeout );
if (status != WAIT_OBJECT_0) break;
}
*written = i ? i : 1;

View file

@ -5410,7 +5410,7 @@ struct remove_completion_reply
apc_param_t cvalue;
apc_param_t information;
unsigned int status;
char __pad_36[4];
obj_handle_t wait_handle;
};
@ -6711,7 +6711,7 @@ union generic_reply
/* ### protocol_version begin ### */
#define SERVER_PROTOCOL_VERSION 843
#define SERVER_PROTOCOL_VERSION 844
/* ### protocol_version end ### */

View file

@ -56,13 +56,71 @@ struct type_descr completion_type =
},
};
struct completion_wait
{
struct object obj;
obj_handle_t handle;
struct completion *completion;
struct thread *thread;
struct list wait_queue_entry;
};
struct completion
{
struct object obj;
struct list queue;
struct list wait_queue;
unsigned int depth;
};
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_destroy( struct object * );
static const struct object_ops completion_wait_ops =
{
sizeof(struct completion_wait), /* size */
&no_type, /* type */
completion_wait_dump, /* dump */
add_queue, /* add_queue */
remove_queue, /* remove_queue */
completion_wait_signaled, /* signaled */
no_satisfied, /* satisfied */
no_signal, /* signal */
no_get_fd, /* get_fd */
default_map_access, /* map_access */
default_get_sd, /* get_sd */
default_set_sd, /* set_sd */
no_get_full_name, /* get_full_name */
no_lookup_name, /* lookup_name */
no_link_name, /* link_name */
NULL, /* unlink_name */
no_open_file, /* open_file */
no_kernel_obj_list, /* get_kernel_obj_list */
no_close_handle, /* close_handle */
completion_wait_destroy /* destroy */
};
static void completion_wait_destroy( struct object *obj )
{
}
static void completion_wait_dump( struct object *obj, int verbose )
{
struct completion_wait *wait = (struct completion_wait *)obj;
assert( obj->ops == &completion_wait_ops );
fprintf( stderr, "Completion wait completion=%p\n", wait->completion );
}
static int completion_wait_signaled( struct object *obj, struct wait_queue_entry *entry )
{
struct completion_wait *wait = (struct completion_wait *)obj;
assert( obj->ops == &completion_wait_ops );
return wait->completion->depth;
}
static void completion_dump( struct object*, int );
static int completion_signaled( struct object *obj, struct wait_queue_entry *entry );
static void completion_destroy( struct object * );
@ -103,12 +161,19 @@ struct comp_msg
static void completion_destroy( struct object *obj)
{
struct completion *completion = (struct completion *) obj;
struct completion_wait *wait, *wait_next;
struct comp_msg *tmp, *next;
LIST_FOR_EACH_ENTRY_SAFE( tmp, next, &completion->queue, struct comp_msg, queue_entry )
{
free( tmp );
}
LIST_FOR_EACH_ENTRY_SAFE( wait, wait_next, &completion->wait_queue, struct completion_wait, wait_queue_entry )
{
assert( wait->completion );
cleanup_thread_completion( wait->thread );
}
}
static void completion_dump( struct object *obj, int verbose )
@ -126,6 +191,35 @@ static int completion_signaled( struct object *obj, struct wait_queue_entry *ent
return !list_empty( &completion->queue );
}
void cleanup_thread_completion( struct thread *thread )
{
if (!thread->completion_wait) return;
if (thread->completion_wait->handle)
{
close_handle( thread->process, thread->completion_wait->handle );
thread->completion_wait->handle = 0;
}
list_remove( &thread->completion_wait->wait_queue_entry );
release_object( &thread->completion_wait->obj );
thread->completion_wait = NULL;
}
static struct completion_wait *create_completion_wait( struct thread *thread )
{
struct completion_wait *wait;
if (!(wait = alloc_object( &completion_wait_ops ))) return NULL;
wait->completion = NULL;
wait->thread = thread;
if (!(wait->handle = alloc_handle( current->process, wait, SYNCHRONIZE, 0 )))
{
release_object( &wait->obj );
return NULL;
}
return wait;
}
static struct completion *create_completion( struct object *root, const struct unicode_str *name,
unsigned int attr, unsigned int concurrent,
const struct security_descriptor *sd )
@ -137,6 +231,7 @@ static struct completion *create_completion( struct object *root, const struct u
if (get_error() != STATUS_OBJECT_NAME_EXISTS)
{
list_init( &completion->queue );
list_init( &completion->wait_queue );
completion->depth = 0;
}
}
@ -153,6 +248,7 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_
unsigned int status, apc_param_t information )
{
struct comp_msg *msg = mem_alloc( sizeof( *msg ) );
struct completion_wait *wait;
if (!msg)
return;
@ -164,7 +260,12 @@ void add_completion( struct completion *completion, apc_param_t ckey, apc_param_
list_add_tail( &completion->queue, &msg->queue_entry );
completion->depth++;
wake_up( &completion->obj, 1 );
LIST_FOR_EACH_ENTRY( wait, &completion->wait_queue, struct completion_wait, wait_queue_entry )
{
wake_up( &wait->obj, 1 );
if (list_empty( &completion->queue )) return;
}
if (!list_empty( &completion->queue )) wake_up( &completion->obj, 0 );
}
/* create a completion */
@ -219,8 +320,22 @@ DECL_HANDLER(remove_completion)
if (!completion) return;
entry = list_head( &completion->queue );
if (current->completion_wait)
{
list_remove( &current->completion_wait->wait_queue_entry );
}
else if (!(current->completion_wait = create_completion_wait( current )))
{
release_object( completion );
return;
}
current->completion_wait->completion = completion;
list_add_head( &completion->wait_queue, &current->completion_wait->wait_queue_entry );
if (!entry)
{
reply->wait_handle = current->completion_wait->handle;
set_error( STATUS_PENDING );
}
else
{
list_remove( entry );
@ -231,6 +346,7 @@ DECL_HANDLER(remove_completion)
reply->status = msg->status;
reply->information = msg->information;
free( msg );
reply->wait_handle = 0;
}
release_object( completion );

View file

@ -238,6 +238,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 void add_completion( struct completion *completion, apc_param_t ckey, apc_param_t cvalue,
unsigned int status, apc_param_t information );
extern void cleanup_thread_completion( struct thread *thread );
/* serial port functions */

View file

@ -3798,6 +3798,7 @@ typedef union
apc_param_t cvalue; /* completion value */
apc_param_t information; /* IO_STATUS_BLOCK Information */
unsigned int status; /* completion result */
obj_handle_t wait_handle; /* handle to completion wait internal object */
@END

View file

@ -2240,6 +2240,7 @@ C_ASSERT( FIELD_OFFSET(struct remove_completion_reply, ckey) == 8 );
C_ASSERT( FIELD_OFFSET(struct remove_completion_reply, cvalue) == 16 );
C_ASSERT( FIELD_OFFSET(struct remove_completion_reply, information) == 24 );
C_ASSERT( FIELD_OFFSET(struct remove_completion_reply, status) == 32 );
C_ASSERT( FIELD_OFFSET(struct remove_completion_reply, wait_handle) == 36 );
C_ASSERT( sizeof(struct remove_completion_reply) == 40 );
C_ASSERT( FIELD_OFFSET(struct query_completion_request, handle) == 12 );
C_ASSERT( sizeof(struct query_completion_request) == 16 );

View file

@ -249,6 +249,7 @@ static inline void init_thread_structure( struct thread *thread )
thread->creation_time = current_time;
thread->exit_time = 0;
thread->completion_wait = NULL;
list_init( &thread->mutex_list );
list_init( &thread->system_apc );
@ -402,6 +403,7 @@ static void cleanup_thread( struct thread *thread )
{
int i;
cleanup_thread_completion( thread );
if (thread->context)
{
thread->context->status = STATUS_ACCESS_DENIED;

View file

@ -31,6 +31,7 @@ struct thread_apc;
struct debug_obj;
struct debug_event;
struct msg_queue;
struct completion_wait;
enum run_state
{
@ -91,6 +92,7 @@ struct thread
struct list kernel_object; /* list of kernel object pointers */
data_size_t desc_len; /* thread description length in bytes */
WCHAR *desc; /* thread description string */
struct completion_wait *completion_wait; /* completion port wait object the thread is associated with */
};
extern struct thread *current;

View file

@ -4527,6 +4527,7 @@ static void dump_remove_completion_reply( const struct remove_completion_reply *
dump_uint64( ", cvalue=", &req->cvalue );
dump_uint64( ", information=", &req->information );
fprintf( stderr, ", status=%08x", req->status );
fprintf( stderr, ", wait_handle=%04x", req->wait_handle );
}
static void dump_query_completion_request( const struct query_completion_request *req )