1*eabc0478Schristos /* $NetBSD: event_iocp.c,v 1.6 2024/08/18 20:47:21 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* 48585484eSchristos * Copyright (c) 2009-2012 Niels Provos, Nick Mathewson 58585484eSchristos * 68585484eSchristos * Redistribution and use in source and binary forms, with or without 78585484eSchristos * modification, are permitted provided that the following conditions 88585484eSchristos * are met: 98585484eSchristos * 1. Redistributions of source code must retain the above copyright 108585484eSchristos * notice, this list of conditions and the following disclaimer. 118585484eSchristos * 2. Redistributions in binary form must reproduce the above copyright 128585484eSchristos * notice, this list of conditions and the following disclaimer in the 138585484eSchristos * documentation and/or other materials provided with the distribution. 148585484eSchristos * 3. The name of the author may not be used to endorse or promote products 158585484eSchristos * derived from this software without specific prior written permission. 168585484eSchristos * 178585484eSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 188585484eSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 198585484eSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 208585484eSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 218585484eSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 228585484eSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 238585484eSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 248585484eSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 258585484eSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 268585484eSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 278585484eSchristos */ 288585484eSchristos #include "evconfig-private.h" 298585484eSchristos 308585484eSchristos #ifndef _WIN32_WINNT 318585484eSchristos /* Minimum required for InitializeCriticalSectionAndSpinCount */ 328585484eSchristos #define _WIN32_WINNT 0x0403 338585484eSchristos #endif 348585484eSchristos #include <winsock2.h> 358585484eSchristos #include <windows.h> 368585484eSchristos #include <process.h> 378585484eSchristos #include <stdio.h> 388585484eSchristos #include <mswsock.h> 398585484eSchristos 408585484eSchristos #include "event2/util.h" 418585484eSchristos #include "util-internal.h" 428585484eSchristos #include "iocp-internal.h" 438585484eSchristos #include "log-internal.h" 448585484eSchristos #include "mm-internal.h" 458585484eSchristos #include "event-internal.h" 468585484eSchristos #include "evthread-internal.h" 478585484eSchristos 488585484eSchristos #define NOTIFICATION_KEY ((ULONG_PTR)-1) 498585484eSchristos 508585484eSchristos void 518585484eSchristos event_overlapped_init_(struct event_overlapped *o, iocp_callback cb) 528585484eSchristos { 538585484eSchristos memset(o, 0, sizeof(struct event_overlapped)); 548585484eSchristos o->cb = cb; 558585484eSchristos } 568585484eSchristos 578585484eSchristos static void 588585484eSchristos handle_entry(OVERLAPPED *o, ULONG_PTR completion_key, DWORD nBytes, int ok) 598585484eSchristos { 608585484eSchristos struct event_overlapped *eo = 618585484eSchristos EVUTIL_UPCAST(o, struct event_overlapped, overlapped); 628585484eSchristos eo->cb(eo, completion_key, nBytes, ok); 638585484eSchristos } 648585484eSchristos 658585484eSchristos static void 668585484eSchristos loop(void *port_) 678585484eSchristos { 688585484eSchristos struct event_iocp_port *port = port_; 698585484eSchristos long ms = port->ms; 708585484eSchristos HANDLE p = port->port; 718585484eSchristos 728585484eSchristos if (ms <= 0) 738585484eSchristos ms = INFINITE; 748585484eSchristos 758585484eSchristos while (1) { 768585484eSchristos OVERLAPPED *overlapped=NULL; 778585484eSchristos ULONG_PTR key=0; 788585484eSchristos DWORD bytes=0; 798585484eSchristos int ok = GetQueuedCompletionStatus(p, &bytes, &key, 808585484eSchristos &overlapped, ms); 818585484eSchristos EnterCriticalSection(&port->lock); 828585484eSchristos if (port->shutdown) { 838585484eSchristos if (--port->n_live_threads == 0) 848585484eSchristos ReleaseSemaphore(port->shutdownSemaphore, 1, 858585484eSchristos NULL); 868585484eSchristos LeaveCriticalSection(&port->lock); 878585484eSchristos return; 888585484eSchristos } 898585484eSchristos LeaveCriticalSection(&port->lock); 908585484eSchristos 918585484eSchristos if (key != NOTIFICATION_KEY && overlapped) 928585484eSchristos handle_entry(overlapped, key, bytes, ok); 938585484eSchristos else if (!overlapped) 948585484eSchristos break; 958585484eSchristos } 968585484eSchristos event_warnx("GetQueuedCompletionStatus exited with no event."); 978585484eSchristos EnterCriticalSection(&port->lock); 988585484eSchristos if (--port->n_live_threads == 0) 998585484eSchristos ReleaseSemaphore(port->shutdownSemaphore, 1, NULL); 1008585484eSchristos LeaveCriticalSection(&port->lock); 1018585484eSchristos } 1028585484eSchristos 1038585484eSchristos int 1048585484eSchristos event_iocp_port_associate_(struct event_iocp_port *port, evutil_socket_t fd, 1058585484eSchristos ev_uintptr_t key) 1068585484eSchristos { 1078585484eSchristos HANDLE h; 1088585484eSchristos h = CreateIoCompletionPort((HANDLE)fd, port->port, key, port->n_threads); 1098585484eSchristos if (!h) 1108585484eSchristos return -1; 1118585484eSchristos return 0; 1128585484eSchristos } 1138585484eSchristos 1148585484eSchristos static void * 1158585484eSchristos get_extension_function(SOCKET s, const GUID *which_fn) 1168585484eSchristos { 1178585484eSchristos void *ptr = NULL; 1188585484eSchristos DWORD bytes=0; 1198585484eSchristos WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 1208585484eSchristos (GUID*)which_fn, sizeof(*which_fn), 1218585484eSchristos &ptr, sizeof(ptr), 1228585484eSchristos &bytes, NULL, NULL); 1238585484eSchristos 1248585484eSchristos /* No need to detect errors here: if ptr is set, then we have a good 1258585484eSchristos function pointer. Otherwise, we should behave as if we had no 1268585484eSchristos function pointer. 1278585484eSchristos */ 1288585484eSchristos return ptr; 1298585484eSchristos } 1308585484eSchristos 1318585484eSchristos /* Mingw doesn't have these in its mswsock.h. The values are copied from 1328585484eSchristos wine.h. Perhaps if we copy them exactly, the cargo will come again. 1338585484eSchristos */ 1348585484eSchristos #ifndef WSAID_ACCEPTEX 1358585484eSchristos #define WSAID_ACCEPTEX \ 1368585484eSchristos {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} 1378585484eSchristos #endif 1388585484eSchristos #ifndef WSAID_CONNECTEX 1398585484eSchristos #define WSAID_CONNECTEX \ 1408585484eSchristos {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} 1418585484eSchristos #endif 1428585484eSchristos #ifndef WSAID_GETACCEPTEXSOCKADDRS 1438585484eSchristos #define WSAID_GETACCEPTEXSOCKADDRS \ 1448585484eSchristos {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} 1458585484eSchristos #endif 1468585484eSchristos 147b8ecfcfeSchristos static int extension_fns_initialized = 0; 148b8ecfcfeSchristos 1498585484eSchristos static void 1508585484eSchristos init_extension_functions(struct win32_extension_fns *ext) 1518585484eSchristos { 1528585484eSchristos const GUID acceptex = WSAID_ACCEPTEX; 1538585484eSchristos const GUID connectex = WSAID_CONNECTEX; 1548585484eSchristos const GUID getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS; 1558585484eSchristos SOCKET s = socket(AF_INET, SOCK_STREAM, 0); 156*eabc0478Schristos if (s == EVUTIL_INVALID_SOCKET) 1578585484eSchristos return; 1588585484eSchristos ext->AcceptEx = get_extension_function(s, &acceptex); 1598585484eSchristos ext->ConnectEx = get_extension_function(s, &connectex); 1608585484eSchristos ext->GetAcceptExSockaddrs = get_extension_function(s, 1618585484eSchristos &getacceptexsockaddrs); 1628585484eSchristos closesocket(s); 163b8ecfcfeSchristos 164b8ecfcfeSchristos extension_fns_initialized = 1; 1658585484eSchristos } 1668585484eSchristos 1678585484eSchristos static struct win32_extension_fns the_extension_fns; 1688585484eSchristos 1698585484eSchristos const struct win32_extension_fns * 1708585484eSchristos event_get_win32_extension_fns_(void) 1718585484eSchristos { 1728585484eSchristos return &the_extension_fns; 1738585484eSchristos } 1748585484eSchristos 1758585484eSchristos #define N_CPUS_DEFAULT 2 1768585484eSchristos 1778585484eSchristos struct event_iocp_port * 1788585484eSchristos event_iocp_port_launch_(int n_cpus) 1798585484eSchristos { 1808585484eSchristos struct event_iocp_port *port; 1818585484eSchristos int i; 1828585484eSchristos 1838585484eSchristos if (!extension_fns_initialized) 1848585484eSchristos init_extension_functions(&the_extension_fns); 1858585484eSchristos 1868585484eSchristos if (!(port = mm_calloc(1, sizeof(struct event_iocp_port)))) 1878585484eSchristos return NULL; 1888585484eSchristos 1898585484eSchristos if (n_cpus <= 0) 1908585484eSchristos n_cpus = N_CPUS_DEFAULT; 1918585484eSchristos port->n_threads = n_cpus * 2; 1928585484eSchristos port->threads = mm_calloc(port->n_threads, sizeof(HANDLE)); 1938585484eSchristos if (!port->threads) 1948585484eSchristos goto err; 1958585484eSchristos 1968585484eSchristos port->port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1978585484eSchristos n_cpus); 1988585484eSchristos port->ms = -1; 1998585484eSchristos if (!port->port) 2008585484eSchristos goto err; 2018585484eSchristos 2028585484eSchristos port->shutdownSemaphore = CreateSemaphore(NULL, 0, 1, NULL); 2038585484eSchristos if (!port->shutdownSemaphore) 2048585484eSchristos goto err; 2058585484eSchristos 2068585484eSchristos for (i=0; i<port->n_threads; ++i) { 2078585484eSchristos ev_uintptr_t th = _beginthread(loop, 0, port); 2088585484eSchristos if (th == (ev_uintptr_t)-1) 2098585484eSchristos goto err; 2108585484eSchristos port->threads[i] = (HANDLE)th; 2118585484eSchristos ++port->n_live_threads; 2128585484eSchristos } 2138585484eSchristos 2148585484eSchristos InitializeCriticalSectionAndSpinCount(&port->lock, 1000); 2158585484eSchristos 2168585484eSchristos return port; 2178585484eSchristos err: 2188585484eSchristos if (port->port) 2198585484eSchristos CloseHandle(port->port); 2208585484eSchristos if (port->threads) 2218585484eSchristos mm_free(port->threads); 2228585484eSchristos if (port->shutdownSemaphore) 2238585484eSchristos CloseHandle(port->shutdownSemaphore); 2248585484eSchristos mm_free(port); 2258585484eSchristos return NULL; 2268585484eSchristos } 2278585484eSchristos 2288585484eSchristos static void 2298585484eSchristos event_iocp_port_unlock_and_free_(struct event_iocp_port *port) 2308585484eSchristos { 2318585484eSchristos DeleteCriticalSection(&port->lock); 2328585484eSchristos CloseHandle(port->port); 2338585484eSchristos CloseHandle(port->shutdownSemaphore); 2348585484eSchristos mm_free(port->threads); 2358585484eSchristos mm_free(port); 2368585484eSchristos } 2378585484eSchristos 2388585484eSchristos static int 2398585484eSchristos event_iocp_notify_all(struct event_iocp_port *port) 2408585484eSchristos { 2418585484eSchristos int i, r, ok=1; 2428585484eSchristos for (i=0; i<port->n_threads; ++i) { 2438585484eSchristos r = PostQueuedCompletionStatus(port->port, 0, NOTIFICATION_KEY, 2448585484eSchristos NULL); 2458585484eSchristos if (!r) 2468585484eSchristos ok = 0; 2478585484eSchristos } 2488585484eSchristos return ok ? 0 : -1; 2498585484eSchristos } 2508585484eSchristos 2518585484eSchristos int 2528585484eSchristos event_iocp_shutdown_(struct event_iocp_port *port, long waitMsec) 2538585484eSchristos { 2548585484eSchristos DWORD ms = INFINITE; 2558585484eSchristos int n; 2568585484eSchristos 2578585484eSchristos EnterCriticalSection(&port->lock); 2588585484eSchristos port->shutdown = 1; 2598585484eSchristos LeaveCriticalSection(&port->lock); 2608585484eSchristos event_iocp_notify_all(port); 2618585484eSchristos 2628585484eSchristos if (waitMsec >= 0) 2638585484eSchristos ms = waitMsec; 2648585484eSchristos 2658585484eSchristos WaitForSingleObject(port->shutdownSemaphore, ms); 2668585484eSchristos EnterCriticalSection(&port->lock); 2678585484eSchristos n = port->n_live_threads; 2688585484eSchristos LeaveCriticalSection(&port->lock); 2698585484eSchristos if (n == 0) { 2708585484eSchristos event_iocp_port_unlock_and_free_(port); 2718585484eSchristos return 0; 2728585484eSchristos } else { 2738585484eSchristos return -1; 2748585484eSchristos } 2758585484eSchristos } 2768585484eSchristos 2778585484eSchristos int 2788585484eSchristos event_iocp_activate_overlapped_( 2798585484eSchristos struct event_iocp_port *port, struct event_overlapped *o, 2808585484eSchristos ev_uintptr_t key, ev_uint32_t n) 2818585484eSchristos { 2828585484eSchristos BOOL r; 2838585484eSchristos 2848585484eSchristos r = PostQueuedCompletionStatus(port->port, n, key, &o->overlapped); 2858585484eSchristos return (r==0) ? -1 : 0; 2868585484eSchristos } 2878585484eSchristos 2888585484eSchristos struct event_iocp_port * 2898585484eSchristos event_base_get_iocp_(struct event_base *base) 2908585484eSchristos { 2918585484eSchristos #ifdef _WIN32 2928585484eSchristos return base->iocp; 2938585484eSchristos #else 2948585484eSchristos return NULL; 2958585484eSchristos #endif 2968585484eSchristos } 297