1/* $NetBSD: event_iocp.c,v 1.5 2020/05/25 20:47:33 christos Exp $ */ 2 3/* 4 * Copyright (c) 2009-2012 Niels Provos, Nick Mathewson 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28#include "evconfig-private.h" 29 30#ifndef _WIN32_WINNT 31/* Minimum required for InitializeCriticalSectionAndSpinCount */ 32#define _WIN32_WINNT 0x0403 33#endif 34#include <winsock2.h> 35#include <windows.h> 36#include <process.h> 37#include <stdio.h> 38#include <mswsock.h> 39 40#include "event2/util.h" 41#include "util-internal.h" 42#include "iocp-internal.h" 43#include "log-internal.h" 44#include "mm-internal.h" 45#include "event-internal.h" 46#include "evthread-internal.h" 47 48#define NOTIFICATION_KEY ((ULONG_PTR)-1) 49 50void 51event_overlapped_init_(struct event_overlapped *o, iocp_callback cb) 52{ 53 memset(o, 0, sizeof(struct event_overlapped)); 54 o->cb = cb; 55} 56 57static void 58handle_entry(OVERLAPPED *o, ULONG_PTR completion_key, DWORD nBytes, int ok) 59{ 60 struct event_overlapped *eo = 61 EVUTIL_UPCAST(o, struct event_overlapped, overlapped); 62 eo->cb(eo, completion_key, nBytes, ok); 63} 64 65static void 66loop(void *port_) 67{ 68 struct event_iocp_port *port = port_; 69 long ms = port->ms; 70 HANDLE p = port->port; 71 72 if (ms <= 0) 73 ms = INFINITE; 74 75 while (1) { 76 OVERLAPPED *overlapped=NULL; 77 ULONG_PTR key=0; 78 DWORD bytes=0; 79 int ok = GetQueuedCompletionStatus(p, &bytes, &key, 80 &overlapped, ms); 81 EnterCriticalSection(&port->lock); 82 if (port->shutdown) { 83 if (--port->n_live_threads == 0) 84 ReleaseSemaphore(port->shutdownSemaphore, 1, 85 NULL); 86 LeaveCriticalSection(&port->lock); 87 return; 88 } 89 LeaveCriticalSection(&port->lock); 90 91 if (key != NOTIFICATION_KEY && overlapped) 92 handle_entry(overlapped, key, bytes, ok); 93 else if (!overlapped) 94 break; 95 } 96 event_warnx("GetQueuedCompletionStatus exited with no event."); 97 EnterCriticalSection(&port->lock); 98 if (--port->n_live_threads == 0) 99 ReleaseSemaphore(port->shutdownSemaphore, 1, NULL); 100 LeaveCriticalSection(&port->lock); 101} 102 103int 104event_iocp_port_associate_(struct event_iocp_port *port, evutil_socket_t fd, 105 ev_uintptr_t key) 106{ 107 HANDLE h; 108 h = CreateIoCompletionPort((HANDLE)fd, port->port, key, port->n_threads); 109 if (!h) 110 return -1; 111 return 0; 112} 113 114static void * 115get_extension_function(SOCKET s, const GUID *which_fn) 116{ 117 void *ptr = NULL; 118 DWORD bytes=0; 119 WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 120 (GUID*)which_fn, sizeof(*which_fn), 121 &ptr, sizeof(ptr), 122 &bytes, NULL, NULL); 123 124 /* No need to detect errors here: if ptr is set, then we have a good 125 function pointer. Otherwise, we should behave as if we had no 126 function pointer. 127 */ 128 return ptr; 129} 130 131/* Mingw doesn't have these in its mswsock.h. The values are copied from 132 wine.h. Perhaps if we copy them exactly, the cargo will come again. 133*/ 134#ifndef WSAID_ACCEPTEX 135#define WSAID_ACCEPTEX \ 136 {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} 137#endif 138#ifndef WSAID_CONNECTEX 139#define WSAID_CONNECTEX \ 140 {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} 141#endif 142#ifndef WSAID_GETACCEPTEXSOCKADDRS 143#define WSAID_GETACCEPTEXSOCKADDRS \ 144 {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} 145#endif 146 147static int extension_fns_initialized = 0; 148 149static void 150init_extension_functions(struct win32_extension_fns *ext) 151{ 152 const GUID acceptex = WSAID_ACCEPTEX; 153 const GUID connectex = WSAID_CONNECTEX; 154 const GUID getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS; 155 SOCKET s = socket(AF_INET, SOCK_STREAM, 0); 156 if (s == INVALID_SOCKET) 157 return; 158 ext->AcceptEx = get_extension_function(s, &acceptex); 159 ext->ConnectEx = get_extension_function(s, &connectex); 160 ext->GetAcceptExSockaddrs = get_extension_function(s, 161 &getacceptexsockaddrs); 162 closesocket(s); 163 164 extension_fns_initialized = 1; 165} 166 167static struct win32_extension_fns the_extension_fns; 168 169const struct win32_extension_fns * 170event_get_win32_extension_fns_(void) 171{ 172 return &the_extension_fns; 173} 174 175#define N_CPUS_DEFAULT 2 176 177struct event_iocp_port * 178event_iocp_port_launch_(int n_cpus) 179{ 180 struct event_iocp_port *port; 181 int i; 182 183 if (!extension_fns_initialized) 184 init_extension_functions(&the_extension_fns); 185 186 if (!(port = mm_calloc(1, sizeof(struct event_iocp_port)))) 187 return NULL; 188 189 if (n_cpus <= 0) 190 n_cpus = N_CPUS_DEFAULT; 191 port->n_threads = n_cpus * 2; 192 port->threads = mm_calloc(port->n_threads, sizeof(HANDLE)); 193 if (!port->threads) 194 goto err; 195 196 port->port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 197 n_cpus); 198 port->ms = -1; 199 if (!port->port) 200 goto err; 201 202 port->shutdownSemaphore = CreateSemaphore(NULL, 0, 1, NULL); 203 if (!port->shutdownSemaphore) 204 goto err; 205 206 for (i=0; i<port->n_threads; ++i) { 207 ev_uintptr_t th = _beginthread(loop, 0, port); 208 if (th == (ev_uintptr_t)-1) 209 goto err; 210 port->threads[i] = (HANDLE)th; 211 ++port->n_live_threads; 212 } 213 214 InitializeCriticalSectionAndSpinCount(&port->lock, 1000); 215 216 return port; 217err: 218 if (port->port) 219 CloseHandle(port->port); 220 if (port->threads) 221 mm_free(port->threads); 222 if (port->shutdownSemaphore) 223 CloseHandle(port->shutdownSemaphore); 224 mm_free(port); 225 return NULL; 226} 227 228static void 229event_iocp_port_unlock_and_free_(struct event_iocp_port *port) 230{ 231 DeleteCriticalSection(&port->lock); 232 CloseHandle(port->port); 233 CloseHandle(port->shutdownSemaphore); 234 mm_free(port->threads); 235 mm_free(port); 236} 237 238static int 239event_iocp_notify_all(struct event_iocp_port *port) 240{ 241 int i, r, ok=1; 242 for (i=0; i<port->n_threads; ++i) { 243 r = PostQueuedCompletionStatus(port->port, 0, NOTIFICATION_KEY, 244 NULL); 245 if (!r) 246 ok = 0; 247 } 248 return ok ? 0 : -1; 249} 250 251int 252event_iocp_shutdown_(struct event_iocp_port *port, long waitMsec) 253{ 254 DWORD ms = INFINITE; 255 int n; 256 257 EnterCriticalSection(&port->lock); 258 port->shutdown = 1; 259 LeaveCriticalSection(&port->lock); 260 event_iocp_notify_all(port); 261 262 if (waitMsec >= 0) 263 ms = waitMsec; 264 265 WaitForSingleObject(port->shutdownSemaphore, ms); 266 EnterCriticalSection(&port->lock); 267 n = port->n_live_threads; 268 LeaveCriticalSection(&port->lock); 269 if (n == 0) { 270 event_iocp_port_unlock_and_free_(port); 271 return 0; 272 } else { 273 return -1; 274 } 275} 276 277int 278event_iocp_activate_overlapped_( 279 struct event_iocp_port *port, struct event_overlapped *o, 280 ev_uintptr_t key, ev_uint32_t n) 281{ 282 BOOL r; 283 284 r = PostQueuedCompletionStatus(port->port, n, key, &o->overlapped); 285 return (r==0) ? -1 : 0; 286} 287 288struct event_iocp_port * 289event_base_get_iocp_(struct event_base *base) 290{ 291#ifdef _WIN32 292 return base->iocp; 293#else 294 return NULL; 295#endif 296} 297