xref: /netbsd-src/external/bsd/ntp/dist/sntp/libevent/event_iocp.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: event_iocp.c,v 1.6 2024/08/18 20:47:21 christos Exp $	*/
28585484eSchristos 
38585484eSchristos /*
48585484eSchristos  * Copyright (c) 2009-2012 Niels Provos, Nick Mathewson
58585484eSchristos  *
68585484eSchristos  * Redistribution and use in source and binary forms, with or without
78585484eSchristos  * modification, are permitted provided that the following conditions
88585484eSchristos  * are met:
98585484eSchristos  * 1. Redistributions of source code must retain the above copyright
108585484eSchristos  *    notice, this list of conditions and the following disclaimer.
118585484eSchristos  * 2. Redistributions in binary form must reproduce the above copyright
128585484eSchristos  *    notice, this list of conditions and the following disclaimer in the
138585484eSchristos  *    documentation and/or other materials provided with the distribution.
148585484eSchristos  * 3. The name of the author may not be used to endorse or promote products
158585484eSchristos  *    derived from this software without specific prior written permission.
168585484eSchristos  *
178585484eSchristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
188585484eSchristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
198585484eSchristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
208585484eSchristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
218585484eSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
228585484eSchristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
238585484eSchristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
248585484eSchristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
258585484eSchristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
268585484eSchristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
278585484eSchristos  */
288585484eSchristos #include "evconfig-private.h"
298585484eSchristos 
308585484eSchristos #ifndef _WIN32_WINNT
318585484eSchristos /* Minimum required for InitializeCriticalSectionAndSpinCount */
328585484eSchristos #define _WIN32_WINNT 0x0403
338585484eSchristos #endif
348585484eSchristos #include <winsock2.h>
358585484eSchristos #include <windows.h>
368585484eSchristos #include <process.h>
378585484eSchristos #include <stdio.h>
388585484eSchristos #include <mswsock.h>
398585484eSchristos 
408585484eSchristos #include "event2/util.h"
418585484eSchristos #include "util-internal.h"
428585484eSchristos #include "iocp-internal.h"
438585484eSchristos #include "log-internal.h"
448585484eSchristos #include "mm-internal.h"
458585484eSchristos #include "event-internal.h"
468585484eSchristos #include "evthread-internal.h"
478585484eSchristos 
488585484eSchristos #define NOTIFICATION_KEY ((ULONG_PTR)-1)
498585484eSchristos 
508585484eSchristos void
518585484eSchristos event_overlapped_init_(struct event_overlapped *o, iocp_callback cb)
528585484eSchristos {
538585484eSchristos 	memset(o, 0, sizeof(struct event_overlapped));
548585484eSchristos 	o->cb = cb;
558585484eSchristos }
568585484eSchristos 
578585484eSchristos static void
588585484eSchristos handle_entry(OVERLAPPED *o, ULONG_PTR completion_key, DWORD nBytes, int ok)
598585484eSchristos {
608585484eSchristos 	struct event_overlapped *eo =
618585484eSchristos 	    EVUTIL_UPCAST(o, struct event_overlapped, overlapped);
628585484eSchristos 	eo->cb(eo, completion_key, nBytes, ok);
638585484eSchristos }
648585484eSchristos 
658585484eSchristos static void
668585484eSchristos loop(void *port_)
678585484eSchristos {
688585484eSchristos 	struct event_iocp_port *port = port_;
698585484eSchristos 	long ms = port->ms;
708585484eSchristos 	HANDLE p = port->port;
718585484eSchristos 
728585484eSchristos 	if (ms <= 0)
738585484eSchristos 		ms = INFINITE;
748585484eSchristos 
758585484eSchristos 	while (1) {
768585484eSchristos 		OVERLAPPED *overlapped=NULL;
778585484eSchristos 		ULONG_PTR key=0;
788585484eSchristos 		DWORD bytes=0;
798585484eSchristos 		int ok = GetQueuedCompletionStatus(p, &bytes, &key,
808585484eSchristos 			&overlapped, ms);
818585484eSchristos 		EnterCriticalSection(&port->lock);
828585484eSchristos 		if (port->shutdown) {
838585484eSchristos 			if (--port->n_live_threads == 0)
848585484eSchristos 				ReleaseSemaphore(port->shutdownSemaphore, 1,
858585484eSchristos 						NULL);
868585484eSchristos 			LeaveCriticalSection(&port->lock);
878585484eSchristos 			return;
888585484eSchristos 		}
898585484eSchristos 		LeaveCriticalSection(&port->lock);
908585484eSchristos 
918585484eSchristos 		if (key != NOTIFICATION_KEY && overlapped)
928585484eSchristos 			handle_entry(overlapped, key, bytes, ok);
938585484eSchristos 		else if (!overlapped)
948585484eSchristos 			break;
958585484eSchristos 	}
968585484eSchristos 	event_warnx("GetQueuedCompletionStatus exited with no event.");
978585484eSchristos 	EnterCriticalSection(&port->lock);
988585484eSchristos 	if (--port->n_live_threads == 0)
998585484eSchristos 		ReleaseSemaphore(port->shutdownSemaphore, 1, NULL);
1008585484eSchristos 	LeaveCriticalSection(&port->lock);
1018585484eSchristos }
1028585484eSchristos 
1038585484eSchristos int
1048585484eSchristos event_iocp_port_associate_(struct event_iocp_port *port, evutil_socket_t fd,
1058585484eSchristos     ev_uintptr_t key)
1068585484eSchristos {
1078585484eSchristos 	HANDLE h;
1088585484eSchristos 	h = CreateIoCompletionPort((HANDLE)fd, port->port, key, port->n_threads);
1098585484eSchristos 	if (!h)
1108585484eSchristos 		return -1;
1118585484eSchristos 	return 0;
1128585484eSchristos }
1138585484eSchristos 
1148585484eSchristos static void *
1158585484eSchristos get_extension_function(SOCKET s, const GUID *which_fn)
1168585484eSchristos {
1178585484eSchristos 	void *ptr = NULL;
1188585484eSchristos 	DWORD bytes=0;
1198585484eSchristos 	WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER,
1208585484eSchristos 	    (GUID*)which_fn, sizeof(*which_fn),
1218585484eSchristos 	    &ptr, sizeof(ptr),
1228585484eSchristos 	    &bytes, NULL, NULL);
1238585484eSchristos 
1248585484eSchristos 	/* No need to detect errors here: if ptr is set, then we have a good
1258585484eSchristos 	   function pointer.  Otherwise, we should behave as if we had no
1268585484eSchristos 	   function pointer.
1278585484eSchristos 	*/
1288585484eSchristos 	return ptr;
1298585484eSchristos }
1308585484eSchristos 
1318585484eSchristos /* Mingw doesn't have these in its mswsock.h.  The values are copied from
1328585484eSchristos    wine.h.   Perhaps if we copy them exactly, the cargo will come again.
1338585484eSchristos */
1348585484eSchristos #ifndef WSAID_ACCEPTEX
1358585484eSchristos #define WSAID_ACCEPTEX \
1368585484eSchristos 	{0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}
1378585484eSchristos #endif
1388585484eSchristos #ifndef WSAID_CONNECTEX
1398585484eSchristos #define WSAID_CONNECTEX \
1408585484eSchristos 	{0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}
1418585484eSchristos #endif
1428585484eSchristos #ifndef WSAID_GETACCEPTEXSOCKADDRS
1438585484eSchristos #define WSAID_GETACCEPTEXSOCKADDRS \
1448585484eSchristos 	{0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}
1458585484eSchristos #endif
1468585484eSchristos 
147b8ecfcfeSchristos static int extension_fns_initialized = 0;
148b8ecfcfeSchristos 
1498585484eSchristos static void
1508585484eSchristos init_extension_functions(struct win32_extension_fns *ext)
1518585484eSchristos {
1528585484eSchristos 	const GUID acceptex = WSAID_ACCEPTEX;
1538585484eSchristos 	const GUID connectex = WSAID_CONNECTEX;
1548585484eSchristos 	const GUID getacceptexsockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
1558585484eSchristos 	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
156*eabc0478Schristos 	if (s == EVUTIL_INVALID_SOCKET)
1578585484eSchristos 		return;
1588585484eSchristos 	ext->AcceptEx = get_extension_function(s, &acceptex);
1598585484eSchristos 	ext->ConnectEx = get_extension_function(s, &connectex);
1608585484eSchristos 	ext->GetAcceptExSockaddrs = get_extension_function(s,
1618585484eSchristos 	    &getacceptexsockaddrs);
1628585484eSchristos 	closesocket(s);
163b8ecfcfeSchristos 
164b8ecfcfeSchristos 	extension_fns_initialized = 1;
1658585484eSchristos }
1668585484eSchristos 
1678585484eSchristos static struct win32_extension_fns the_extension_fns;
1688585484eSchristos 
1698585484eSchristos const struct win32_extension_fns *
1708585484eSchristos event_get_win32_extension_fns_(void)
1718585484eSchristos {
1728585484eSchristos 	return &the_extension_fns;
1738585484eSchristos }
1748585484eSchristos 
1758585484eSchristos #define N_CPUS_DEFAULT 2
1768585484eSchristos 
1778585484eSchristos struct event_iocp_port *
1788585484eSchristos event_iocp_port_launch_(int n_cpus)
1798585484eSchristos {
1808585484eSchristos 	struct event_iocp_port *port;
1818585484eSchristos 	int i;
1828585484eSchristos 
1838585484eSchristos 	if (!extension_fns_initialized)
1848585484eSchristos 		init_extension_functions(&the_extension_fns);
1858585484eSchristos 
1868585484eSchristos 	if (!(port = mm_calloc(1, sizeof(struct event_iocp_port))))
1878585484eSchristos 		return NULL;
1888585484eSchristos 
1898585484eSchristos 	if (n_cpus <= 0)
1908585484eSchristos 		n_cpus = N_CPUS_DEFAULT;
1918585484eSchristos 	port->n_threads = n_cpus * 2;
1928585484eSchristos 	port->threads = mm_calloc(port->n_threads, sizeof(HANDLE));
1938585484eSchristos 	if (!port->threads)
1948585484eSchristos 		goto err;
1958585484eSchristos 
1968585484eSchristos 	port->port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
1978585484eSchristos 			n_cpus);
1988585484eSchristos 	port->ms = -1;
1998585484eSchristos 	if (!port->port)
2008585484eSchristos 		goto err;
2018585484eSchristos 
2028585484eSchristos 	port->shutdownSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
2038585484eSchristos 	if (!port->shutdownSemaphore)
2048585484eSchristos 		goto err;
2058585484eSchristos 
2068585484eSchristos 	for (i=0; i<port->n_threads; ++i) {
2078585484eSchristos 		ev_uintptr_t th = _beginthread(loop, 0, port);
2088585484eSchristos 		if (th == (ev_uintptr_t)-1)
2098585484eSchristos 			goto err;
2108585484eSchristos 		port->threads[i] = (HANDLE)th;
2118585484eSchristos 		++port->n_live_threads;
2128585484eSchristos 	}
2138585484eSchristos 
2148585484eSchristos 	InitializeCriticalSectionAndSpinCount(&port->lock, 1000);
2158585484eSchristos 
2168585484eSchristos 	return port;
2178585484eSchristos err:
2188585484eSchristos 	if (port->port)
2198585484eSchristos 		CloseHandle(port->port);
2208585484eSchristos 	if (port->threads)
2218585484eSchristos 		mm_free(port->threads);
2228585484eSchristos 	if (port->shutdownSemaphore)
2238585484eSchristos 		CloseHandle(port->shutdownSemaphore);
2248585484eSchristos 	mm_free(port);
2258585484eSchristos 	return NULL;
2268585484eSchristos }
2278585484eSchristos 
2288585484eSchristos static void
2298585484eSchristos event_iocp_port_unlock_and_free_(struct event_iocp_port *port)
2308585484eSchristos {
2318585484eSchristos 	DeleteCriticalSection(&port->lock);
2328585484eSchristos 	CloseHandle(port->port);
2338585484eSchristos 	CloseHandle(port->shutdownSemaphore);
2348585484eSchristos 	mm_free(port->threads);
2358585484eSchristos 	mm_free(port);
2368585484eSchristos }
2378585484eSchristos 
2388585484eSchristos static int
2398585484eSchristos event_iocp_notify_all(struct event_iocp_port *port)
2408585484eSchristos {
2418585484eSchristos 	int i, r, ok=1;
2428585484eSchristos 	for (i=0; i<port->n_threads; ++i) {
2438585484eSchristos 		r = PostQueuedCompletionStatus(port->port, 0, NOTIFICATION_KEY,
2448585484eSchristos 		    NULL);
2458585484eSchristos 		if (!r)
2468585484eSchristos 			ok = 0;
2478585484eSchristos 	}
2488585484eSchristos 	return ok ? 0 : -1;
2498585484eSchristos }
2508585484eSchristos 
2518585484eSchristos int
2528585484eSchristos event_iocp_shutdown_(struct event_iocp_port *port, long waitMsec)
2538585484eSchristos {
2548585484eSchristos 	DWORD ms = INFINITE;
2558585484eSchristos 	int n;
2568585484eSchristos 
2578585484eSchristos 	EnterCriticalSection(&port->lock);
2588585484eSchristos 	port->shutdown = 1;
2598585484eSchristos 	LeaveCriticalSection(&port->lock);
2608585484eSchristos 	event_iocp_notify_all(port);
2618585484eSchristos 
2628585484eSchristos 	if (waitMsec >= 0)
2638585484eSchristos 		ms = waitMsec;
2648585484eSchristos 
2658585484eSchristos 	WaitForSingleObject(port->shutdownSemaphore, ms);
2668585484eSchristos 	EnterCriticalSection(&port->lock);
2678585484eSchristos 	n = port->n_live_threads;
2688585484eSchristos 	LeaveCriticalSection(&port->lock);
2698585484eSchristos 	if (n == 0) {
2708585484eSchristos 		event_iocp_port_unlock_and_free_(port);
2718585484eSchristos 		return 0;
2728585484eSchristos 	} else {
2738585484eSchristos 		return -1;
2748585484eSchristos 	}
2758585484eSchristos }
2768585484eSchristos 
2778585484eSchristos int
2788585484eSchristos event_iocp_activate_overlapped_(
2798585484eSchristos     struct event_iocp_port *port, struct event_overlapped *o,
2808585484eSchristos     ev_uintptr_t key, ev_uint32_t n)
2818585484eSchristos {
2828585484eSchristos 	BOOL r;
2838585484eSchristos 
2848585484eSchristos 	r = PostQueuedCompletionStatus(port->port, n, key, &o->overlapped);
2858585484eSchristos 	return (r==0) ? -1 : 0;
2868585484eSchristos }
2878585484eSchristos 
2888585484eSchristos struct event_iocp_port *
2898585484eSchristos event_base_get_iocp_(struct event_base *base)
2908585484eSchristos {
2918585484eSchristos #ifdef _WIN32
2928585484eSchristos 	return base->iocp;
2938585484eSchristos #else
2948585484eSchristos 	return NULL;
2958585484eSchristos #endif
2968585484eSchristos }
297