xref: /netbsd-src/external/bsd/libevent/dist/buffer_iocp.c (revision 657871a79c9a2060a6255a242fa1a1ef76b56ec6)
1*657871a7Schristos /*	$NetBSD: buffer_iocp.c,v 1.1.1.3 2021/04/07 02:43:13 christos Exp $	*/
26ecf6635Schristos /*
36ecf6635Schristos  * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson
46ecf6635Schristos  *
56ecf6635Schristos  * Redistribution and use in source and binary forms, with or without
66ecf6635Schristos  * modification, are permitted provided that the following conditions
76ecf6635Schristos  * are met:
86ecf6635Schristos  * 1. Redistributions of source code must retain the above copyright
96ecf6635Schristos  *    notice, this list of conditions and the following disclaimer.
106ecf6635Schristos  * 2. Redistributions in binary form must reproduce the above copyright
116ecf6635Schristos  *    notice, this list of conditions and the following disclaimer in the
126ecf6635Schristos  *    documentation and/or other materials provided with the distribution.
136ecf6635Schristos  * 3. The name of the author may not be used to endorse or promote products
146ecf6635Schristos  *    derived from this software without specific prior written permission.
156ecf6635Schristos  *
166ecf6635Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
176ecf6635Schristos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
186ecf6635Schristos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
196ecf6635Schristos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
206ecf6635Schristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
216ecf6635Schristos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
226ecf6635Schristos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
236ecf6635Schristos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
246ecf6635Schristos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
256ecf6635Schristos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
266ecf6635Schristos  */
276ecf6635Schristos 
286ecf6635Schristos /**
296ecf6635Schristos    @file buffer_iocp.c
306ecf6635Schristos 
316ecf6635Schristos    This module implements overlapped read and write functions for evbuffer
326ecf6635Schristos    objects on Windows.
336ecf6635Schristos */
34805a1ce9Schristos #include "event2/event-config.h"
35805a1ce9Schristos #include <sys/cdefs.h>
36*657871a7Schristos __RCSID("$NetBSD: buffer_iocp.c,v 1.1.1.3 2021/04/07 02:43:13 christos Exp $");
37805a1ce9Schristos #include "evconfig-private.h"
386ecf6635Schristos 
396ecf6635Schristos #include "event2/buffer.h"
406ecf6635Schristos #include "event2/buffer_compat.h"
416ecf6635Schristos #include "event2/util.h"
426ecf6635Schristos #include "event2/thread.h"
436ecf6635Schristos #include "util-internal.h"
446ecf6635Schristos #include "evthread-internal.h"
456ecf6635Schristos #include "evbuffer-internal.h"
466ecf6635Schristos #include "iocp-internal.h"
476ecf6635Schristos #include "mm-internal.h"
486ecf6635Schristos 
496ecf6635Schristos #include <winsock2.h>
50*657871a7Schristos #include <winerror.h>
516ecf6635Schristos #include <windows.h>
526ecf6635Schristos #include <stdio.h>
536ecf6635Schristos 
546ecf6635Schristos #define MAX_WSABUFS 16
556ecf6635Schristos 
566ecf6635Schristos /** An evbuffer that can handle overlapped IO. */
576ecf6635Schristos struct evbuffer_overlapped {
586ecf6635Schristos 	struct evbuffer buffer;
596ecf6635Schristos 	/** The socket that we're doing overlapped IO on. */
606ecf6635Schristos 	evutil_socket_t fd;
616ecf6635Schristos 
626ecf6635Schristos 	/** pending I/O type */
636ecf6635Schristos 	unsigned read_in_progress : 1;
646ecf6635Schristos 	unsigned write_in_progress : 1;
656ecf6635Schristos 
666ecf6635Schristos 	/** The first pinned chain in the buffer. */
676ecf6635Schristos 	struct evbuffer_chain *first_pinned;
686ecf6635Schristos 
696ecf6635Schristos 	/** How many chains are pinned; how many of the fields in buffers
706ecf6635Schristos 	 * are we using. */
716ecf6635Schristos 	int n_buffers;
726ecf6635Schristos 	WSABUF buffers[MAX_WSABUFS];
736ecf6635Schristos };
746ecf6635Schristos 
756ecf6635Schristos /** Given an evbuffer, return the correponding evbuffer structure, or NULL if
766ecf6635Schristos  * the evbuffer isn't overlapped. */
776ecf6635Schristos static inline struct evbuffer_overlapped *
upcast_evbuffer(struct evbuffer * buf)786ecf6635Schristos upcast_evbuffer(struct evbuffer *buf)
796ecf6635Schristos {
806ecf6635Schristos 	if (!buf || !buf->is_overlapped)
816ecf6635Schristos 		return NULL;
826ecf6635Schristos 	return EVUTIL_UPCAST(buf, struct evbuffer_overlapped, buffer);
836ecf6635Schristos }
846ecf6635Schristos 
856ecf6635Schristos /** Unpin all the chains noted as pinned in 'eo'. */
866ecf6635Schristos static void
pin_release(struct evbuffer_overlapped * eo,unsigned flag)876ecf6635Schristos pin_release(struct evbuffer_overlapped *eo, unsigned flag)
886ecf6635Schristos {
896ecf6635Schristos 	int i;
906ecf6635Schristos 	struct evbuffer_chain *next, *chain = eo->first_pinned;
916ecf6635Schristos 
926ecf6635Schristos 	for (i = 0; i < eo->n_buffers; ++i) {
936ecf6635Schristos 		EVUTIL_ASSERT(chain);
946ecf6635Schristos 		next = chain->next;
95805a1ce9Schristos 		evbuffer_chain_unpin_(chain, flag);
966ecf6635Schristos 		chain = next;
976ecf6635Schristos 	}
986ecf6635Schristos }
996ecf6635Schristos 
1006ecf6635Schristos void
evbuffer_commit_read_(struct evbuffer * evbuf,ev_ssize_t nBytes)101805a1ce9Schristos evbuffer_commit_read_(struct evbuffer *evbuf, ev_ssize_t nBytes)
1026ecf6635Schristos {
1036ecf6635Schristos 	struct evbuffer_overlapped *buf = upcast_evbuffer(evbuf);
1046ecf6635Schristos 	struct evbuffer_chain **chainp;
1056ecf6635Schristos 	size_t remaining, len;
1066ecf6635Schristos 	unsigned i;
1076ecf6635Schristos 
1086ecf6635Schristos 	EVBUFFER_LOCK(evbuf);
1096ecf6635Schristos 	EVUTIL_ASSERT(buf->read_in_progress && !buf->write_in_progress);
1106ecf6635Schristos 	EVUTIL_ASSERT(nBytes >= 0); /* XXXX Can this be false? */
1116ecf6635Schristos 
1126ecf6635Schristos 	evbuffer_unfreeze(evbuf, 0);
1136ecf6635Schristos 
1146ecf6635Schristos 	chainp = evbuf->last_with_datap;
1156ecf6635Schristos 	if (!((*chainp)->flags & EVBUFFER_MEM_PINNED_R))
1166ecf6635Schristos 		chainp = &(*chainp)->next;
1176ecf6635Schristos 	remaining = nBytes;
1186ecf6635Schristos 	for (i = 0; remaining > 0 && i < (unsigned)buf->n_buffers; ++i) {
1196ecf6635Schristos 		EVUTIL_ASSERT(*chainp);
1206ecf6635Schristos 		len = buf->buffers[i].len;
1216ecf6635Schristos 		if (remaining < len)
1226ecf6635Schristos 			len = remaining;
1236ecf6635Schristos 		(*chainp)->off += len;
1246ecf6635Schristos 		evbuf->last_with_datap = chainp;
1256ecf6635Schristos 		remaining -= len;
1266ecf6635Schristos 		chainp = &(*chainp)->next;
1276ecf6635Schristos 	}
1286ecf6635Schristos 
1296ecf6635Schristos 	pin_release(buf, EVBUFFER_MEM_PINNED_R);
1306ecf6635Schristos 
1316ecf6635Schristos 	buf->read_in_progress = 0;
1326ecf6635Schristos 
1336ecf6635Schristos 	evbuf->total_len += nBytes;
1346ecf6635Schristos 	evbuf->n_add_for_cb += nBytes;
1356ecf6635Schristos 
136805a1ce9Schristos 	evbuffer_invoke_callbacks_(evbuf);
1376ecf6635Schristos 
138805a1ce9Schristos 	evbuffer_decref_and_unlock_(evbuf);
1396ecf6635Schristos }
1406ecf6635Schristos 
1416ecf6635Schristos void
evbuffer_commit_write_(struct evbuffer * evbuf,ev_ssize_t nBytes)142805a1ce9Schristos evbuffer_commit_write_(struct evbuffer *evbuf, ev_ssize_t nBytes)
1436ecf6635Schristos {
1446ecf6635Schristos 	struct evbuffer_overlapped *buf = upcast_evbuffer(evbuf);
1456ecf6635Schristos 
1466ecf6635Schristos 	EVBUFFER_LOCK(evbuf);
1476ecf6635Schristos 	EVUTIL_ASSERT(buf->write_in_progress && !buf->read_in_progress);
1486ecf6635Schristos 	evbuffer_unfreeze(evbuf, 1);
1496ecf6635Schristos 	evbuffer_drain(evbuf, nBytes);
1506ecf6635Schristos 	pin_release(buf,EVBUFFER_MEM_PINNED_W);
1516ecf6635Schristos 	buf->write_in_progress = 0;
152805a1ce9Schristos 	evbuffer_decref_and_unlock_(evbuf);
1536ecf6635Schristos }
1546ecf6635Schristos 
1556ecf6635Schristos struct evbuffer *
evbuffer_overlapped_new_(evutil_socket_t fd)156805a1ce9Schristos evbuffer_overlapped_new_(evutil_socket_t fd)
1576ecf6635Schristos {
1586ecf6635Schristos 	struct evbuffer_overlapped *evo;
1596ecf6635Schristos 
1606ecf6635Schristos 	evo = mm_calloc(1, sizeof(struct evbuffer_overlapped));
1616ecf6635Schristos 	if (!evo)
1626ecf6635Schristos 		return NULL;
1636ecf6635Schristos 
164805a1ce9Schristos 	LIST_INIT(&evo->buffer.callbacks);
1656ecf6635Schristos 	evo->buffer.refcnt = 1;
1666ecf6635Schristos 	evo->buffer.last_with_datap = &evo->buffer.first;
1676ecf6635Schristos 
1686ecf6635Schristos 	evo->buffer.is_overlapped = 1;
1696ecf6635Schristos 	evo->fd = fd;
1706ecf6635Schristos 
1716ecf6635Schristos 	return &evo->buffer;
1726ecf6635Schristos }
1736ecf6635Schristos 
1746ecf6635Schristos int
evbuffer_launch_write_(struct evbuffer * buf,ev_ssize_t at_most,struct event_overlapped * ol)175805a1ce9Schristos evbuffer_launch_write_(struct evbuffer *buf, ev_ssize_t at_most,
1766ecf6635Schristos 		struct event_overlapped *ol)
1776ecf6635Schristos {
1786ecf6635Schristos 	struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
1796ecf6635Schristos 	int r = -1;
1806ecf6635Schristos 	int i;
1816ecf6635Schristos 	struct evbuffer_chain *chain;
1826ecf6635Schristos 	DWORD bytesSent;
1836ecf6635Schristos 
1846ecf6635Schristos 	if (!buf) {
1856ecf6635Schristos 		/* No buffer, or it isn't overlapped */
1866ecf6635Schristos 		return -1;
1876ecf6635Schristos 	}
1886ecf6635Schristos 
1896ecf6635Schristos 	EVBUFFER_LOCK(buf);
1906ecf6635Schristos 	EVUTIL_ASSERT(!buf_o->read_in_progress);
1916ecf6635Schristos 	if (buf->freeze_start || buf_o->write_in_progress)
1926ecf6635Schristos 		goto done;
1936ecf6635Schristos 	if (!buf->total_len) {
1946ecf6635Schristos 		/* Nothing to write */
1956ecf6635Schristos 		r = 0;
1966ecf6635Schristos 		goto done;
1976ecf6635Schristos 	} else if (at_most < 0 || (size_t)at_most > buf->total_len) {
1986ecf6635Schristos 		at_most = buf->total_len;
1996ecf6635Schristos 	}
2006ecf6635Schristos 	evbuffer_freeze(buf, 1);
2016ecf6635Schristos 
2026ecf6635Schristos 	buf_o->first_pinned = NULL;
2036ecf6635Schristos 	buf_o->n_buffers = 0;
2046ecf6635Schristos 	memset(buf_o->buffers, 0, sizeof(buf_o->buffers));
2056ecf6635Schristos 
2066ecf6635Schristos 	chain = buf_o->first_pinned = buf->first;
2076ecf6635Schristos 
2086ecf6635Schristos 	for (i=0; i < MAX_WSABUFS && chain; ++i, chain=chain->next) {
2096ecf6635Schristos 		WSABUF *b = &buf_o->buffers[i];
2106ecf6635Schristos 		b->buf = (char*)( chain->buffer + chain->misalign );
211805a1ce9Schristos 		evbuffer_chain_pin_(chain, EVBUFFER_MEM_PINNED_W);
2126ecf6635Schristos 
2136ecf6635Schristos 		if ((size_t)at_most > chain->off) {
2146ecf6635Schristos 			/* XXXX Cast is safe for now, since win32 has no
2156ecf6635Schristos 			   mmaped chains.  But later, we need to have this
2166ecf6635Schristos 			   add more WSAbufs if chain->off is greater than
2176ecf6635Schristos 			   ULONG_MAX */
2186ecf6635Schristos 			b->len = (unsigned long)chain->off;
2196ecf6635Schristos 			at_most -= chain->off;
2206ecf6635Schristos 		} else {
2216ecf6635Schristos 			b->len = (unsigned long)at_most;
2226ecf6635Schristos 			++i;
2236ecf6635Schristos 			break;
2246ecf6635Schristos 		}
2256ecf6635Schristos 	}
2266ecf6635Schristos 
2276ecf6635Schristos 	buf_o->n_buffers = i;
228805a1ce9Schristos 	evbuffer_incref_(buf);
2296ecf6635Schristos 	if (WSASend(buf_o->fd, buf_o->buffers, i, &bytesSent, 0,
2306ecf6635Schristos 		&ol->overlapped, NULL)) {
2316ecf6635Schristos 		int error = WSAGetLastError();
2326ecf6635Schristos 		if (error != WSA_IO_PENDING) {
2336ecf6635Schristos 			/* An actual error. */
2346ecf6635Schristos 			pin_release(buf_o, EVBUFFER_MEM_PINNED_W);
2356ecf6635Schristos 			evbuffer_unfreeze(buf, 1);
2366ecf6635Schristos 			evbuffer_free(buf); /* decref */
2376ecf6635Schristos 			goto done;
2386ecf6635Schristos 		}
2396ecf6635Schristos 	}
2406ecf6635Schristos 
2416ecf6635Schristos 	buf_o->write_in_progress = 1;
2426ecf6635Schristos 	r = 0;
2436ecf6635Schristos done:
2446ecf6635Schristos 	EVBUFFER_UNLOCK(buf);
2456ecf6635Schristos 	return r;
2466ecf6635Schristos }
2476ecf6635Schristos 
2486ecf6635Schristos int
evbuffer_launch_read_(struct evbuffer * buf,size_t at_most,struct event_overlapped * ol)249805a1ce9Schristos evbuffer_launch_read_(struct evbuffer *buf, size_t at_most,
2506ecf6635Schristos 		struct event_overlapped *ol)
2516ecf6635Schristos {
2526ecf6635Schristos 	struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
2536ecf6635Schristos 	int r = -1, i;
2546ecf6635Schristos 	int nvecs;
2556ecf6635Schristos 	int npin=0;
2566ecf6635Schristos 	struct evbuffer_chain *chain=NULL, **chainp;
2576ecf6635Schristos 	DWORD bytesRead;
2586ecf6635Schristos 	DWORD flags = 0;
2596ecf6635Schristos 	struct evbuffer_iovec vecs[MAX_WSABUFS];
2606ecf6635Schristos 
2616ecf6635Schristos 	if (!buf_o)
2626ecf6635Schristos 		return -1;
2636ecf6635Schristos 	EVBUFFER_LOCK(buf);
2646ecf6635Schristos 	EVUTIL_ASSERT(!buf_o->write_in_progress);
2656ecf6635Schristos 	if (buf->freeze_end || buf_o->read_in_progress)
2666ecf6635Schristos 		goto done;
2676ecf6635Schristos 
2686ecf6635Schristos 	buf_o->first_pinned = NULL;
2696ecf6635Schristos 	buf_o->n_buffers = 0;
2706ecf6635Schristos 	memset(buf_o->buffers, 0, sizeof(buf_o->buffers));
2716ecf6635Schristos 
272805a1ce9Schristos 	if (evbuffer_expand_fast_(buf, at_most, MAX_WSABUFS) == -1)
2736ecf6635Schristos 		goto done;
2746ecf6635Schristos 	evbuffer_freeze(buf, 0);
2756ecf6635Schristos 
276805a1ce9Schristos 	nvecs = evbuffer_read_setup_vecs_(buf, at_most,
2776ecf6635Schristos 	    vecs, MAX_WSABUFS, &chainp, 1);
2786ecf6635Schristos 	for (i=0;i<nvecs;++i) {
2796ecf6635Schristos 		WSABUF_FROM_EVBUFFER_IOV(
2806ecf6635Schristos 			&buf_o->buffers[i],
2816ecf6635Schristos 			&vecs[i]);
2826ecf6635Schristos 	}
2836ecf6635Schristos 
2846ecf6635Schristos 	buf_o->n_buffers = nvecs;
2856ecf6635Schristos 	buf_o->first_pinned = chain = *chainp;
2866ecf6635Schristos 
2876ecf6635Schristos 	npin=0;
2886ecf6635Schristos 	for ( ; chain; chain = chain->next) {
289805a1ce9Schristos 		evbuffer_chain_pin_(chain, EVBUFFER_MEM_PINNED_R);
2906ecf6635Schristos 		++npin;
2916ecf6635Schristos 	}
2926ecf6635Schristos 	EVUTIL_ASSERT(npin == nvecs);
2936ecf6635Schristos 
294805a1ce9Schristos 	evbuffer_incref_(buf);
2956ecf6635Schristos 	if (WSARecv(buf_o->fd, buf_o->buffers, nvecs, &bytesRead, &flags,
2966ecf6635Schristos 		    &ol->overlapped, NULL)) {
2976ecf6635Schristos 		int error = WSAGetLastError();
2986ecf6635Schristos 		if (error != WSA_IO_PENDING) {
2996ecf6635Schristos 			/* An actual error. */
3006ecf6635Schristos 			pin_release(buf_o, EVBUFFER_MEM_PINNED_R);
3016ecf6635Schristos 			evbuffer_unfreeze(buf, 0);
3026ecf6635Schristos 			evbuffer_free(buf); /* decref */
3036ecf6635Schristos 			goto done;
3046ecf6635Schristos 		}
3056ecf6635Schristos 	}
3066ecf6635Schristos 
3076ecf6635Schristos 	buf_o->read_in_progress = 1;
3086ecf6635Schristos 	r = 0;
3096ecf6635Schristos done:
3106ecf6635Schristos 	EVBUFFER_UNLOCK(buf);
3116ecf6635Schristos 	return r;
3126ecf6635Schristos }
3136ecf6635Schristos 
3146ecf6635Schristos evutil_socket_t
evbuffer_overlapped_get_fd_(struct evbuffer * buf)315805a1ce9Schristos evbuffer_overlapped_get_fd_(struct evbuffer *buf)
3166ecf6635Schristos {
3176ecf6635Schristos 	struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
3186ecf6635Schristos 	return buf_o ? buf_o->fd : -1;
3196ecf6635Schristos }
3206ecf6635Schristos 
3216ecf6635Schristos void
evbuffer_overlapped_set_fd_(struct evbuffer * buf,evutil_socket_t fd)322805a1ce9Schristos evbuffer_overlapped_set_fd_(struct evbuffer *buf, evutil_socket_t fd)
3236ecf6635Schristos {
3246ecf6635Schristos 	struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
3256ecf6635Schristos 	EVBUFFER_LOCK(buf);
3266ecf6635Schristos 	/* XXX is this right?, should it cancel current I/O operations? */
3276ecf6635Schristos 	if (buf_o)
3286ecf6635Schristos 		buf_o->fd = fd;
3296ecf6635Schristos 	EVBUFFER_UNLOCK(buf);
3306ecf6635Schristos }
331