ws2_32/tests: Add some tests for sockets bound to a completion port.

Dmitry Timoshkov <[email protected]>
Newsgroups gmane.comp.emulators.wine.patches
Message-ID <[email protected]>
Signed-off-by: Dmitry Timoshkov <[email protected]>
---
 dlls/ws2_32/tests/sock.c | 707 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 705 insertions(+), 2 deletions(-)

diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c
index 137c0f681e..80577bbb34 100644
--- a/dlls/ws2_32/tests/sock.c
+++ b/dlls/ws2_32/tests/sock.c
@@ -4,6 +4,7 @@
  * Copyright 2002 Martin Wilck
  * Copyright 2005 Thomas Kho
  * Copyright 2008 Jeff Zaroyko
+ * Copyright 2017 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -88,6 +89,9 @@ static int   (WINAPI *pWSAPoll)(WSAPOLLFD *,ULONG,INT);
 static DWORD (WINAPI *pGetAdaptersInfo)(PIP_ADAPTER_INFO,PULONG);
 static DWORD (WINAPI *pGetIpForwardTable)(PMIB_IPFORWARDTABLE,PULONG,BOOL);
 
+/* Function pointers from ntdll */
+static DWORD (WINAPI *pNtClose)(HANDLE);
+
 /**************** Structs and typedefs ***************/
 
 typedef struct thread_info
@@ -274,6 +278,56 @@ end:
     return -1;
 }
 
+static int tcp_socketpair_ovl(SOCKET *src, SOCKET *dst)
+{
+    SOCKET server = INVALID_SOCKET;
+    struct sockaddr_in addr;
+    int len, ret;
+
+    *src = INVALID_SOCKET;
+    *dst = INVALID_SOCKET;
+
+    *src = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
+    if (*src == INVALID_SOCKET)
+        goto end;
+
+    server = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
+    if (server == INVALID_SOCKET)
+        goto end;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+    ret = bind(server, (struct sockaddr*)&addr, sizeof(addr));
+    if (ret != 0)
+        goto end;
+
+    len = sizeof(addr);
+    ret = getsockname(server, (struct sockaddr*)&addr, &len);
+    if (ret != 0)
+        goto end;
+
+    ret = listen(server, 1);
+    if (ret != 0)
+        goto end;
+
+    ret = connect(*src, (struct sockaddr*)&addr, sizeof(addr));
+    if (ret != 0)
+        goto end;
+
+    len = sizeof(addr);
+    *dst = accept(server, (struct sockaddr*)&addr, &len);
+
+end:
+    if (server != INVALID_SOCKET)
+        closesocket(server);
+    if (*src != INVALID_SOCKET && *dst != INVALID_SOCKET)
+        return 0;
+    closesocket(*src);
+    closesocket(*dst);
+    return -1;
+}
+
 static void set_so_opentype ( BOOL overlapped )
 {
     int optval = !overlapped, newval, len = sizeof (int);
@@ -1227,7 +1281,7 @@ static void Init (void)
 {
     WORD ver = MAKEWORD (2, 2);
     WSADATA data;
-    HMODULE hws2_32 = GetModuleHandleA("ws2_32.dll"), hiphlpapi;
+    HMODULE hws2_32 = GetModuleHandleA("ws2_32.dll"), hiphlpapi, ntdll;
 
     pfreeaddrinfo = (void *)GetProcAddress(hws2_32, "freeaddrinfo");
     pgetaddrinfo = (void *)GetProcAddress(hws2_32, "getaddrinfo");
@@ -1254,6 +1308,10 @@ static void Init (void)
         pGetAdaptersInfo = (void *)GetProcAddress(hiphlpapi, "GetAdaptersInfo");
     }
 
+    ntdll = LoadLibraryA("ntdll.dll");
+    if (ntdll)
+        pNtClose = (void *)GetProcAddress(ntdll, "NtClose");
+
     ok ( WSAStartup ( ver, &data ) == 0, "WSAStartup failed\n" );
     tls = TlsAlloc();
 }
@@ -10793,7 +10851,651 @@ todo_wine
     HeapFree(GetProcessHeap(), 0, name);
 }
 
