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