From: Matteo Bruni mbruni@codeweavers.com
--- dlls/ntdll/unix/file.c | 51 ++++++++++++++++++++----- include/wine/server_protocol.h | 5 ++- server/async.c | 69 ++++++++++++++++++++++++++++++---- server/protocol.def | 3 ++ server/request_handlers.h | 2 + server/request_trace.h | 8 +++- 6 files changed, 120 insertions(+), 18 deletions(-)
diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c index aacd96f5a3d..f934bcc24e7 100644 --- a/dlls/ntdll/unix/file.c +++ b/dlls/ntdll/unix/file.c @@ -6424,20 +6424,53 @@ NTSTATUS WINAPI NtFlushBuffersFileEx( HANDLE handle, ULONG flags, void *params, static NTSTATUS cancel_io( HANDLE handle, IO_STATUS_BLOCK *io, IO_STATUS_BLOCK *io_status, BOOL only_thread ) { - unsigned int status; + unsigned int status, i, count = 8, size; + obj_handle_t *cancelled_handles = NULL; + data_size_t handles_size = 0; + union select_op select_op;
- SERVER_START_REQ( cancel_async ) + for (;;) { - req->handle = wine_server_obj_handle( handle ); - req->iosb = wine_server_client_ptr( io ); - req->only_thread = only_thread; - if (!(status = wine_server_call( req ))) + size = count * sizeof(*cancelled_handles); + cancelled_handles = malloc( size ); + if (!cancelled_handles) + return STATUS_NO_MEMORY; + + SERVER_START_REQ( cancel_async ) { - io_status->Status = status; - io_status->Information = 0; + req->handle = wine_server_obj_handle( handle ); + req->iosb = wine_server_client_ptr( io ); + req->only_thread = only_thread; + wine_server_set_reply( req, cancelled_handles, size ); + if (!(status = wine_server_call( req ))) + { + io_status->Status = status; + io_status->Information = 0; + handles_size = wine_server_reply_size( reply ); + } + else if (status == STATUS_BUFFER_OVERFLOW) + { + free( cancelled_handles ); + count = reply->handles_size / sizeof(*cancelled_handles); + } } + SERVER_END_REQ; + if (status != STATUS_BUFFER_OVERFLOW) + break; + } + + if (handles_size) + { + for (i = 0; i < handles_size / sizeof(*cancelled_handles); ++i) + { + select_op.wait.op = SELECT_WAIT; + select_op.wait.handles[0] = cancelled_handles[i]; + + server_select( &select_op, offsetof(union select_op, wait.handles[1]), + SELECT_INTERRUPTIBLE, TIMEOUT_INFINITE, NULL, NULL ); + } + free( cancelled_handles ); } - SERVER_END_REQ;
return status; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 3cdc9375e2d..4a5075ea33f 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3238,6 +3238,9 @@ struct cancel_async_request struct cancel_async_reply { struct reply_header __header; + data_size_t handles_size; + /* VARARG(handles,uints,handles_size); */ + char __pad_12[4]; };
@@ -6798,6 +6801,6 @@ union generic_reply struct set_keyboard_repeat_reply set_keyboard_repeat_reply; };
-#define SERVER_PROTOCOL_VERSION 863 +#define SERVER_PROTOCOL_VERSION 864
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/async.c b/server/async.c index d2d929c9709..c697a2e6a02 100644 --- a/server/async.c +++ b/server/async.c @@ -63,6 +63,7 @@ struct async unsigned int comp_flags; /* completion flags */ async_completion_callback completion_callback; /* callback to be called on completion */ void *completion_callback_private; /* argument to completion_callback */ + struct list canceled_entry; };
static void async_dump( struct object *obj, int verbose ); @@ -118,8 +119,8 @@ static void async_satisfied( struct object *obj, struct wait_queue_entry *entry struct async *async = (struct async *)obj; assert( obj->ops == &async_ops );
- /* we only return an async handle for asyncs created via create_request_async() */ - assert( async->iosb ); + if (!async->iosb) + return;
if (async->direct_result) { @@ -574,7 +575,27 @@ int async_waiting( struct async_queue *queue ) return !async->terminated; }
-static int cancel_async( struct process *process, struct object *obj, struct thread *thread, client_ptr_t iosb ) +static int cancel_count_async( struct process *process, struct object *obj, struct thread *thread, + client_ptr_t iosb ) +{ + struct async *async; + int count = 0; + + LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) + { + if (async->terminated || async->canceled || async->is_system) continue; + if ((!obj || (get_fd_user( async->fd ) == obj)) && + (!thread || async->thread == thread) && + (!iosb || async->data.iosb == iosb)) + { + count++; + } + } + return count; +} + +static int cancel_async( struct process *process, struct object *obj, struct thread *thread, + client_ptr_t iosb, struct list *canceled_list ) { struct async *async; int woken = 0; @@ -592,7 +613,9 @@ restart: (!iosb || async->data.iosb == iosb)) { async->canceled = 1; + async->signaled = 0; fd_cancel_async( async->fd, async ); + list_add_tail( canceled_list, &async->canceled_entry ); woken++; goto restart; } @@ -815,12 +838,44 @@ DECL_HANDLER(cancel_async) { struct object *obj = get_handle_obj( current->process, req->handle, 0, NULL ); struct thread *thread = req->only_thread ? current : NULL; + struct list canceled_list = LIST_INIT(canceled_list); + unsigned int count = 0, capacity, i; + struct async *async, *next_async; + obj_handle_t *ptr; + + if (!obj) + return; + + count = cancel_count_async( current->process, obj, thread, req->iosb ); + if (!count && !thread) set_error( STATUS_NOT_FOUND ); + release_object( obj ); + capacity = get_reply_max_size() / sizeof(*ptr);
- if (obj) + if (count) { - int count = cancel_async( current->process, obj, thread, req->iosb ); - if (!count && !thread) set_error( STATUS_NOT_FOUND ); - release_object( obj ); + if (count > capacity) + { + set_error( STATUS_BUFFER_OVERFLOW ); + reply->handles_size = count * sizeof(*ptr); + return; + } + count = cancel_async( current->process, obj, thread, req->iosb, &canceled_list ); + count = min( count, capacity ); + i = 0; + if ((ptr = set_reply_data_size( count * sizeof(*ptr) ))) + { + LIST_FOR_EACH_ENTRY_SAFE( async, next_async, &canceled_list, struct async, canceled_entry ) + { + if (i++ < capacity) + { + if (!async->wait_handle) + async->wait_handle = alloc_handle( current->process, async, SYNCHRONIZE, 0 ); + *ptr++ = async->wait_handle; + } + list_remove( &async->canceled_entry ); + } + reply->handles_size = count * sizeof(*ptr); + } } }
diff --git a/server/protocol.def b/server/protocol.def index 63bb0111473..7cf330f2cf2 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2431,6 +2431,9 @@ enum message_type obj_handle_t handle; /* handle to comm port, socket or file */ client_ptr_t iosb; /* I/O status block (NULL=all) */ int only_thread; /* cancel matching this thread */ +@REPLY + data_size_t handles_size; + VARARG(handles,uints,handles_size); /* handles list */ @END
diff --git a/server/request_handlers.h b/server/request_handlers.h index 2afc9707869..4652f1808d9 100644 --- a/server/request_handlers.h +++ b/server/request_handlers.h @@ -1386,6 +1386,8 @@ C_ASSERT( offsetof(struct cancel_async_request, handle) == 12 ); C_ASSERT( offsetof(struct cancel_async_request, iosb) == 16 ); C_ASSERT( offsetof(struct cancel_async_request, only_thread) == 24 ); C_ASSERT( sizeof(struct cancel_async_request) == 32 ); +C_ASSERT( offsetof(struct cancel_async_reply, handles_size) == 8 ); +C_ASSERT( sizeof(struct cancel_async_reply) == 16 ); C_ASSERT( offsetof(struct get_async_result_request, user_arg) == 16 ); C_ASSERT( sizeof(struct get_async_result_request) == 24 ); C_ASSERT( sizeof(struct get_async_result_reply) == 8 ); diff --git a/server/request_trace.h b/server/request_trace.h index bd961791024..91154a3ce87 100644 --- a/server/request_trace.h +++ b/server/request_trace.h @@ -1552,6 +1552,12 @@ static void dump_cancel_async_request( const struct cancel_async_request *req ) fprintf( stderr, ", only_thread=%d", req->only_thread ); }
+static void dump_cancel_async_reply( const struct cancel_async_reply *req ) +{ + fprintf( stderr, " handles_size=%u", req->handles_size ); + dump_varargs_uints( ", handles=", min( cur_size, req->handles_size )); +} + static void dump_get_async_result_request( const struct get_async_result_request *req ) { dump_uint64( " user_arg=", &req->user_arg ); @@ -3792,7 +3798,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = NULL, NULL, NULL, - NULL, + (dump_func)dump_cancel_async_reply, (dump_func)dump_get_async_result_reply, (dump_func)dump_set_async_direct_result_reply, (dump_func)dump_read_reply,