From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/pipe.c | 5 ++++- dlls/ws2_32/tests/afd.c | 11 +++++++---- server/async.c | 16 ++++++++++++++++ server/file.h | 1 + server/handle.c | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c index c9cfdb03826..a1115bf47b9 100644 --- a/dlls/ntdll/tests/pipe.c +++ b/dlls/ntdll/tests/pipe.c @@ -3067,9 +3067,12 @@ static void test_async_cancel_on_handle_close(void) ok(bret, "failed, error %lu.\n", GetLastError());
CloseHandle(read); + /* Canceled asyncs with completion port and no event do not update IOSB before removing completion. */ + todo_wine_if(other_process && tests[i].apc_context && !tests[i].event) ok(io.Status == 0xcccccccc, "got %#lx.\n", io.Status); + if (other_process && tests[i].apc_context && !tests[i].event) - todo_wine test_queued_completion(port, &io, STATUS_CANCELLED, 0); + test_queued_completion(port, &io, STATUS_CANCELLED, 0); else test_no_queued_completion(port);
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index ef3e6385c7f..97139605bf1 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -2858,17 +2858,20 @@ static void test_async_cancel_on_handle_close(void)
closesocket(listener);
+ /* Canceled asyncs with completion port and no event do not update IOSB before removing completion. */ + todo_wine_if(other_process && tests[i].apc_context && !tests[i].event) ok(io.Status == 0xcccccccc, "got %#lx\n", io.Status); + memset(&io, 0xcc, sizeof(io)); key = 0xcc; value = 0; ret = NtRemoveIoCompletion(port, &key, &value, &io, &zero); if (other_process && tests[i].apc_context && !tests[i].event) { - todo_wine ok(!ret, "got %#lx\n", ret); - todo_wine ok(!key, "got key %#Ix\n", key); - todo_wine ok(value == 0xdeadbeef, "got value %#Ix\n", value); - todo_wine ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status); + ok(!ret, "got %#lx\n", ret); + ok(!key, "got key %#Ix\n", key); + ok(value == 0xdeadbeef, "got value %#Ix\n", value); + ok(io.Status == STATUS_CANCELLED, "got %#lx\n", io.Status); } else { diff --git a/server/async.c b/server/async.c index 26946b5f5ce..b1d09129938 100644 --- a/server/async.c +++ b/server/async.c @@ -614,6 +614,22 @@ void cancel_process_asyncs( struct process *process ) cancel_async( process, NULL, NULL, 0 ); }
+void cancel_asyncs_on_handles_closed( struct process *process, struct object *obj ) +{ + struct async *async; + +restart: + LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) + { + if (async->terminated || async->canceled || get_fd_user( async->fd ) != obj) continue; + if (!async->completion || !async->data.apc_context || async->event) continue; + + async->canceled = 1; + fd_cancel_async( async->fd, async ); + goto restart; + } +} + void cancel_terminating_thread_asyncs( struct thread *thread ) { struct async *async; diff --git a/server/file.h b/server/file.h index 210fcec5e78..778a8c7a065 100644 --- a/server/file.h +++ b/server/file.h @@ -246,6 +246,7 @@ extern struct thread *async_get_thread( struct async *async ); extern struct async *find_pending_async( struct async_queue *queue ); extern void cancel_process_asyncs( struct process *process ); extern void cancel_terminating_thread_asyncs( struct thread *thread ); +extern void cancel_asyncs_on_handles_closed( struct process *process, struct object *obj );
static inline void init_async_queue( struct async_queue *queue ) { diff --git a/server/handle.c b/server/handle.c index 38ad80da267..5f48e458431 100644 --- a/server/handle.c +++ b/server/handle.c @@ -37,6 +37,7 @@ #include "process.h" #include "thread.h" #include "security.h" +#include "file.h" #include "request.h"
struct handle_entry @@ -417,6 +418,21 @@ struct handle_table *copy_handle_table( struct process *process, struct process return table; }
+/* return number of open handles to the object in the process */ +static unsigned int get_obj_handle_count( struct process *process, const struct object *obj ) +{ + struct handle_table *table = process->handles; + struct handle_entry *ptr; + unsigned int count = 0; + int i; + + if (!table) return 0; + + for (i = 0, ptr = table->entries; i <= table->last; i++, ptr++) + if (ptr->ptr == obj) ++count; + return count; +} + /* close a handle and decrement the refcount of the associated object */ unsigned int close_handle( struct process *process, obj_handle_t handle ) { @@ -428,6 +444,8 @@ unsigned int close_handle( struct process *process, obj_handle_t handle ) if (entry->access & RESERVED_CLOSE_PROTECT) return STATUS_HANDLE_NOT_CLOSABLE; obj = entry->ptr; if (!obj->ops->close_handle( obj, process, handle )) return STATUS_HANDLE_NOT_CLOSABLE; + if (obj->handle_count == 1 || get_obj_handle_count( process, obj ) == 1) + cancel_asyncs_on_handles_closed( process, obj ); entry->ptr = NULL; table = handle_is_global(handle) ? global_table : process->handles; if (entry < table->entries + table->free) table->free = entry - table->entries;