The test reproduces a hang by Planet Zoo when exiting the game.
Signed-off-by: Alexey Prokhin alexey@prokhin.ru --- v2: Update kernelbase patch. Supersedes 184509
Given it is one of my first contributions to wine, I have a few questions/notes:
1.
LPTHREAD_START_ROUTINE test_routines[] = {
I didn't find such pattern anywhere else in the tests, but I hope it's ok.
2.
ok( info[0].CompletionKey == 0xdeadbeef || info[0].CompletionKey == 0,
I am not sure if info should be checked at all in the case. And if it is, should I mark the second equality as broken? (Because info is only zeroed out on win server 2008)
3. Is it ok there is a test for GetQueuedCompletionStatus. Or should it be moved to kernelbase?
4. Should a test for GetQueuedCompletionStatusEx also be added? --- dlls/ntdll/tests/file.c | 101 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+)
diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 31c18454f0..c5fb28e16f 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -4953,6 +4953,106 @@ static void test_file_readonly_access(void) DeleteFileW(path); }
+static DWORD WINAPI io_thread_NtRemoveIoCompletion(void *data) +{ + LARGE_INTEGER timeout; + IO_STATUS_BLOCK iosb; + ULONG_PTR key, value; + HANDLE h = data; + NTSTATUS res; + + timeout.QuadPart = -30000000; + res = pNtRemoveIoCompletion( h, &key, &value, &iosb, &timeout ); + ok( res == STATUS_ABANDONED_WAIT_0 || broken( res == STATUS_TIMEOUT ), /* XP/2003 */ + "expected STATUS_ABANDONED_WAIT_0, got %#x\n", res ); + /* key, value and iosb either are unchanged or contain garbage */ + + return 0; +} + +static DWORD WINAPI io_thread_NtRemoveIoCompletionEx(void *data) +{ + FILE_IO_COMPLETION_INFORMATION info[2] = {{0}}; + LARGE_INTEGER timeout; + HANDLE h = data; + NTSTATUS res; + ULONG count; + + if (!pNtRemoveIoCompletionEx) + { + skip("NtRemoveIoCompletionEx() not present\n"); + return 1; + } + + timeout.QuadPart = -30000000; + count = 0xdeadbeef; + info[0].CompletionKey = 0xdeadbeef; + info[0].CompletionValue = 0xdeadbeef; + info[0].IoStatusBlock.Information = 0xdeadbeef; + U(info[0].IoStatusBlock).Status = 0xdeadbeef; + + res = pNtRemoveIoCompletionEx( h, info, 2, &count, &timeout, FALSE ); + ok( res == STATUS_ABANDONED_WAIT_0, "expected STATUS_ABANDONED_WAIT_0, got %#x\n", res ); + ok( count == 1, "wrong count %u\n", count ); + ok( info[0].CompletionKey == 0xdeadbeef || info[0].CompletionKey == 0, + "wrong key %#lx\n", info[0].CompletionKey ); + ok( info[0].CompletionValue == 0xdeadbeef || info[0].CompletionValue == 0, + "wrong value %#lx\n", info[0].CompletionValue ); + ok( info[0].IoStatusBlock.Information == 0xdeadbeef || info[0].IoStatusBlock.Information == 0, + "wrong information %#lx\n", info[0].IoStatusBlock.Information ); + ok( U(info[0].IoStatusBlock).Status == 0xdeadbeef || U(info[0].IoStatusBlock).Status == 0, + "wrong status %#x\n", U(info[0].IoStatusBlock).Status); + + return 0; +} + +static DWORD WINAPI io_thread_GetQueuedCompletionStatus(void *data) +{ + HANDLE h = data; + OVERLAPPED *pov; + NTSTATUS error; + ULONG_PTR key; + DWORD count; + BOOL ret; + + pov = (void *)0xdeadbeaf; + SetLastError(0xdeadbeef); + ret = GetQueuedCompletionStatus( h, &count, &key, &pov, 3000 ); + error = GetLastError(); + ok( !ret, "GetQueuedCompletionStatus succeeded\n" ); + ok( error == ERROR_ABANDONED_WAIT_0 || broken( error == WAIT_TIMEOUT ), /* XP/2003 */ + "expected ERROR_ABANDONED_WAIT_0, got %#x\n", error ); + ok( pov == NULL, "expected NULL, got %p\n", pov ); + + return 0; +} + +static void test_close_io_completion(void) +{ + HANDLE h, hThread; + NTSTATUS res; + LPTHREAD_START_ROUTINE test_routines[] = { + io_thread_NtRemoveIoCompletion, + io_thread_NtRemoveIoCompletionEx, + io_thread_GetQueuedCompletionStatus + }; + + for (int i = 0; i < ARRAY_SIZE(test_routines); ++i) + { + res = pNtCreateIoCompletion( &h, IO_COMPLETION_ALL_ACCESS, NULL, 0 ); + ok( res == STATUS_SUCCESS, "%d: NtCreateIoCompletion failed: %#x\n", i, res ); + ok( h && h != INVALID_HANDLE_VALUE, "%d: got invalid handle %p\n", i, h ); + + hThread = CreateThread( NULL, 0, test_routines[i], h, 0, NULL ); + Sleep( 400 ); + + CloseHandle( h ); + + ok( WaitForSingleObject( hThread, 10000 ) == 0, "%d: wait failed\n", i ); + CloseHandle( hThread ); + } +} + START_TEST(file) { HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); @@ -5023,4 +5123,5 @@ START_TEST(file) test_query_attribute_information_file(); test_ioctl(); test_flush_buffers_file(); + test_close_io_completion(); }