1eda27f95SNick Mathewson /*
2e49e2891SNick Mathewson * Copyright (c) 2009-2012 Niels Provos, Nick Mathewson
3eda27f95SNick Mathewson *
4eda27f95SNick Mathewson * Redistribution and use in source and binary forms, with or without
5eda27f95SNick Mathewson * modification, are permitted provided that the following conditions
6eda27f95SNick Mathewson * are met:
7eda27f95SNick Mathewson * 1. Redistributions of source code must retain the above copyright
8eda27f95SNick Mathewson * notice, this list of conditions and the following disclaimer.
9eda27f95SNick Mathewson * 2. Redistributions in binary form must reproduce the above copyright
10eda27f95SNick Mathewson * notice, this list of conditions and the following disclaimer in the
11eda27f95SNick Mathewson * documentation and/or other materials provided with the distribution.
12eda27f95SNick Mathewson * 3. The name of the author may not be used to endorse or promote products
13eda27f95SNick Mathewson * derived from this software without specific prior written permission.
14eda27f95SNick Mathewson *
15eda27f95SNick Mathewson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16eda27f95SNick Mathewson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17eda27f95SNick Mathewson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18eda27f95SNick Mathewson * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19eda27f95SNick Mathewson * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20eda27f95SNick Mathewson * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21eda27f95SNick Mathewson * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22eda27f95SNick Mathewson * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23eda27f95SNick Mathewson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24eda27f95SNick Mathewson * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25eda27f95SNick Mathewson */
26ded0a090SKevin Bowling #include "evconfig-private.h"
27ca737ff3SNick Mathewson
28816115a1SPeter Rosin #ifndef _WIN32_WINNT
29816115a1SPeter Rosin /* Minimum required for InitializeCriticalSectionAndSpinCount */
30816115a1SPeter Rosin #define _WIN32_WINNT 0x0403
31816115a1SPeter Rosin #endif
325b5b880bSNick Mathewson #include <winsock2.h>
3393d4f884SNick Mathewson #include <windows.h>
340b987813SNick Mathewson #include <process.h>
35f1090833SNick Mathewson #include <stdio.h>
36fa313f28SNick Mathewson #include <mswsock.h>
370b987813SNick Mathewson
3893d4f884SNick Mathewson #include "event2/util.h"
3993d4f884SNick Mathewson #include "util-internal.h"
40ca737ff3SNick Mathewson #include "iocp-internal.h"
410b987813SNick Mathewson #include "log-internal.h"
420b987813SNick Mathewson #include "mm-internal.h"
43fe47003dSNick Mathewson #include "event-internal.h"
4476f7e7aeSChristopher Davis #include "evthread-internal.h"
45ca737ff3SNick Mathewson
46f1090833SNick Mathewson #define NOTIFICATION_KEY ((ULONG_PTR)-1)
47f1090833SNick Mathewson
48ca737ff3SNick Mathewson void
event_overlapped_init_(struct event_overlapped * o,iocp_callback cb)498ac3c4c2SNick Mathewson event_overlapped_init_(struct event_overlapped *o, iocp_callback cb)
50ca737ff3SNick Mathewson {
5193d4f884SNick Mathewson memset(o, 0, sizeof(struct event_overlapped));
52ca737ff3SNick Mathewson o->cb = cb;
53ca737ff3SNick Mathewson }
54ca737ff3SNick Mathewson
55ca737ff3SNick Mathewson static void
handle_entry(OVERLAPPED * o,ULONG_PTR completion_key,DWORD nBytes,int ok)56a84c87d7SNick Mathewson handle_entry(OVERLAPPED *o, ULONG_PTR completion_key, DWORD nBytes, int ok)
57ca737ff3SNick Mathewson {
58ca737ff3SNick Mathewson struct event_overlapped *eo =
59ca737ff3SNick Mathewson EVUTIL_UPCAST(o, struct event_overlapped, overlapped);
60a84c87d7SNick Mathewson eo->cb(eo, completion_key, nBytes, ok);
61ca737ff3SNick Mathewson }
62ca737ff3SNick Mathewson
63ca737ff3SNick Mathewson static void
loop(void * port_)64946b5841SNick Mathewson loop(void *port_)
65ca737ff3SNick Mathewson {
66946b5841SNick Mathewson struct event_iocp_port *port = port_;
670b987813SNick Mathewson long ms = port->ms;
68f1090833SNick Mathewson HANDLE p = port->port;
69ca737ff3SNick Mathewson
70ca737ff3SNick Mathewson if (ms <= 0)
71ca737ff3SNick Mathewson ms = INFINITE;
72ca737ff3SNick Mathewson
73a84c87d7SNick Mathewson while (1) {
74a84c87d7SNick Mathewson OVERLAPPED *overlapped=NULL;
75a84c87d7SNick Mathewson ULONG_PTR key=0;
76a84c87d7SNick Mathewson DWORD bytes=0;
77a84c87d7SNick Mathewson int ok = GetQueuedCompletionStatus(p, &bytes, &key,
78a84c87d7SNick Mathewson &overlapped, ms);
79f1090833SNick Mathewson EnterCriticalSection(&port->lock);
80f1090833SNick Mathewson if (port->shutdown) {
81f1090833SNick Mathewson if (--port->n_live_threads == 0)
82d844242fSChristopher Davis ReleaseSemaphore(port->shutdownSemaphore, 1,
83d844242fSChristopher Davis NULL);
84f1090833SNick Mathewson LeaveCriticalSection(&port->lock);
850b987813SNick Mathewson return;
86f1090833SNick Mathewson }
87f1090833SNick Mathewson LeaveCriticalSection(&port->lock);
88f1090833SNick Mathewson
89a84c87d7SNick Mathewson if (key != NOTIFICATION_KEY && overlapped)
90a84c87d7SNick Mathewson handle_entry(overlapped, key, bytes, ok);
91a84c87d7SNick Mathewson else if (!overlapped)
92a84c87d7SNick Mathewson break;
93ca737ff3SNick Mathewson }
940b987813SNick Mathewson event_warnx("GetQueuedCompletionStatus exited with no event.");
95f1090833SNick Mathewson EnterCriticalSection(&port->lock);
96f1090833SNick Mathewson if (--port->n_live_threads == 0)
97f1090833SNick Mathewson ReleaseSemaphore(port->shutdownSemaphore, 1, NULL);
98f1090833SNick Mathewson LeaveCriticalSection(&port->lock);
99ca737ff3SNick Mathewson }
100ca737ff3SNick Mathewson
1010b987813SNick Mathewson int
event_iocp_port_associate_(struct event_iocp_port * port,evutil_socket_t fd,ev_uintptr_t key)1028ac3c4c2SNick Mathewson event_iocp_port_associate_(struct event_iocp_port *port, evutil_socket_t fd,
103cef61a2fSNick Mathewson ev_uintptr_t key)
1040b987813SNick Mathewson {
1050b987813SNick Mathewson HANDLE h;
1060b987813SNick Mathewson h = CreateIoCompletionPort((HANDLE)fd, port->port, key, port->n_threads);
1070b987813SNick Mathewson if (!h)
1080b987813SNick Mathewson return -1;
1090b987813SNick Mathewson return 0;
1100b987813SNick Mathewson }
1110b987813SNick Mathewson
112fa313f28SNick Mathewson static void *
get_extension_function(SOCKET s,const GUID * which_fn)113fa313f28SNick Mathewson get_extension_function(SOCKET s, const GUID *which_fn)
114fa313f28SNick Mathewson {
115fa313f28SNick Mathewson void *ptr = NULL;
116fa313f28SNick Mathewson DWORD bytes=0;
117fa313f28SNick Mathewson WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER,
118fa313f28SNick Mathewson (GUID*)which_fn, sizeof(*which_fn),
119fa313f28SNick Mathewson &ptr, sizeof(ptr),
120fa313f28SNick Mathewson &bytes, NULL, NULL);
121fa313f28SNick Mathewson
122fa313f28SNick Mathewson /* No need to detect errors here: if ptr is set, then we have a good
123fa313f28SNick Mathewson function pointer. Otherwise, we should behave as if we had no
124fa313f28SNick Mathewson function pointer.
125fa313f28SNick Mathewson */
126fa313f28SNick Mathewson return ptr;
127fa313f28SNick Mathewson }
128fa313f28SNick Mathewson
129fa313f28SNick Mathewson /* Mingw doesn't have these in its mswsock.h. The values are copied from
130fa313f28SNick Mathewson wine.h. Perhaps if we copy them exactly, the cargo will come again.
131fa313f28SNick Mathewson */
132fa313f28SNick Mathewson #ifndef WSAID_ACCEPTEX
133fa313f28SNick Mathewson #define WSAID_ACCEPTEX \
134fa313f28SNick Mathewson {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}
135fa313f28SNick Mathewson #endif
136fa313f28SNick Mathewson #ifndef WSAID_CONNECTEX
137fa313f28SNick Mathewson #define WSAID_CONNECTEX \
138fa313f28SNick Mathewson {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}
139fa313f28SNick Mathewson #endif
140fa313f28SNick Mathewson #ifndef WSAID_GETACCEPTEXSOCKADDRS
141fa313f28SNick Mathewson #define WSAID_GETACCEPTEXSOCKADDRS \
142fa313f28SNick Mathewson {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}
143fa313f28SNick Mathewson #endif
144fa313f28SNick Mathewson
1453b77d628SNick Mathewson static int extension_fns_initialized = 0;
1463b77d628SNick Mathewson
147fa313f28SNick Mathewson static void
init_extension_functions(struct win32_extension_fns * ext)148fa313f28SNick Mathewson init_extension_functions(struct win32_extension_fns *ext)
149fa313f28SNick Mathewson {
150fa313f28SNick Mathewson const GUID acceptex = WSAID_ACCEPTEX;
151fa313f28SNick Mathewson const GUID connectex = WSAID_CONNECTEX;
152fa313f28SNick Mathewson const GUID getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
153fa313f28SNick Mathewson SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
154*e66078a0SAzat Khuzhin if (s == EVUTIL_INVALID_SOCKET)
155fa313f28SNick Mathewson return;
156fa313f28SNick Mathewson ext->AcceptEx = get_extension_function(s, &acceptex);
157fa313f28SNick Mathewson ext->ConnectEx = get_extension_function(s, &connectex);
158fa313f28SNick Mathewson ext->GetAcceptExSockaddrs = get_extension_function(s,
159fa313f28SNick Mathewson &getacceptexsockaddrs);
160fa313f28SNick Mathewson closesocket(s);
1613b77d628SNick Mathewson
1623b77d628SNick Mathewson extension_fns_initialized = 1;
163fa313f28SNick Mathewson }
164fa313f28SNick Mathewson
165fa313f28SNick Mathewson static struct win32_extension_fns the_extension_fns;
166fa313f28SNick Mathewson
167fa313f28SNick Mathewson const struct win32_extension_fns *
event_get_win32_extension_fns_(void)1688ac3c4c2SNick Mathewson event_get_win32_extension_fns_(void)
169fa313f28SNick Mathewson {
170fa313f28SNick Mathewson return &the_extension_fns;
171fa313f28SNick Mathewson }
172fa313f28SNick Mathewson
1732447fe88SChristopher Davis #define N_CPUS_DEFAULT 2
1742447fe88SChristopher Davis
1750b987813SNick Mathewson struct event_iocp_port *
event_iocp_port_launch_(int n_cpus)1768ac3c4c2SNick Mathewson event_iocp_port_launch_(int n_cpus)
1770b987813SNick Mathewson {
1780b987813SNick Mathewson struct event_iocp_port *port;
179f1090833SNick Mathewson int i;
1800b987813SNick Mathewson
181fa313f28SNick Mathewson if (!extension_fns_initialized)
182fa313f28SNick Mathewson init_extension_functions(&the_extension_fns);
183fa313f28SNick Mathewson
1840b987813SNick Mathewson if (!(port = mm_calloc(1, sizeof(struct event_iocp_port))))
1850b987813SNick Mathewson return NULL;
1862447fe88SChristopher Davis
1872447fe88SChristopher Davis if (n_cpus <= 0)
1882447fe88SChristopher Davis n_cpus = N_CPUS_DEFAULT;
1892447fe88SChristopher Davis port->n_threads = n_cpus * 2;
19019715a60SNick Mathewson port->threads = mm_calloc(port->n_threads, sizeof(HANDLE));
191f1090833SNick Mathewson if (!port->threads)
192f1090833SNick Mathewson goto err;
193f1090833SNick Mathewson
1942447fe88SChristopher Davis port->port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
1952447fe88SChristopher Davis n_cpus);
1960b987813SNick Mathewson port->ms = -1;
1970b987813SNick Mathewson if (!port->port)
198f1090833SNick Mathewson goto err;
1990b987813SNick Mathewson
200f1090833SNick Mathewson port->shutdownSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
201f1090833SNick Mathewson if (!port->shutdownSemaphore)
202f1090833SNick Mathewson goto err;
2030b987813SNick Mathewson
204f1090833SNick Mathewson for (i=0; i<port->n_threads; ++i) {
205cef61a2fSNick Mathewson ev_uintptr_t th = _beginthread(loop, 0, port);
206cef61a2fSNick Mathewson if (th == (ev_uintptr_t)-1)
207f1090833SNick Mathewson goto err;
208f1090833SNick Mathewson port->threads[i] = (HANDLE)th;
209f1090833SNick Mathewson ++port->n_live_threads;
2100b987813SNick Mathewson }
2110b987813SNick Mathewson
21232c6f1baSNick Mathewson InitializeCriticalSectionAndSpinCount(&port->lock, 1000);
2130b987813SNick Mathewson
214f1090833SNick Mathewson return port;
215f1090833SNick Mathewson err:
216f1090833SNick Mathewson if (port->port)
217f1090833SNick Mathewson CloseHandle(port->port);
218f1090833SNick Mathewson if (port->threads)
219f1090833SNick Mathewson mm_free(port->threads);
220f1090833SNick Mathewson if (port->shutdownSemaphore)
221f1090833SNick Mathewson CloseHandle(port->shutdownSemaphore);
222f1090833SNick Mathewson mm_free(port);
223f1090833SNick Mathewson return NULL;
224f1090833SNick Mathewson }
225f1090833SNick Mathewson
226f1090833SNick Mathewson static void
event_iocp_port_unlock_and_free_(struct event_iocp_port * port)227cb9da0bfSNick Mathewson event_iocp_port_unlock_and_free_(struct event_iocp_port *port)
2280b987813SNick Mathewson {
229f1090833SNick Mathewson DeleteCriticalSection(&port->lock);
230f1090833SNick Mathewson CloseHandle(port->port);
231f1090833SNick Mathewson CloseHandle(port->shutdownSemaphore);
232f1090833SNick Mathewson mm_free(port->threads);
233f1090833SNick Mathewson mm_free(port);
234f1090833SNick Mathewson }
235f1090833SNick Mathewson
236f1090833SNick Mathewson static int
event_iocp_notify_all(struct event_iocp_port * port)237f1090833SNick Mathewson event_iocp_notify_all(struct event_iocp_port *port)
238f1090833SNick Mathewson {
239f1090833SNick Mathewson int i, r, ok=1;
240f1090833SNick Mathewson for (i=0; i<port->n_threads; ++i) {
241f1090833SNick Mathewson r = PostQueuedCompletionStatus(port->port, 0, NOTIFICATION_KEY,
242f1090833SNick Mathewson NULL);
243f1090833SNick Mathewson if (!r)
244f1090833SNick Mathewson ok = 0;
245f1090833SNick Mathewson }
246f1090833SNick Mathewson return ok ? 0 : -1;
247f1090833SNick Mathewson }
248f1090833SNick Mathewson
249f1090833SNick Mathewson int
event_iocp_shutdown_(struct event_iocp_port * port,long waitMsec)2508ac3c4c2SNick Mathewson event_iocp_shutdown_(struct event_iocp_port *port, long waitMsec)
251f1090833SNick Mathewson {
252d844242fSChristopher Davis DWORD ms = INFINITE;
253f1090833SNick Mathewson int n;
254d844242fSChristopher Davis
255f1090833SNick Mathewson EnterCriticalSection(&port->lock);
2560b987813SNick Mathewson port->shutdown = 1;
257f1090833SNick Mathewson LeaveCriticalSection(&port->lock);
258f1090833SNick Mathewson event_iocp_notify_all(port);
259f1090833SNick Mathewson
260d844242fSChristopher Davis if (waitMsec >= 0)
261d844242fSChristopher Davis ms = waitMsec;
262d844242fSChristopher Davis
263d844242fSChristopher Davis WaitForSingleObject(port->shutdownSemaphore, ms);
264f1090833SNick Mathewson EnterCriticalSection(&port->lock);
265f1090833SNick Mathewson n = port->n_live_threads;
266f1090833SNick Mathewson LeaveCriticalSection(&port->lock);
267f1090833SNick Mathewson if (n == 0) {
268cb9da0bfSNick Mathewson event_iocp_port_unlock_and_free_(port);
269f1090833SNick Mathewson return 0;
270f1090833SNick Mathewson } else {
271f1090833SNick Mathewson return -1;
272f1090833SNick Mathewson }
273f1090833SNick Mathewson }
274f1090833SNick Mathewson
275f1090833SNick Mathewson int
event_iocp_activate_overlapped_(struct event_iocp_port * port,struct event_overlapped * o,ev_uintptr_t key,ev_uint32_t n)2768ac3c4c2SNick Mathewson event_iocp_activate_overlapped_(
277f1090833SNick Mathewson struct event_iocp_port *port, struct event_overlapped *o,
278cef61a2fSNick Mathewson ev_uintptr_t key, ev_uint32_t n)
279f1090833SNick Mathewson {
280f1090833SNick Mathewson BOOL r;
281f1090833SNick Mathewson
282f1090833SNick Mathewson r = PostQueuedCompletionStatus(port->port, n, key, &o->overlapped);
283f1090833SNick Mathewson return (r==0) ? -1 : 0;
2840b987813SNick Mathewson }
285b69d03b5SNick Mathewson
286fe47003dSNick Mathewson struct event_iocp_port *
event_base_get_iocp_(struct event_base * base)2878ac3c4c2SNick Mathewson event_base_get_iocp_(struct event_base *base)
288b69d03b5SNick Mathewson {
2899f560bfaSNick Mathewson #ifdef _WIN32
290b69d03b5SNick Mathewson return base->iocp;
291b69d03b5SNick Mathewson #else
2927a844735SNick Mathewson return NULL;
293b69d03b5SNick Mathewson #endif
294b69d03b5SNick Mathewson }
295