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