1290001Sglebius/* 2290001Sglebius * Copyright (c) 2009-2012 Niels Provos, Nick Mathewson 3290001Sglebius * 4290001Sglebius * Redistribution and use in source and binary forms, with or without 5290001Sglebius * modification, are permitted provided that the following conditions 6290001Sglebius * are met: 7290001Sglebius * 1. Redistributions of source code must retain the above copyright 8290001Sglebius * notice, this list of conditions and the following disclaimer. 9290001Sglebius * 2. Redistributions in binary form must reproduce the above copyright 10290001Sglebius * notice, this list of conditions and the following disclaimer in the 11290001Sglebius * documentation and/or other materials provided with the distribution. 12290001Sglebius * 3. The name of the author may not be used to endorse or promote products 13290001Sglebius * derived from this software without specific prior written permission. 14290001Sglebius * 15290001Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16290001Sglebius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17290001Sglebius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18290001Sglebius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19290001Sglebius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20290001Sglebius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21290001Sglebius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22290001Sglebius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23290001Sglebius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24290001Sglebius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25290001Sglebius */ 26290001Sglebius#include "evconfig-private.h" 27290001Sglebius 28290001Sglebius#ifndef _WIN32_WINNT 29290001Sglebius/* Minimum required for InitializeCriticalSectionAndSpinCount */ 30290001Sglebius#define _WIN32_WINNT 0x0403 31290001Sglebius#endif 32290001Sglebius#include <winsock2.h> 33290001Sglebius#include <windows.h> 34290001Sglebius#include <process.h> 35290001Sglebius#include <stdio.h> 36290001Sglebius#include <mswsock.h> 37290001Sglebius 38290001Sglebius#include "event2/util.h" 39290001Sglebius#include "util-internal.h" 40290001Sglebius#include "iocp-internal.h" 41290001Sglebius#include "log-internal.h" 42290001Sglebius#include "mm-internal.h" 43290001Sglebius#include "event-internal.h" 44290001Sglebius#include "evthread-internal.h" 45290001Sglebius 46290001Sglebius#define NOTIFICATION_KEY ((ULONG_PTR)-1) 47290001Sglebius 48290001Sglebiusvoid 49290001Sglebiusevent_overlapped_init_(struct event_overlapped *o, iocp_callback cb) 50290001Sglebius{ 51290001Sglebius memset(o, 0, sizeof(struct event_overlapped)); 52290001Sglebius o->cb = cb; 53290001Sglebius} 54290001Sglebius 55290001Sglebiusstatic void 56290001Sglebiushandle_entry(OVERLAPPED *o, ULONG_PTR completion_key, DWORD nBytes, int ok) 57290001Sglebius{ 58290001Sglebius struct event_overlapped *eo = 59290001Sglebius EVUTIL_UPCAST(o, struct event_overlapped, overlapped); 60290001Sglebius eo->cb(eo, completion_key, nBytes, ok); 61290001Sglebius} 62290001Sglebius 63290001Sglebiusstatic void 64290001Sglebiusloop(void *port_) 65290001Sglebius{ 66290001Sglebius struct event_iocp_port *port = port_; 67290001Sglebius long ms = port->ms; 68290001Sglebius HANDLE p = port->port; 69290001Sglebius 70290001Sglebius if (ms <= 0) 71290001Sglebius ms = INFINITE; 72290001Sglebius 73290001Sglebius while (1) { 74290001Sglebius OVERLAPPED *overlapped=NULL; 75290001Sglebius ULONG_PTR key=0; 76290001Sglebius DWORD bytes=0; 77290001Sglebius int ok = GetQueuedCompletionStatus(p, &bytes, &key, 78290001Sglebius &overlapped, ms); 79290001Sglebius EnterCriticalSection(&port->lock); 80290001Sglebius if (port->shutdown) { 81290001Sglebius if (--port->n_live_threads == 0) 82290001Sglebius ReleaseSemaphore(port->shutdownSemaphore, 1, 83290001Sglebius NULL); 84290001Sglebius LeaveCriticalSection(&port->lock); 85290001Sglebius return; 86290001Sglebius } 87290001Sglebius LeaveCriticalSection(&port->lock); 88290001Sglebius 89290001Sglebius if (key != NOTIFICATION_KEY && overlapped) 90290001Sglebius handle_entry(overlapped, key, bytes, ok); 91290001Sglebius else if (!overlapped) 92290001Sglebius break; 93290001Sglebius } 94290001Sglebius event_warnx("GetQueuedCompletionStatus exited with no event."); 95290001Sglebius EnterCriticalSection(&port->lock); 96290001Sglebius if (--port->n_live_threads == 0) 97290001Sglebius ReleaseSemaphore(port->shutdownSemaphore, 1, NULL); 98290001Sglebius LeaveCriticalSection(&port->lock); 99290001Sglebius} 100290001Sglebius 101290001Sglebiusint 102290001Sglebiusevent_iocp_port_associate_(struct event_iocp_port *port, evutil_socket_t fd, 103290001Sglebius ev_uintptr_t key) 104290001Sglebius{ 105290001Sglebius HANDLE h; 106290001Sglebius h = CreateIoCompletionPort((HANDLE)fd, port->port, key, port->n_threads); 107290001Sglebius if (!h) 108290001Sglebius return -1; 109290001Sglebius return 0; 110290001Sglebius} 111290001Sglebius 112290001Sglebiusstatic void * 113290001Sglebiusget_extension_function(SOCKET s, const GUID *which_fn) 114290001Sglebius{ 115290001Sglebius void *ptr = NULL; 116290001Sglebius DWORD bytes=0; 117290001Sglebius WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 118290001Sglebius (GUID*)which_fn, sizeof(*which_fn), 119290001Sglebius &ptr, sizeof(ptr), 120290001Sglebius &bytes, NULL, NULL); 121290001Sglebius 122290001Sglebius /* No need to detect errors here: if ptr is set, then we have a good 123290001Sglebius function pointer. Otherwise, we should behave as if we had no 124290001Sglebius function pointer. 125290001Sglebius */ 126290001Sglebius return ptr; 127290001Sglebius} 128290001Sglebius 129290001Sglebius/* Mingw doesn't have these in its mswsock.h. The values are copied from 130290001Sglebius wine.h. Perhaps if we copy them exactly, the cargo will come again. 131290001Sglebius*/ 132290001Sglebius#ifndef WSAID_ACCEPTEX 133290001Sglebius#define WSAID_ACCEPTEX \ 134290001Sglebius {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} 135290001Sglebius#endif 136290001Sglebius#ifndef WSAID_CONNECTEX 137290001Sglebius#define WSAID_CONNECTEX \ 138290001Sglebius {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} 139290001Sglebius#endif 140290001Sglebius#ifndef WSAID_GETACCEPTEXSOCKADDRS 141290001Sglebius#define WSAID_GETACCEPTEXSOCKADDRS \ 142290001Sglebius {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} 143290001Sglebius#endif 144290001Sglebius 145290001Sglebiusstatic int extension_fns_initialized = 0; 146290001Sglebius 147290001Sglebiusstatic void 148290001Sglebiusinit_extension_functions(struct win32_extension_fns *ext) 149290001Sglebius{ 150290001Sglebius const GUID acceptex = WSAID_ACCEPTEX; 151290001Sglebius const GUID connectex = WSAID_CONNECTEX; 152290001Sglebius const GUID getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS; 153290001Sglebius SOCKET s = socket(AF_INET, SOCK_STREAM, 0); 154290001Sglebius if (s == INVALID_SOCKET) 155290001Sglebius return; 156290001Sglebius ext->AcceptEx = get_extension_function(s, &acceptex); 157290001Sglebius ext->ConnectEx = get_extension_function(s, &connectex); 158290001Sglebius ext->GetAcceptExSockaddrs = get_extension_function(s, 159290001Sglebius &getacceptexsockaddrs); 160290001Sglebius closesocket(s); 161290001Sglebius 162290001Sglebius extension_fns_initialized = 1; 163290001Sglebius} 164290001Sglebius 165290001Sglebiusstatic struct win32_extension_fns the_extension_fns; 166290001Sglebius 167290001Sglebiusconst struct win32_extension_fns * 168290001Sglebiusevent_get_win32_extension_fns_(void) 169290001Sglebius{ 170290001Sglebius return &the_extension_fns; 171290001Sglebius} 172290001Sglebius 173290001Sglebius#define N_CPUS_DEFAULT 2 174290001Sglebius 175290001Sglebiusstruct event_iocp_port * 176290001Sglebiusevent_iocp_port_launch_(int n_cpus) 177290001Sglebius{ 178290001Sglebius struct event_iocp_port *port; 179290001Sglebius int i; 180290001Sglebius 181290001Sglebius if (!extension_fns_initialized) 182290001Sglebius init_extension_functions(&the_extension_fns); 183290001Sglebius 184290001Sglebius if (!(port = mm_calloc(1, sizeof(struct event_iocp_port)))) 185290001Sglebius return NULL; 186290001Sglebius 187290001Sglebius if (n_cpus <= 0) 188290001Sglebius n_cpus = N_CPUS_DEFAULT; 189290001Sglebius port->n_threads = n_cpus * 2; 190290001Sglebius port->threads = mm_calloc(port->n_threads, sizeof(HANDLE)); 191290001Sglebius if (!port->threads) 192290001Sglebius goto err; 193290001Sglebius 194290001Sglebius port->port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 195290001Sglebius n_cpus); 196290001Sglebius port->ms = -1; 197290001Sglebius if (!port->port) 198290001Sglebius goto err; 199290001Sglebius 200290001Sglebius port->shutdownSemaphore = CreateSemaphore(NULL, 0, 1, NULL); 201290001Sglebius if (!port->shutdownSemaphore) 202290001Sglebius goto err; 203290001Sglebius 204290001Sglebius for (i=0; i<port->n_threads; ++i) { 205290001Sglebius ev_uintptr_t th = _beginthread(loop, 0, port); 206290001Sglebius if (th == (ev_uintptr_t)-1) 207290001Sglebius goto err; 208290001Sglebius port->threads[i] = (HANDLE)th; 209290001Sglebius ++port->n_live_threads; 210290001Sglebius } 211290001Sglebius 212290001Sglebius InitializeCriticalSectionAndSpinCount(&port->lock, 1000); 213290001Sglebius 214290001Sglebius return port; 215290001Sglebiuserr: 216290001Sglebius if (port->port) 217290001Sglebius CloseHandle(port->port); 218290001Sglebius if (port->threads) 219290001Sglebius mm_free(port->threads); 220290001Sglebius if (port->shutdownSemaphore) 221290001Sglebius CloseHandle(port->shutdownSemaphore); 222290001Sglebius mm_free(port); 223290001Sglebius return NULL; 224290001Sglebius} 225290001Sglebius 226290001Sglebiusstatic void 227290001Sglebiusevent_iocp_port_unlock_and_free_(struct event_iocp_port *port) 228290001Sglebius{ 229290001Sglebius DeleteCriticalSection(&port->lock); 230290001Sglebius CloseHandle(port->port); 231290001Sglebius CloseHandle(port->shutdownSemaphore); 232290001Sglebius mm_free(port->threads); 233290001Sglebius mm_free(port); 234290001Sglebius} 235290001Sglebius 236290001Sglebiusstatic int 237290001Sglebiusevent_iocp_notify_all(struct event_iocp_port *port) 238290001Sglebius{ 239290001Sglebius int i, r, ok=1; 240290001Sglebius for (i=0; i<port->n_threads; ++i) { 241290001Sglebius r = PostQueuedCompletionStatus(port->port, 0, NOTIFICATION_KEY, 242290001Sglebius NULL); 243290001Sglebius if (!r) 244290001Sglebius ok = 0; 245290001Sglebius } 246290001Sglebius return ok ? 0 : -1; 247290001Sglebius} 248290001Sglebius 249290001Sglebiusint 250290001Sglebiusevent_iocp_shutdown_(struct event_iocp_port *port, long waitMsec) 251290001Sglebius{ 252290001Sglebius DWORD ms = INFINITE; 253290001Sglebius int n; 254290001Sglebius 255290001Sglebius EnterCriticalSection(&port->lock); 256290001Sglebius port->shutdown = 1; 257290001Sglebius LeaveCriticalSection(&port->lock); 258290001Sglebius event_iocp_notify_all(port); 259290001Sglebius 260290001Sglebius if (waitMsec >= 0) 261290001Sglebius ms = waitMsec; 262290001Sglebius 263290001Sglebius WaitForSingleObject(port->shutdownSemaphore, ms); 264290001Sglebius EnterCriticalSection(&port->lock); 265290001Sglebius n = port->n_live_threads; 266290001Sglebius LeaveCriticalSection(&port->lock); 267290001Sglebius if (n == 0) { 268290001Sglebius event_iocp_port_unlock_and_free_(port); 269290001Sglebius return 0; 270290001Sglebius } else { 271290001Sglebius return -1; 272290001Sglebius } 273290001Sglebius} 274290001Sglebius 275290001Sglebiusint 276290001Sglebiusevent_iocp_activate_overlapped_( 277290001Sglebius struct event_iocp_port *port, struct event_overlapped *o, 278290001Sglebius ev_uintptr_t key, ev_uint32_t n) 279290001Sglebius{ 280290001Sglebius BOOL r; 281290001Sglebius 282290001Sglebius r = PostQueuedCompletionStatus(port->port, n, key, &o->overlapped); 283290001Sglebius return (r==0) ? -1 : 0; 284290001Sglebius} 285290001Sglebius 286290001Sglebiusstruct event_iocp_port * 287290001Sglebiusevent_base_get_iocp_(struct event_base *base) 288290001Sglebius{ 289290001Sglebius#ifdef _WIN32 290290001Sglebius return base->iocp; 291290001Sglebius#else 292290001Sglebius return NULL; 293290001Sglebius#endif 294290001Sglebius} 295