|
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 |
|
47 void |
|
48 event_overlapped_init(struct event_overlapped *o, iocp_callback cb) |
|
49 { |
|
50 memset(o, 0, sizeof(struct event_overlapped)); |
|
51 o->cb = cb; |
|
52 } |
|
53 |
|
54 static void |
|
55 handle_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 |
|
62 static void |
|
63 loop(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 |
|
100 int |
|
101 event_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 |
|
111 static void * |
|
112 get_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 |
|
144 static void |
|
145 init_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 |
|
160 static struct win32_extension_fns the_extension_fns; |
|
161 static int extension_fns_initialized = 0; |
|
162 |
|
163 const struct win32_extension_fns * |
|
164 event_get_win32_extension_fns(void) |
|
165 { |
|
166 return &the_extension_fns; |
|
167 } |
|
168 |
|
169 #define N_CPUS_DEFAULT 2 |
|
170 |
|
171 struct event_iocp_port * |
|
172 event_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; |
|
211 err: |
|
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 |
|
222 static 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 |
|
232 static int |
|
233 event_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 |
|
245 int |
|
246 event_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 |
|
271 int |
|
272 event_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 |
|
282 struct event_iocp_port * |
|
283 event_base_get_iocp(struct event_base *base) |
|
284 { |
|
285 #ifdef WIN32 |
|
286 return base->iocp; |
|
287 #else |
|
288 return NULL; |
|
289 #endif |
|
290 } |