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