From: Paul Gofman pgofman@codeweavers.com
--- dlls/ntdll/tests/file.c | 26 ++++++++++++++++++++++++++ dlls/ntdll/unix/sync.c | 14 +++++++++++--- server/completion.c | 6 ++++-- server/protocol.def | 1 + 4 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 0adc0998ee2..2e90b0d3177 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -966,6 +966,32 @@ static void test_set_io_completion(void) ok( res == STATUS_SUCCESS, "NtCreateIoCompletion failed: %#lx\n", res ); ok( h && h != INVALID_HANDLE_VALUE, "got invalid handle %p\n", h );
+ apc_count = 0; + QueueUserAPC( user_apc_proc, GetCurrentThread(), (ULONG_PTR)&apc_count ); + res = pNtSetIoCompletion( h, 123, 456, 789, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, TRUE ); + /* Before a thread is associated with completion port APC takes priority over pending completion. */ + ok( res == STATUS_USER_APC, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count <= 1, "wrong count %lu\n", count ); + ok( apc_count == 1, "wrong apc count %u\n", apc_count ); + + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, TRUE ); + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletion failed: %#lx\n", res ); + ok( count == 1, "wrong count %lu\n", count ); + + apc_count = 0; + QueueUserAPC( user_apc_proc, GetCurrentThread(), (ULONG_PTR)&apc_count ); + res = pNtSetIoCompletion( h, 123, 456, 789, size ); + ok( res == STATUS_SUCCESS, "NtSetIoCompletion failed: %#lx\n", res ); + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, TRUE ); + /* After a thread is associated with completion port existing completion is returned if APC is pending. */ + ok( res == STATUS_SUCCESS, "NtRemoveIoCompletionEx failed: %#lx\n", res ); + ok( count == 1, "wrong count %lu\n", count ); + ok( apc_count == 0, "wrong apc count %u\n", apc_count ); + SleepEx( 0, TRUE); + ok( apc_count == 1, "wrong apc count %u\n", apc_count ); + res = pNtRemoveIoCompletion( h, &key, &value, &iosb, &timeout ); ok( res == STATUS_TIMEOUT, "NtRemoveIoCompletion failed: %#lx\n", res );
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index f5536e398b5..80f82c18730 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -2007,6 +2007,7 @@ NTSTATUS WINAPI NtRemoveIoCompletion( HANDLE handle, ULONG_PTR *key, ULONG_PTR * SERVER_START_REQ( remove_completion ) { req->handle = wine_server_obj_handle( handle ); + req->alertable = 0; if (!(status = wine_server_call( req ))) { *key = reply->ckey; @@ -2055,6 +2056,7 @@ NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORM SERVER_START_REQ( remove_completion ) { req->handle = wine_server_obj_handle( handle ); + req->alertable = alertable; if (!(status = wine_server_call( req ))) { info[i].CompletionKey = reply->ckey; @@ -2068,13 +2070,19 @@ NTSTATUS WINAPI NtRemoveIoCompletionEx( HANDLE handle, FILE_IO_COMPLETION_INFORM if (status != STATUS_SUCCESS) break; ++i; } - if (i || status != STATUS_PENDING) + if (i || (status != STATUS_PENDING && status != STATUS_USER_APC)) { if (i) status = STATUS_SUCCESS; goto done; } - if (!timeout || timeout->QuadPart || alertable) status = NtWaitForSingleObject( wait_handle, alertable, timeout ); - else status = STATUS_TIMEOUT; + if (status == STATUS_USER_APC) + { + status = NtDelayExecution( TRUE, NULL ); + assert( status == STATUS_USER_APC ); + goto done; + } + if (!timeout || timeout->QuadPart) status = NtWaitForSingleObject( wait_handle, alertable, timeout ); + else status = STATUS_TIMEOUT; if (status != WAIT_OBJECT_0) goto done;
SERVER_START_REQ( get_thread_completion ) diff --git a/server/completion.c b/server/completion.c index c4b070ef888..d419a7e9b09 100644 --- a/server/completion.c +++ b/server/completion.c @@ -339,23 +339,25 @@ DECL_HANDLER(remove_completion) struct completion* completion = get_completion_obj( current->process, req->handle, IO_COMPLETION_MODIFY_STATE ); struct list *entry; struct comp_msg *msg; + BOOL alerted;
if (!completion) return;
entry = list_head( &completion->queue ); if (current->completion_wait && current->completion_wait->completion != completion) cleanup_thread_completion( current ); + alerted = req->alertable && !list_empty( ¤t->user_apc ) && !(entry && current->completion_wait); if (!current->completion_wait && !(current->completion_wait = create_completion_wait( completion, current ))) { release_object( completion ); return; } - if (!entry) + if (alerted || !entry) { list_remove( ¤t->completion_wait->wait_queue_entry ); list_add_head( &completion->wait_queue, ¤t->completion_wait->wait_queue_entry ); reply->wait_handle = current->completion_wait->handle; - set_error( STATUS_PENDING ); + set_error( alerted ? STATUS_USER_APC : STATUS_PENDING ); } else { diff --git a/server/protocol.def b/server/protocol.def index 9028faa8992..e86bfea69fa 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3784,6 +3784,7 @@ typedef union /* get completion from completion port queue */ @REQ(remove_completion) obj_handle_t handle; /* port handle */ + int alertable; /* completion wait is alertable */ @REPLY apc_param_t ckey; /* completion key */ apc_param_t cvalue; /* completion value */