1*eabc0478Schristos /* $NetBSD: buffer_iocp.c,v 1.6 2024/08/18 20:47:20 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* 48585484eSchristos * Copyright (c) 2009-2012 Niels Provos and 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 298585484eSchristos /** 308585484eSchristos @file buffer_iocp.c 318585484eSchristos 328585484eSchristos This module implements overlapped read and write functions for evbuffer 338585484eSchristos objects on Windows. 348585484eSchristos */ 358585484eSchristos #include "event2/event-config.h" 368585484eSchristos #include "evconfig-private.h" 378585484eSchristos 388585484eSchristos #include "event2/buffer.h" 398585484eSchristos #include "event2/buffer_compat.h" 408585484eSchristos #include "event2/util.h" 418585484eSchristos #include "event2/thread.h" 428585484eSchristos #include "util-internal.h" 438585484eSchristos #include "evthread-internal.h" 448585484eSchristos #include "evbuffer-internal.h" 458585484eSchristos #include "iocp-internal.h" 468585484eSchristos #include "mm-internal.h" 478585484eSchristos 488585484eSchristos #include <winsock2.h> 49*eabc0478Schristos #include <winerror.h> 508585484eSchristos #include <windows.h> 518585484eSchristos #include <stdio.h> 528585484eSchristos 538585484eSchristos #define MAX_WSABUFS 16 548585484eSchristos 558585484eSchristos /** An evbuffer that can handle overlapped IO. */ 568585484eSchristos struct evbuffer_overlapped { 578585484eSchristos struct evbuffer buffer; 588585484eSchristos /** The socket that we're doing overlapped IO on. */ 598585484eSchristos evutil_socket_t fd; 608585484eSchristos 618585484eSchristos /** pending I/O type */ 628585484eSchristos unsigned read_in_progress : 1; 638585484eSchristos unsigned write_in_progress : 1; 648585484eSchristos 658585484eSchristos /** The first pinned chain in the buffer. */ 668585484eSchristos struct evbuffer_chain *first_pinned; 678585484eSchristos 688585484eSchristos /** How many chains are pinned; how many of the fields in buffers 698585484eSchristos * are we using. */ 708585484eSchristos int n_buffers; 718585484eSchristos WSABUF buffers[MAX_WSABUFS]; 728585484eSchristos }; 738585484eSchristos 748585484eSchristos /** Given an evbuffer, return the correponding evbuffer structure, or NULL if 758585484eSchristos * the evbuffer isn't overlapped. */ 768585484eSchristos static inline struct evbuffer_overlapped * 778585484eSchristos upcast_evbuffer(struct evbuffer *buf) 788585484eSchristos { 798585484eSchristos if (!buf || !buf->is_overlapped) 808585484eSchristos return NULL; 818585484eSchristos return EVUTIL_UPCAST(buf, struct evbuffer_overlapped, buffer); 828585484eSchristos } 838585484eSchristos 848585484eSchristos /** Unpin all the chains noted as pinned in 'eo'. */ 858585484eSchristos static void 868585484eSchristos pin_release(struct evbuffer_overlapped *eo, unsigned flag) 878585484eSchristos { 888585484eSchristos int i; 898585484eSchristos struct evbuffer_chain *next, *chain = eo->first_pinned; 908585484eSchristos 918585484eSchristos for (i = 0; i < eo->n_buffers; ++i) { 928585484eSchristos EVUTIL_ASSERT(chain); 938585484eSchristos next = chain->next; 948585484eSchristos evbuffer_chain_unpin_(chain, flag); 958585484eSchristos chain = next; 968585484eSchristos } 978585484eSchristos } 988585484eSchristos 998585484eSchristos void 1008585484eSchristos evbuffer_commit_read_(struct evbuffer *evbuf, ev_ssize_t nBytes) 1018585484eSchristos { 1028585484eSchristos struct evbuffer_overlapped *buf = upcast_evbuffer(evbuf); 1038585484eSchristos struct evbuffer_chain **chainp; 1048585484eSchristos size_t remaining, len; 1058585484eSchristos unsigned i; 1068585484eSchristos 1078585484eSchristos EVBUFFER_LOCK(evbuf); 1088585484eSchristos EVUTIL_ASSERT(buf->read_in_progress && !buf->write_in_progress); 1098585484eSchristos EVUTIL_ASSERT(nBytes >= 0); /* XXXX Can this be false? */ 1108585484eSchristos 1118585484eSchristos evbuffer_unfreeze(evbuf, 0); 1128585484eSchristos 1138585484eSchristos chainp = evbuf->last_with_datap; 1148585484eSchristos if (!((*chainp)->flags & EVBUFFER_MEM_PINNED_R)) 1158585484eSchristos chainp = &(*chainp)->next; 1168585484eSchristos remaining = nBytes; 1178585484eSchristos for (i = 0; remaining > 0 && i < (unsigned)buf->n_buffers; ++i) { 1188585484eSchristos EVUTIL_ASSERT(*chainp); 1198585484eSchristos len = buf->buffers[i].len; 1208585484eSchristos if (remaining < len) 1218585484eSchristos len = remaining; 1228585484eSchristos (*chainp)->off += len; 1238585484eSchristos evbuf->last_with_datap = chainp; 1248585484eSchristos remaining -= len; 1258585484eSchristos chainp = &(*chainp)->next; 1268585484eSchristos } 1278585484eSchristos 1288585484eSchristos pin_release(buf, EVBUFFER_MEM_PINNED_R); 1298585484eSchristos 1308585484eSchristos buf->read_in_progress = 0; 1318585484eSchristos 1328585484eSchristos evbuf->total_len += nBytes; 1338585484eSchristos evbuf->n_add_for_cb += nBytes; 1348585484eSchristos 1358585484eSchristos evbuffer_invoke_callbacks_(evbuf); 1368585484eSchristos 1378585484eSchristos evbuffer_decref_and_unlock_(evbuf); 1388585484eSchristos } 1398585484eSchristos 1408585484eSchristos void 1418585484eSchristos evbuffer_commit_write_(struct evbuffer *evbuf, ev_ssize_t nBytes) 1428585484eSchristos { 1438585484eSchristos struct evbuffer_overlapped *buf = upcast_evbuffer(evbuf); 1448585484eSchristos 1458585484eSchristos EVBUFFER_LOCK(evbuf); 1468585484eSchristos EVUTIL_ASSERT(buf->write_in_progress && !buf->read_in_progress); 1478585484eSchristos evbuffer_unfreeze(evbuf, 1); 1488585484eSchristos evbuffer_drain(evbuf, nBytes); 1498585484eSchristos pin_release(buf,EVBUFFER_MEM_PINNED_W); 1508585484eSchristos buf->write_in_progress = 0; 1518585484eSchristos evbuffer_decref_and_unlock_(evbuf); 1528585484eSchristos } 1538585484eSchristos 1548585484eSchristos struct evbuffer * 1558585484eSchristos evbuffer_overlapped_new_(evutil_socket_t fd) 1568585484eSchristos { 1578585484eSchristos struct evbuffer_overlapped *evo; 1588585484eSchristos 1598585484eSchristos evo = mm_calloc(1, sizeof(struct evbuffer_overlapped)); 1608585484eSchristos if (!evo) 1618585484eSchristos return NULL; 1628585484eSchristos 1638585484eSchristos LIST_INIT(&evo->buffer.callbacks); 1648585484eSchristos evo->buffer.refcnt = 1; 1658585484eSchristos evo->buffer.last_with_datap = &evo->buffer.first; 1668585484eSchristos 1678585484eSchristos evo->buffer.is_overlapped = 1; 1688585484eSchristos evo->fd = fd; 1698585484eSchristos 1708585484eSchristos return &evo->buffer; 1718585484eSchristos } 1728585484eSchristos 1738585484eSchristos int 1748585484eSchristos evbuffer_launch_write_(struct evbuffer *buf, ev_ssize_t at_most, 1758585484eSchristos struct event_overlapped *ol) 1768585484eSchristos { 1778585484eSchristos struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); 1788585484eSchristos int r = -1; 1798585484eSchristos int i; 1808585484eSchristos struct evbuffer_chain *chain; 1818585484eSchristos DWORD bytesSent; 1828585484eSchristos 1838585484eSchristos if (!buf) { 1848585484eSchristos /* No buffer, or it isn't overlapped */ 1858585484eSchristos return -1; 1868585484eSchristos } 1878585484eSchristos 1888585484eSchristos EVBUFFER_LOCK(buf); 1898585484eSchristos EVUTIL_ASSERT(!buf_o->read_in_progress); 1908585484eSchristos if (buf->freeze_start || buf_o->write_in_progress) 1918585484eSchristos goto done; 1928585484eSchristos if (!buf->total_len) { 1938585484eSchristos /* Nothing to write */ 1948585484eSchristos r = 0; 1958585484eSchristos goto done; 1968585484eSchristos } else if (at_most < 0 || (size_t)at_most > buf->total_len) { 1978585484eSchristos at_most = buf->total_len; 1988585484eSchristos } 1998585484eSchristos evbuffer_freeze(buf, 1); 2008585484eSchristos 2018585484eSchristos buf_o->first_pinned = NULL; 2028585484eSchristos buf_o->n_buffers = 0; 2038585484eSchristos memset(buf_o->buffers, 0, sizeof(buf_o->buffers)); 2048585484eSchristos 2058585484eSchristos chain = buf_o->first_pinned = buf->first; 2068585484eSchristos 2078585484eSchristos for (i=0; i < MAX_WSABUFS && chain; ++i, chain=chain->next) { 2088585484eSchristos WSABUF *b = &buf_o->buffers[i]; 2098585484eSchristos b->buf = (char*)( chain->buffer + chain->misalign ); 2108585484eSchristos evbuffer_chain_pin_(chain, EVBUFFER_MEM_PINNED_W); 2118585484eSchristos 2128585484eSchristos if ((size_t)at_most > chain->off) { 2138585484eSchristos /* XXXX Cast is safe for now, since win32 has no 2148585484eSchristos mmaped chains. But later, we need to have this 2158585484eSchristos add more WSAbufs if chain->off is greater than 2168585484eSchristos ULONG_MAX */ 2178585484eSchristos b->len = (unsigned long)chain->off; 2188585484eSchristos at_most -= chain->off; 2198585484eSchristos } else { 2208585484eSchristos b->len = (unsigned long)at_most; 2218585484eSchristos ++i; 2228585484eSchristos break; 2238585484eSchristos } 2248585484eSchristos } 2258585484eSchristos 2268585484eSchristos buf_o->n_buffers = i; 2278585484eSchristos evbuffer_incref_(buf); 2288585484eSchristos if (WSASend(buf_o->fd, buf_o->buffers, i, &bytesSent, 0, 2298585484eSchristos &ol->overlapped, NULL)) { 2308585484eSchristos int error = WSAGetLastError(); 2318585484eSchristos if (error != WSA_IO_PENDING) { 2328585484eSchristos /* An actual error. */ 2338585484eSchristos pin_release(buf_o, EVBUFFER_MEM_PINNED_W); 2348585484eSchristos evbuffer_unfreeze(buf, 1); 2358585484eSchristos evbuffer_free(buf); /* decref */ 2368585484eSchristos goto done; 2378585484eSchristos } 2388585484eSchristos } 2398585484eSchristos 2408585484eSchristos buf_o->write_in_progress = 1; 2418585484eSchristos r = 0; 2428585484eSchristos done: 2438585484eSchristos EVBUFFER_UNLOCK(buf); 2448585484eSchristos return r; 2458585484eSchristos } 2468585484eSchristos 2478585484eSchristos int 2488585484eSchristos evbuffer_launch_read_(struct evbuffer *buf, size_t at_most, 2498585484eSchristos struct event_overlapped *ol) 2508585484eSchristos { 2518585484eSchristos struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); 2528585484eSchristos int r = -1, i; 2538585484eSchristos int nvecs; 2548585484eSchristos int npin=0; 2558585484eSchristos struct evbuffer_chain *chain=NULL, **chainp; 2568585484eSchristos DWORD bytesRead; 2578585484eSchristos DWORD flags = 0; 2588585484eSchristos struct evbuffer_iovec vecs[MAX_WSABUFS]; 2598585484eSchristos 2608585484eSchristos if (!buf_o) 2618585484eSchristos return -1; 2628585484eSchristos EVBUFFER_LOCK(buf); 2638585484eSchristos EVUTIL_ASSERT(!buf_o->write_in_progress); 2648585484eSchristos if (buf->freeze_end || buf_o->read_in_progress) 2658585484eSchristos goto done; 2668585484eSchristos 2678585484eSchristos buf_o->first_pinned = NULL; 2688585484eSchristos buf_o->n_buffers = 0; 2698585484eSchristos memset(buf_o->buffers, 0, sizeof(buf_o->buffers)); 2708585484eSchristos 2718585484eSchristos if (evbuffer_expand_fast_(buf, at_most, MAX_WSABUFS) == -1) 2728585484eSchristos goto done; 2738585484eSchristos evbuffer_freeze(buf, 0); 2748585484eSchristos 2758585484eSchristos nvecs = evbuffer_read_setup_vecs_(buf, at_most, 2768585484eSchristos vecs, MAX_WSABUFS, &chainp, 1); 2778585484eSchristos for (i=0;i<nvecs;++i) { 2788585484eSchristos WSABUF_FROM_EVBUFFER_IOV( 2798585484eSchristos &buf_o->buffers[i], 2808585484eSchristos &vecs[i]); 2818585484eSchristos } 2828585484eSchristos 2838585484eSchristos buf_o->n_buffers = nvecs; 2848585484eSchristos buf_o->first_pinned = chain = *chainp; 2858585484eSchristos 2868585484eSchristos npin=0; 2878585484eSchristos for ( ; chain; chain = chain->next) { 2888585484eSchristos evbuffer_chain_pin_(chain, EVBUFFER_MEM_PINNED_R); 2898585484eSchristos ++npin; 2908585484eSchristos } 2918585484eSchristos EVUTIL_ASSERT(npin == nvecs); 2928585484eSchristos 2938585484eSchristos evbuffer_incref_(buf); 2948585484eSchristos if (WSARecv(buf_o->fd, buf_o->buffers, nvecs, &bytesRead, &flags, 2958585484eSchristos &ol->overlapped, NULL)) { 2968585484eSchristos int error = WSAGetLastError(); 2978585484eSchristos if (error != WSA_IO_PENDING) { 2988585484eSchristos /* An actual error. */ 2998585484eSchristos pin_release(buf_o, EVBUFFER_MEM_PINNED_R); 3008585484eSchristos evbuffer_unfreeze(buf, 0); 3018585484eSchristos evbuffer_free(buf); /* decref */ 3028585484eSchristos goto done; 3038585484eSchristos } 3048585484eSchristos } 3058585484eSchristos 3068585484eSchristos buf_o->read_in_progress = 1; 3078585484eSchristos r = 0; 3088585484eSchristos done: 3098585484eSchristos EVBUFFER_UNLOCK(buf); 3108585484eSchristos return r; 3118585484eSchristos } 3128585484eSchristos 3138585484eSchristos evutil_socket_t 3148585484eSchristos evbuffer_overlapped_get_fd_(struct evbuffer *buf) 3158585484eSchristos { 3168585484eSchristos struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); 3178585484eSchristos return buf_o ? buf_o->fd : -1; 3188585484eSchristos } 3198585484eSchristos 3208585484eSchristos void 3218585484eSchristos evbuffer_overlapped_set_fd_(struct evbuffer *buf, evutil_socket_t fd) 3228585484eSchristos { 3238585484eSchristos struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); 3248585484eSchristos EVBUFFER_LOCK(buf); 3258585484eSchristos /* XXX is this right?, should it cancel current I/O operations? */ 3268585484eSchristos if (buf_o) 3278585484eSchristos buf_o->fd = fd; 3288585484eSchristos EVBUFFER_UNLOCK(buf); 3298585484eSchristos } 330