Commit 6360f569 authored by Paul Gofman's avatar Paul Gofman Committed by Alexandre Julliard

ws2_32/tests: Add test for async cancel on socket's last process handle close.

parent f311bb5f
......@@ -33,6 +33,24 @@
#define TIMEOUT_INFINITE _I64_MAX
static HANDLE create_process(const char *arg)
{
STARTUPINFOA si = { 0 };
PROCESS_INFORMATION pi;
char cmdline[MAX_PATH];
char **argv;
BOOL ret;
si.cb = sizeof(si);
winetest_get_mainargs(&argv);
sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg);
ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
ok(ret, "got %lu.\n", GetLastError());
ret = CloseHandle(pi.hThread);
ok(ret, "got %lu.\n", GetLastError());
return pi.hProcess;
}
static void tcp_socketpair_flags(SOCKET *src, SOCKET *dst, DWORD flags)
{
SOCKET server = INVALID_SOCKET;
......@@ -2464,11 +2482,11 @@ static NTSTATUS WINAPI thread_NtDeviceIoControlFile(BOOL kill_thread, HANDLE han
return p.ret;
}
static unsigned int test_async_thread_termination_apc_count;
static unsigned int test_apc_count;
static void WINAPI test_async_thread_termination_apc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved )
static void WINAPI test_apc_proc( void *arg, IO_STATUS_BLOCK *iosb, ULONG reserved )
{
++test_async_thread_termination_apc_count;
++test_apc_count;
}
static void test_async_thread_termination(void)
......@@ -2486,18 +2504,18 @@ static void test_async_thread_termination(void)
{TRUE, TRUE, NULL, NULL},
{FALSE, FALSE, NULL, NULL},
{TRUE, FALSE, NULL, NULL},
{FALSE, TRUE, test_async_thread_termination_apc, NULL},
{TRUE, TRUE, test_async_thread_termination_apc, NULL},
{FALSE, FALSE, test_async_thread_termination_apc, NULL},
{TRUE, FALSE, test_async_thread_termination_apc, NULL},
{FALSE, TRUE, test_apc_proc, NULL},
{TRUE, TRUE, test_apc_proc, NULL},
{FALSE, FALSE, test_apc_proc, NULL},
{TRUE, FALSE, test_apc_proc, NULL},
{FALSE, TRUE, NULL, (void *)0xdeadbeef},
{TRUE, TRUE, NULL, (void *)0xdeadbeef},
{FALSE, FALSE, NULL, (void *)0xdeadbeef},
{TRUE, FALSE, NULL, (void *)0xdeadbeef},
{FALSE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef},
{TRUE, TRUE, test_async_thread_termination_apc, (void *)0xdeadbeef},
{FALSE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef},
{TRUE, FALSE, test_async_thread_termination_apc, (void *)0xdeadbeef},
{FALSE, TRUE, test_apc_proc, (void *)0xdeadbeef},
{TRUE, TRUE, test_apc_proc, (void *)0xdeadbeef},
{FALSE, FALSE, test_apc_proc, (void *)0xdeadbeef},
{TRUE, FALSE, test_apc_proc, (void *)0xdeadbeef},
};
const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
......@@ -2550,7 +2568,7 @@ static void test_async_thread_termination(void)
}
SleepEx(0, TRUE);
ok(!test_async_thread_termination_apc_count, "got APC.\n");
ok(!test_apc_count, "got APC.\n");
port = CreateIoCompletionPort((HANDLE)listener, NULL, 0, 0);
......@@ -2754,12 +2772,153 @@ static void test_read_write(void)
CloseHandle(event);
}
static void test_async_cancel_on_handle_close(void)
{
static const struct
{
BOOL event;
PIO_APC_ROUTINE apc;
void *apc_context;
}
tests[] =
{
{TRUE, NULL, NULL},
{FALSE, NULL, NULL},
{TRUE, test_apc_proc, NULL},
{FALSE, test_apc_proc, NULL},
{TRUE, NULL, (void *)0xdeadbeef},
{FALSE, NULL, (void *)0xdeadbeef},
{TRUE, test_apc_proc, (void *)0xdeadbeef},
{FALSE, test_apc_proc, (void *)0xdeadbeef},
};
const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
char in_buffer[offsetof(struct afd_poll_params, sockets[3])];
char out_buffer[offsetof(struct afd_poll_params, sockets[3])];
struct afd_poll_params *in_params = (struct afd_poll_params *)in_buffer;
struct afd_poll_params *out_params = (struct afd_poll_params *)out_buffer;
unsigned int i, other_process;
LARGE_INTEGER zero = {{0}};
HANDLE process_handle;
ULONG_PTR key, value;
IO_STATUS_BLOCK io;
HANDLE event, port;
ULONG params_size;
SOCKET listener;
HANDLE handle2;
DWORD ret;
BOOL bret;
process_handle = create_process("sleep");
event = CreateEventW(NULL, FALSE, FALSE, NULL);
in_params->count = 1;
in_params->exclusive = FALSE;
in_params->sockets[0].flags = ~0;
in_params->sockets[0].status = 0xdeadbeef;
params_size = offsetof(struct afd_poll_params, sockets[1]);
in_params->timeout = -10 * 1000 * 1000 * 5;
for (other_process = 0; other_process < 2; ++other_process)
{
for (i = 0; i < ARRAY_SIZE(tests); ++i)
{
winetest_push_context("other_process %u, i %u", other_process, i);
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ret = bind(listener, (const struct sockaddr *)&bind_addr, sizeof(bind_addr));
ok(!ret, "got error %u\n", WSAGetLastError());
ret = listen(listener, 1);
ok(!ret, "got error %u\n", WSAGetLastError());
port = CreateIoCompletionPort((HANDLE)listener, NULL, 0, 0);
ok(!!port, "got %p.\n", port);
in_params->sockets[0].socket = listener;
memset(&io, 0xcc, sizeof(io));
ResetEvent(event);
ret = NtDeviceIoControlFile((HANDLE)listener, tests[i].event ? event : NULL,
tests[i].apc, tests[i].apc_context, &io, IOCTL_AFD_POLL, in_params, params_size,
out_params, params_size);
if (tests[i].apc)
{
ok(ret == STATUS_INVALID_PARAMETER, "got %#lx\n", ret);
winetest_pop_context();
continue;
}
ok(ret == STATUS_PENDING, "got %#lx.\n", ret);
ok(io.Status == 0xcccccccc, "got %#lx.\n", io.Status);
bret = DuplicateHandle(GetCurrentProcess(), (HANDLE)listener,
other_process ? process_handle : GetCurrentProcess(),
&handle2, 0, FALSE, DUPLICATE_SAME_ACCESS);
ok(bret, "failed, error %lu.\n", GetLastError());
closesocket(listener);
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);
}
else
{
ok(ret == WAIT_TIMEOUT, "got %#lx\n", ret);
}
ret = WaitForSingleObject(event, 0);
ok(ret == WAIT_TIMEOUT, "got %#lx.\n", ret);
if (other_process)
{
bret = DuplicateHandle(process_handle, handle2, GetCurrentProcess(), (HANDLE *)&listener, 0, FALSE,
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
ok(bret, "failed, error %lu.\n", GetLastError());
}
else
{
listener = (SOCKET)handle2;
}
CloseHandle((HANDLE)listener);
CloseHandle(port);
winetest_pop_context();
}
}
CloseHandle(event);
TerminateProcess(process_handle, 0);
WaitForSingleObject(process_handle, INFINITE);
CloseHandle(process_handle);
}
START_TEST(afd)
{
WSADATA data;
char **argv;
int argc;
WSAStartup(MAKEWORD(2, 2), &data);
argc = winetest_get_mainargs(&argv);
if (argc >= 3)
{
if (!strcmp(argv[2], "sleep"))
{
Sleep(5000);
return;
}
return;
}
test_open_device();
test_poll();
test_poll_exclusive();
......@@ -2773,6 +2932,7 @@ START_TEST(afd)
test_getsockname();
test_async_thread_termination();
test_read_write();
test_async_cancel_on_handle_close();
WSACleanup();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment