1 /* $NetBSD: buffer_iocp.c,v 1.1.1.3 2021/04/07 02:43:13 christos Exp $ */ 2 /* 3 * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /** 29 @file buffer_iocp.c 30 31 This module implements overlapped read and write functions for evbuffer 32 objects on Windows. 33 */ 34 #include "event2/event-config.h" 35 #include <sys/cdefs.h> 36 __RCSID("$NetBSD: buffer_iocp.c,v 1.1.1.3 2021/04/07 02:43:13 christos Exp $"); 37 #include "evconfig-private.h" 38 39 #include "event2/buffer.h" 40 #include "event2/buffer_compat.h" 41 #include "event2/util.h" 42 #include "event2/thread.h" 43 #include "util-internal.h" 44 #include "evthread-internal.h" 45 #include "evbuffer-internal.h" 46 #include "iocp-internal.h" 47 #include "mm-internal.h" 48 49 #include <winsock2.h> 50 #include <winerror.h> 51 #include <windows.h> 52 #include <stdio.h> 53 54 #define MAX_WSABUFS 16 55 56 /** An evbuffer that can handle overlapped IO. */ 57 struct evbuffer_overlapped { 58 struct evbuffer buffer; 59 /** The socket that we're doing overlapped IO on. */ 60 evutil_socket_t fd; 61 62 /** pending I/O type */ 63 unsigned read_in_progress : 1; 64 unsigned write_in_progress : 1; 65 66 /** The first pinned chain in the buffer. */ 67 struct evbuffer_chain *first_pinned; 68 69 /** How many chains are pinned; how many of the fields in buffers 70 * are we using. */ 71 int n_buffers; 72 WSABUF buffers[MAX_WSABUFS]; 73 }; 74 75 /** Given an evbuffer, return the correponding evbuffer structure, or NULL if 76 * the evbuffer isn't overlapped. */ 77 static inline struct evbuffer_overlapped * 78 upcast_evbuffer(struct evbuffer *buf) 79 { 80 if (!buf || !buf->is_overlapped) 81 return NULL; 82 return EVUTIL_UPCAST(buf, struct evbuffer_overlapped, buffer); 83 } 84 85 /** Unpin all the chains noted as pinned in 'eo'. */ 86 static void 87 pin_release(struct evbuffer_overlapped *eo, unsigned flag) 88 { 89 int i; 90 struct evbuffer_chain *next, *chain = eo->first_pinned; 91 92 for (i = 0; i < eo->n_buffers; ++i) { 93 EVUTIL_ASSERT(chain); 94 next = chain->next; 95 evbuffer_chain_unpin_(chain, flag); 96 chain = next; 97 } 98 } 99 100 void 101 evbuffer_commit_read_(struct evbuffer *evbuf, ev_ssize_t nBytes) 102 { 103 struct evbuffer_overlapped *buf = upcast_evbuffer(evbuf); 104 struct evbuffer_chain **chainp; 105 size_t remaining, len; 106 unsigned i; 107 108 EVBUFFER_LOCK(evbuf); 109 EVUTIL_ASSERT(buf->read_in_progress && !buf->write_in_progress); 110 EVUTIL_ASSERT(nBytes >= 0); /* XXXX Can this be false? */ 111 112 evbuffer_unfreeze(evbuf, 0); 113 114 chainp = evbuf->last_with_datap; 115 if (!((*chainp)->flags & EVBUFFER_MEM_PINNED_R)) 116 chainp = &(*chainp)->next; 117 remaining = nBytes; 118 for (i = 0; remaining > 0 && i < (unsigned)buf->n_buffers; ++i) { 119 EVUTIL_ASSERT(*chainp); 120 len = buf->buffers[i].len; 121 if (remaining < len) 122 len = remaining; 123 (*chainp)->off += len; 124 evbuf->last_with_datap = chainp; 125 remaining -= len; 126 chainp = &(*chainp)->next; 127 } 128 129 pin_release(buf, EVBUFFER_MEM_PINNED_R); 130 131 buf->read_in_progress = 0; 132 133 evbuf->total_len += nBytes; 134 evbuf->n_add_for_cb += nBytes; 135 136 evbuffer_invoke_callbacks_(evbuf); 137 138 evbuffer_decref_and_unlock_(evbuf); 139 } 140 141 void 142 evbuffer_commit_write_(struct evbuffer *evbuf, ev_ssize_t nBytes) 143 { 144 struct evbuffer_overlapped *buf = upcast_evbuffer(evbuf); 145 146 EVBUFFER_LOCK(evbuf); 147 EVUTIL_ASSERT(buf->write_in_progress && !buf->read_in_progress); 148 evbuffer_unfreeze(evbuf, 1); 149 evbuffer_drain(evbuf, nBytes); 150 pin_release(buf,EVBUFFER_MEM_PINNED_W); 151 buf->write_in_progress = 0; 152 evbuffer_decref_and_unlock_(evbuf); 153 } 154 155 struct evbuffer * 156 evbuffer_overlapped_new_(evutil_socket_t fd) 157 { 158 struct evbuffer_overlapped *evo; 159 160 evo = mm_calloc(1, sizeof(struct evbuffer_overlapped)); 161 if (!evo) 162 return NULL; 163 164 LIST_INIT(&evo->buffer.callbacks); 165 evo->buffer.refcnt = 1; 166 evo->buffer.last_with_datap = &evo->buffer.first; 167 168 evo->buffer.is_overlapped = 1; 169 evo->fd = fd; 170 171 return &evo->buffer; 172 } 173 174 int 175 evbuffer_launch_write_(struct evbuffer *buf, ev_ssize_t at_most, 176 struct event_overlapped *ol) 177 { 178 struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); 179 int r = -1; 180 int i; 181 struct evbuffer_chain *chain; 182 DWORD bytesSent; 183 184 if (!buf) { 185 /* No buffer, or it isn't overlapped */ 186 return -1; 187 } 188 189 EVBUFFER_LOCK(buf); 190 EVUTIL_ASSERT(!buf_o->read_in_progress); 191 if (buf->freeze_start || buf_o->write_in_progress) 192 goto done; 193 if (!buf->total_len) { 194 /* Nothing to write */ 195 r = 0; 196 goto done; 197 } else if (at_most < 0 || (size_t)at_most > buf->total_len) { 198 at_most = buf->total_len; 199 } 200 evbuffer_freeze(buf, 1); 201 202 buf_o->first_pinned = NULL; 203 buf_o->n_buffers = 0; 204 memset(buf_o->buffers, 0, sizeof(buf_o->buffers)); 205 206 chain = buf_o->first_pinned = buf->first; 207 208 for (i=0; i < MAX_WSABUFS && chain; ++i, chain=chain->next) { 209 WSABUF *b = &buf_o->buffers[i]; 210 b->buf = (char*)( chain->buffer + chain->misalign ); 211 evbuffer_chain_pin_(chain, EVBUFFER_MEM_PINNED_W); 212 213 if ((size_t)at_most > chain->off) { 214 /* XXXX Cast is safe for now, since win32 has no 215 mmaped chains. But later, we need to have this 216 add more WSAbufs if chain->off is greater than 217 ULONG_MAX */ 218 b->len = (unsigned long)chain->off; 219 at_most -= chain->off; 220 } else { 221 b->len = (unsigned long)at_most; 222 ++i; 223 break; 224 } 225 } 226 227 buf_o->n_buffers = i; 228 evbuffer_incref_(buf); 229 if (WSASend(buf_o->fd, buf_o->buffers, i, &bytesSent, 0, 230 &ol->overlapped, NULL)) { 231 int error = WSAGetLastError(); 232 if (error != WSA_IO_PENDING) { 233 /* An actual error. */ 234 pin_release(buf_o, EVBUFFER_MEM_PINNED_W); 235 evbuffer_unfreeze(buf, 1); 236 evbuffer_free(buf); /* decref */ 237 goto done; 238 } 239 } 240 241 buf_o->write_in_progress = 1; 242 r = 0; 243 done: 244 EVBUFFER_UNLOCK(buf); 245 return r; 246 } 247 248 int 249 evbuffer_launch_read_(struct evbuffer *buf, size_t at_most, 250 struct event_overlapped *ol) 251 { 252 struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); 253 int r = -1, i; 254 int nvecs; 255 int npin=0; 256 struct evbuffer_chain *chain=NULL, **chainp; 257 DWORD bytesRead; 258 DWORD flags = 0; 259 struct evbuffer_iovec vecs[MAX_WSABUFS]; 260 261 if (!buf_o) 262 return -1; 263 EVBUFFER_LOCK(buf); 264 EVUTIL_ASSERT(!buf_o->write_in_progress); 265 if (buf->freeze_end || buf_o->read_in_progress) 266 goto done; 267 268 buf_o->first_pinned = NULL; 269 buf_o->n_buffers = 0; 270 memset(buf_o->buffers, 0, sizeof(buf_o->buffers)); 271 272 if (evbuffer_expand_fast_(buf, at_most, MAX_WSABUFS) == -1) 273 goto done; 274 evbuffer_freeze(buf, 0); 275 276 nvecs = evbuffer_read_setup_vecs_(buf, at_most, 277 vecs, MAX_WSABUFS, &chainp, 1); 278 for (i=0;i<nvecs;++i) { 279 WSABUF_FROM_EVBUFFER_IOV( 280 &buf_o->buffers[i], 281 &vecs[i]); 282 } 283 284 buf_o->n_buffers = nvecs; 285 buf_o->first_pinned = chain = *chainp; 286 287 npin=0; 288 for ( ; chain; chain = chain->next) { 289 evbuffer_chain_pin_(chain, EVBUFFER_MEM_PINNED_R); 290 ++npin; 291 } 292 EVUTIL_ASSERT(npin == nvecs); 293 294 evbuffer_incref_(buf); 295 if (WSARecv(buf_o->fd, buf_o->buffers, nvecs, &bytesRead, &flags, 296 &ol->overlapped, NULL)) { 297 int error = WSAGetLastError(); 298 if (error != WSA_IO_PENDING) { 299 /* An actual error. */ 300 pin_release(buf_o, EVBUFFER_MEM_PINNED_R); 301 evbuffer_unfreeze(buf, 0); 302 evbuffer_free(buf); /* decref */ 303 goto done; 304 } 305 } 306 307 buf_o->read_in_progress = 1; 308 r = 0; 309 done: 310 EVBUFFER_UNLOCK(buf); 311 return r; 312 } 313 314 evutil_socket_t 315 evbuffer_overlapped_get_fd_(struct evbuffer *buf) 316 { 317 struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); 318 return buf_o ? buf_o->fd : -1; 319 } 320 321 void 322 evbuffer_overlapped_set_fd_(struct evbuffer *buf, evutil_socket_t fd) 323 { 324 struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf); 325 EVBUFFER_LOCK(buf); 326 /* XXX is this right?, should it cancel current I/O operations? */ 327 if (buf_o) 328 buf_o->fd = fd; 329 EVBUFFER_UNLOCK(buf); 330 } 331