-/**************** Main program  ***************/
+static void sync_read(SOCKET src, SOCKET dst)
+{
+    int ret;
+    char data[512];
+
+    ret = send(dst, "Hello World!", 12, 0);
+    ok(ret == 12, "send returned %d\n", ret);
+
+    memset(data, 0, sizeof(data));
+    ret = recv(src, data, sizeof(data), 0);
+    ok(ret == 12, "expected 12, got %d\n", ret);
+    ok(!memcmp(data, "Hello World!", 12), "got %u bytes (%*s)\n", ret, ret, data);
+}
+
+static void iocp_async_read(SOCKET src, SOCKET dst)
+{
+    HANDLE port;
+    WSAOVERLAPPED ovl, *ovl_iocp;
+    WSABUF buf;
+    int ret;
+    char data[512];
+    DWORD flags, bytes;
+    ULONG_PTR key;
+
+    memset(data, 0, sizeof(data));
+    memset(&ovl, 0, sizeof(ovl));
+
+    port = CreateIoCompletionPort((HANDLE)src, 0, 0x12345678, 0);
+    ok(port != 0, "CreateIoCompletionPort error %u\n", GetLastError());
+
+    buf.len = sizeof(data);
+    buf.buf = data;
+    bytes = 0xdeadbeef;
+    flags = 0;
+    SetLastError(0xdeadbeef);
+    ret = WSARecv(src, &buf, 1, &bytes, &flags, &ovl, NULL);
+    ok(ret == SOCKET_ERROR, "got %d\n", ret);
+    ok(GetLastError() == ERROR_IO_PENDING, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == WAIT_TIMEOUT, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+    ok(key == 0xdeadbeef, "got key %#lx\n", key);
+    ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+
+    ret = send(dst, "Hello World!", 12, 0);
+    ok(ret == 12, "send returned %d\n", ret);
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = NULL;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(ret, "got %d\n", ret);
+    ok(bytes == 12, "got bytes %u\n", bytes);
+    ok(key == 0x12345678, "got key %#lx\n", key);
+    ok(ovl_iocp == &ovl, "got ovl %p\n", ovl_iocp);
+    if (ovl_iocp)
+    {
+        ok(ovl_iocp->InternalHigh == 12, "got %#lx\n", ovl_iocp->InternalHigh);
+        ok(!ovl_iocp->Internal , "got %#lx\n", ovl_iocp->Internal);
+        ok(!memcmp(data, "Hello World!", 12), "got %u bytes (%*s)\n", bytes, bytes, data);
+    }
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == WAIT_TIMEOUT, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+    ok(key == 0xdeadbeef, "got key %#lx\n", key);
+    ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+
+    CloseHandle(port);
+}
+
+static void iocp_async_read_closesocket(SOCKET src, int how_to_close)
+{
+    HANDLE port;
+    WSAOVERLAPPED ovl, *ovl_iocp;
+    WSABUF buf;
+    int ret;
+    char data[512];
+    DWORD flags, bytes;
+    ULONG_PTR key;
+    HWND hwnd;
+    MSG msg;
+
+    hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP,
+                           0, 0, 0, 0, NULL, NULL, 0, NULL);
+    ok(hwnd != 0, "CreateWindowEx failed\n");
+
+    ret = WSAAsyncSelect(src, hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE);
+    ok(!ret, "got %d\n", ret);
+
+    Sleep(100);
+    memset(&msg, 0, sizeof(msg));
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(ret, "got %d\n", ret);
+    ok(msg.hwnd == hwnd, "got %p\n", msg.hwnd);
+    ok(msg.message == WM_SOCKET, "got %04x\n", msg.message);
+    ok(msg.wParam == src, "got %08lx\n", msg.wParam);
+    ok(msg.lParam == 2, "got %08lx\n", msg.lParam);
+
+    memset(data, 0, sizeof(data));
+    memset(&ovl, 0, sizeof(ovl));
+
+    port = CreateIoCompletionPort((HANDLE)src, 0, 0x12345678, 0);
+    ok(port != 0, "CreateIoCompletionPort error %u\n", GetLastError());
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    buf.len = sizeof(data);
+    buf.buf = data;
+    bytes = 0xdeadbeef;
+    flags = 0;
+    SetLastError(0xdeadbeef);
+    ret = WSARecv(src, &buf, 1, &bytes, &flags, &ovl, NULL);
+    ok(ret == SOCKET_ERROR, "got %d\n", ret);
+    ok(GetLastError() == ERROR_IO_PENDING, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == WAIT_TIMEOUT, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+    ok(key == 0xdeadbeef, "got key %#lx\n", key);
+    ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    switch (how_to_close)
+    {
+    case 0:
+        closesocket(src);
+        break;
+    case 2:
+        if (pNtClose)
+        {
+            pNtClose((HANDLE)src);
+            break;
+        }
+        win_skip("NtClose is not available on this platform");
+        how_to_close = 1;
+        /* fall through */
+    case 1:
+        CloseHandle((HANDLE)src);
+        break;
+    default:
+        ok(0, "wrong value %d\n", how_to_close);
+        break;
+    }
+
+    Sleep(100);
+    memset(&msg, 0, sizeof(msg));
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    switch (how_to_close)
+    {
+    case 0:
+        ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+        break;
+    case 1:
+    case 2:
+todo_wine
+{
+        ok(ret, "got %d\n", ret);
+        ok(msg.hwnd == hwnd, "got %p\n", msg.hwnd);
+        ok(msg.message == WM_SOCKET, "got %04x\n", msg.message);
+        ok(msg.wParam == src, "got %08lx\n", msg.wParam);
+        ok(msg.lParam == 0x20, "got %08lx\n", msg.lParam);
+}
+        break;
+    default:
+        ok(0, "wrong value %d\n", how_to_close);
+        break;
+    }
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = NULL;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+todo_wine
+    ok(GetLastError() == ERROR_CONNECTION_ABORTED || GetLastError() == ERROR_NETNAME_DELETED /* XP */, "got %u\n", GetLastError());
+    ok(!bytes, "got bytes %u\n", bytes);
+    ok(key == 0x12345678, "got key %#lx\n", key);
+    ok(ovl_iocp == &ovl, "got ovl %p\n", ovl_iocp);
+    if (ovl_iocp)
+    {
+        ok(!ovl_iocp->InternalHigh, "got %#lx\n", ovl_iocp->InternalHigh);
+        ok(ovl_iocp->Internal == STATUS_CONNECTION_ABORTED || STATUS_LOCAL_DISCONNECT /* XP */, "got %#lx\n", ovl_iocp->Internal);
+    }
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == WAIT_TIMEOUT, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+    ok(key == 0xdeadbeef, "got key %#lx\n", key);
+    ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+
+    CloseHandle(port);
+
+    DestroyWindow(hwnd);
+}
+
+static void iocp_async_closesocket(SOCKET src)
+{
+    HANDLE port;
+    WSAOVERLAPPED *ovl_iocp;
+    int ret;
+    DWORD bytes;
+    ULONG_PTR key;
+    HWND hwnd;
+    MSG msg;
+
+    hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP,
+                           0, 0, 0, 0, NULL, NULL, 0, NULL);
+    ok(hwnd != 0, "CreateWindowEx failed\n");
+
+    ret = WSAAsyncSelect(src, hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE);
+    ok(!ret, "got %d\n", ret);
+
+    Sleep(100);
+    memset(&msg, 0, sizeof(msg));
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(ret, "got %d\n", ret);
+    ok(msg.hwnd == hwnd, "got %p\n", msg.hwnd);
+    ok(msg.message == WM_SOCKET, "got %04x\n", msg.message);
+    ok(msg.wParam == src, "got %08lx\n", msg.wParam);
+    ok(msg.lParam == 2, "got %08lx\n", msg.lParam);
+
+    port = CreateIoCompletionPort((HANDLE)src, 0, 0x12345678, 0);
+    ok(port != 0, "CreateIoCompletionPort error %u\n", GetLastError());
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == WAIT_TIMEOUT, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+    ok(key == 0xdeadbeef, "got key %lu\n", key);
+    ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    closesocket(src);
+
+    Sleep(100);
+    memset(&msg, 0, sizeof(msg));
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == WAIT_TIMEOUT, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+    ok(key == 0xdeadbeef, "got key %lu\n", key);
+    ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+
+    CloseHandle(port);
+
+    DestroyWindow(hwnd);
+}
+
+struct wsa_async_select_info
+{
+    SOCKET sock;
+    HWND hwnd;
+};
+
+static DWORD WINAPI wsa_async_select_thread(void *param)
+{
+    struct wsa_async_select_info *info = param;
+    int ret;
+
+    ret = WSAAsyncSelect(info->sock, info->hwnd, WM_SOCKET, FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE);
+    ok(!ret, "got %d\n", ret);
+
+    return 0;
+}
+
+struct wsa_recv_info
+{
+    SOCKET sock;
+    WSABUF wsa_buf;
+    WSAOVERLAPPED ovl;
+};
+
+static DWORD WINAPI wsa_recv_thread(void *param)
+{
+    struct wsa_recv_info *info = param;
+    int ret;
+    DWORD flags, bytes;
+
+    bytes = 0xdeadbeef;
+    flags = 0;
+    SetLastError(0xdeadbeef);
+    ret = WSARecv(info->sock, &info->wsa_buf, 1, &bytes, &flags, &info->ovl, NULL);
+    ok(ret == SOCKET_ERROR, "got %d\n", ret);
+    ok(GetLastError() == ERROR_IO_PENDING, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+
+    return 0;
+}
+
+static void iocp_async_read_thread_closesocket(SOCKET src)
+{
+    struct wsa_async_select_info select_info;
+    struct wsa_recv_info recv_info;
+    HANDLE port, thread;
+    WSAOVERLAPPED *ovl_iocp;
+    int ret;
+    char data[512];
+    DWORD bytes, tid;
+    ULONG_PTR key;
+    HWND hwnd;
+    MSG msg;
+
+    hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP,
+                           0, 0, 0, 0, NULL, NULL, 0, NULL);
+    ok(hwnd != 0, "CreateWindowEx failed\n");
+
+    select_info.sock = src;
+    select_info.hwnd = hwnd;
+    thread = CreateThread(NULL, 0, wsa_async_select_thread, &select_info, 0, &tid);
+    ok(thread != 0, "CreateThread error %u\n", GetLastError());
+    ret = WaitForSingleObject(thread, 10000);
+    ok(ret == WAIT_OBJECT_0, "thread failed to terminate\n");
+
+    Sleep(100);
+    memset(&msg, 0, sizeof(msg));
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(ret, "got %d\n", ret);
+    ok(msg.hwnd == hwnd, "got %p\n", msg.hwnd);
+    ok(msg.message == WM_SOCKET, "got %04x\n", msg.message);
+    ok(msg.wParam == src, "got %08lx\n", msg.wParam);
+    ok(msg.lParam == 2, "got %08lx\n", msg.lParam);
+
+    port = CreateIoCompletionPort((HANDLE)src, 0, 0x12345678, 0);
+    ok(port != 0, "CreateIoCompletionPort error %u\n", GetLastError());
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    memset(data, 0, sizeof(data));
+    memset(&recv_info.ovl, 0, sizeof(recv_info.ovl));
+    recv_info.sock = src;
+    recv_info.wsa_buf.len = sizeof(data);
+    recv_info.wsa_buf.buf = data;
+    thread = CreateThread(NULL, 0, wsa_recv_thread, &recv_info, 0, &tid);
+    ok(thread != 0, "CreateThread error %u\n", GetLastError());
+    ret = WaitForSingleObject(thread, 10000);
+    ok(ret == WAIT_OBJECT_0, "thread failed to terminate\n");
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == WAIT_TIMEOUT || broken(GetLastError() == ERROR_OPERATION_ABORTED) /* XP */,
+       "got %u\n", GetLastError());
+    if (GetLastError() == WAIT_TIMEOUT)
+    {
+        ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+        ok(key == 0xdeadbeef, "got key %lx\n", key);
+        ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+    }
+    else /* XP */
+    {
+        ok(!bytes, "got bytes %u\n", bytes);
+        ok(key == 0x12345678, "got key %#lx\n", key);
+        ok(ovl_iocp == &recv_info.ovl, "got ovl %p\n", ovl_iocp);
+        if (ovl_iocp)
+        {
+            ok(!ovl_iocp->InternalHigh, "got %#lx\n", ovl_iocp->InternalHigh);
+            ok(ovl_iocp->Internal == STATUS_CANCELLED, "got %#lx\n", ovl_iocp->Internal);
+        }
+
+        closesocket(src);
+        goto xp_is_broken;
+    }
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    closesocket(src);
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = NULL;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+todo_wine
+    ok(GetLastError() == ERROR_CONNECTION_ABORTED || GetLastError() == ERROR_NETNAME_DELETED /* XP */, "got %u\n", GetLastError());
+todo_wine
+    ok(!bytes, "got bytes %u\n", bytes);
+todo_wine
+    ok(key == 0x12345678, "got key %#lx\n", key);
+todo_wine
+    ok(ovl_iocp == &recv_info.ovl, "got ovl %p\n", ovl_iocp);
+    if (ovl_iocp)
+    {
+        ok(!ovl_iocp->InternalHigh, "got %#lx\n", ovl_iocp->InternalHigh);
+        ok(ovl_iocp->Internal == STATUS_CONNECTION_ABORTED || STATUS_LOCAL_DISCONNECT /* XP */, "got %#lx\n", ovl_iocp->Internal);
+    }
+
+xp_is_broken:
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == WAIT_TIMEOUT, "got %u\n", GetLastError());
+    ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+    ok(key == 0xdeadbeef, "got key %lu\n", key);
+    ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+
+    CloseHandle(port);
+
+    DestroyWindow(hwnd);
+}
+
+static void iocp_async_read_thread(SOCKET src, SOCKET dst)
+{
+    struct wsa_async_select_info select_info;
+    struct wsa_recv_info recv_info;
+    HANDLE port, thread;
+    WSAOVERLAPPED *ovl_iocp;
+    int ret;
+    char data[512];
+    DWORD bytes, tid;
+    ULONG_PTR key;
+    HWND hwnd;
+    MSG msg;
+
+    hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP,
+                           0, 0, 0, 0, NULL, NULL, 0, NULL);
+    ok(hwnd != 0, "CreateWindowEx failed\n");
+
+    select_info.sock = src;
+    select_info.hwnd = hwnd;
+    thread = CreateThread(NULL, 0, wsa_async_select_thread, &select_info, 0, &tid);
+    ok(thread != 0, "CreateThread error %u\n", GetLastError());
+    ret = WaitForSingleObject(thread, 10000);
+    ok(ret == WAIT_OBJECT_0, "thread failed to terminate\n");
+
+    Sleep(100);
+    memset(&msg, 0, sizeof(msg));
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(ret, "got %d\n", ret);
+    ok(msg.hwnd == hwnd, "got %p\n", msg.hwnd);
+    ok(msg.message == WM_SOCKET, "got %04x\n", msg.message);
+    ok(msg.wParam == src, "got %08lx\n", msg.wParam);
+    ok(msg.lParam == 2, "got %08lx\n", msg.lParam);
+
+    port = CreateIoCompletionPort((HANDLE)src, 0, 0x12345678, 0);
+    ok(port != 0, "CreateIoCompletionPort error %u\n", GetLastError());
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    memset(data, 0, sizeof(data));
+    memset(&recv_info.ovl, 0, sizeof(recv_info.ovl));
+    recv_info.sock = src;
+    recv_info.wsa_buf.len = sizeof(data);
+    recv_info.wsa_buf.buf = data;
+    thread = CreateThread(NULL, 0, wsa_recv_thread, &recv_info, 0, &tid);
+    ok(thread != 0, "CreateThread error %u\n", GetLastError());
+    ret = WaitForSingleObject(thread, 10000);
+    ok(ret == WAIT_OBJECT_0, "thread failed to terminate\n");
+
+    Sleep(100);
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+    ok(!ret, "got %d\n", ret);
+    ok(GetLastError() == WAIT_TIMEOUT || broken(GetLastError() == ERROR_OPERATION_ABORTED) /* XP */, "got %u\n", GetLastError());
+    if (GetLastError() == WAIT_TIMEOUT)
+    {
+        ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+        ok(key == 0xdeadbeef, "got key %lu\n", key);
+        ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+    }
+    else /* document XP behaviour */
+    {
+        ok(bytes == 0, "got bytes %u\n", bytes);
+        ok(key == 0x12345678, "got key %#lx\n", key);
+        ok(ovl_iocp == &recv_info.ovl, "got ovl %p\n", ovl_iocp);
+        if (ovl_iocp)
+        {
+            ok(!ovl_iocp->InternalHigh, "got %#lx\n", ovl_iocp->InternalHigh);
+            ok(ovl_iocp->Internal == STATUS_CANCELLED, "got %#lx\n", ovl_iocp->Internal);
+        }
+    }
+
+    Sleep(100);
+    memset(&msg, 0, sizeof(msg));
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+    ok(!ret || broken(msg.hwnd == hwnd) /* XP */, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+    if (ret) /* document XP behaviour */
+    {
+        ok(msg.message == WM_SOCKET, "got %04x\n", msg.message);
+        ok(msg.wParam == src, "got %08lx\n", msg.wParam);
+        ok(msg.lParam == 1, "got %08lx\n", msg.lParam);
+    }
+
+    ret = send(dst, "Hello World!", 12, 0);
+    ok(ret == 12, "send returned %d\n", ret);
+
+    Sleep(100);
+    memset(&msg, 0, sizeof(msg));
+    ret = PeekMessageA(&msg, hwnd, WM_SOCKET, WM_SOCKET, PM_REMOVE);
+todo_wine
+    ok(!ret || broken(msg.hwnd == hwnd) /* XP */, "got %04x,%08lx,%08lx\n", msg.message, msg.wParam, msg.lParam);
+    if (ret) /* document XP behaviour */
+    {
+        ok(msg.hwnd == hwnd, "got %p\n", msg.hwnd);
+        ok(msg.message == WM_SOCKET, "got %04x\n", msg.message);
+        ok(msg.wParam == src, "got %08lx\n", msg.wParam);
+        ok(msg.lParam == 1, "got %08lx\n", msg.lParam);
+    }
+
+    bytes = 0xdeadbeef;
+    key = 0xdeadbeef;
+    ovl_iocp = (void *)0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = GetQueuedCompletionStatus(port, &bytes, &key, &ovl_iocp, 100);
+todo_wine
+    ok(ret || broken(GetLastError() == WAIT_TIMEOUT) /* XP */, "got %u\n", GetLastError());
+    if (ret)
+    {
+        ok(bytes == 12, "got bytes %u\n", bytes);
+        ok(key == 0x12345678, "got key %#lx\n", key);
+        ok(ovl_iocp == &recv_info.ovl, "got ovl %p\n", ovl_iocp);
+        if (ovl_iocp)
+        {
+            ok(ovl_iocp->InternalHigh == 12, "got %#lx\n", ovl_iocp->InternalHigh);
+            ok(!ovl_iocp->Internal , "got %#lx\n", ovl_iocp->Internal);
+            ok(!memcmp(data, "Hello World!", 12), "got %u bytes (%*s)\n", bytes, bytes, data);
+        }
+    }
+    else /* document XP behaviour */
+    {
+        ok(bytes == 0xdeadbeef, "got bytes %u\n", bytes);
+        ok(key == 0xdeadbeef, "got key %lu\n", key);
+        ok(!ovl_iocp, "got ovl %p\n", ovl_iocp);
+    }
+
+    CloseHandle(port);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_iocp(void)
+{
+    SOCKET src, dst;
+    int i, ret;
+
+    ret = tcp_socketpair_ovl(&src, &dst);
+    ok(!ret, "creating socket pair failed\n");
+    sync_read(src, dst);
+    iocp_async_read(src, dst);
+    closesocket(src);
+    closesocket(dst);
+
+    ret = tcp_socketpair_ovl(&src, &dst);
+    ok(!ret, "creating socket pair failed\n");
+    iocp_async_read_thread(src, dst);
+    closesocket(src);
+    closesocket(dst);
+
+    for (i = 0; i <= 2; i++)
+    {
+        ret = tcp_socketpair_ovl(&src, &dst);
+        ok(!ret, "creating socket pair failed\n");
+        iocp_async_read_closesocket(src, i);
+        closesocket(dst);
+    }
+
+    ret = tcp_socketpair_ovl(&src, &dst);
+    ok(!ret, "creating socket pair failed\n");
+    iocp_async_closesocket(src);
+    closesocket(dst);
+
+    ret = tcp_socketpair_ovl(&src, &dst);
+    ok(!ret, "creating socket pair failed\n");
+    iocp_async_read_thread_closesocket(src);
+    closesocket(dst);
+}
 
 START_TEST( sock )
 {
@@ -10852,6 +11554,7 @@ START_TEST( sock )
     test_WSARecv();
     test_WSAPoll();
     test_write_watch();
+    test_iocp();
 
     test_events(0);
     test_events(1);
-- 
2.14.2
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.