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