1838d0a81SNick Mathewson /*
2e49e2891SNick Mathewson * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson
3838d0a81SNick Mathewson *
4838d0a81SNick Mathewson * Redistribution and use in source and binary forms, with or without
5838d0a81SNick Mathewson * modification, are permitted provided that the following conditions
6838d0a81SNick Mathewson * are met:
7838d0a81SNick Mathewson * 1. Redistributions of source code must retain the above copyright
8838d0a81SNick Mathewson * notice, this list of conditions and the following disclaimer.
9838d0a81SNick Mathewson * 2. Redistributions in binary form must reproduce the above copyright
10838d0a81SNick Mathewson * notice, this list of conditions and the following disclaimer in the
11838d0a81SNick Mathewson * documentation and/or other materials provided with the distribution.
12838d0a81SNick Mathewson * 3. The name of the author may not be used to endorse or promote products
13838d0a81SNick Mathewson * derived from this software without specific prior written permission.
14838d0a81SNick Mathewson *
15838d0a81SNick Mathewson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16838d0a81SNick Mathewson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17838d0a81SNick Mathewson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18838d0a81SNick Mathewson * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19838d0a81SNick Mathewson * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20838d0a81SNick Mathewson * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21838d0a81SNick Mathewson * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22838d0a81SNick Mathewson * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23838d0a81SNick Mathewson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24838d0a81SNick Mathewson * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25838d0a81SNick Mathewson */
26838d0a81SNick Mathewson
27838d0a81SNick Mathewson /**
28838d0a81SNick Mathewson @file buffer_iocp.c
29838d0a81SNick Mathewson
30838d0a81SNick Mathewson This module implements overlapped read and write functions for evbuffer
31838d0a81SNick Mathewson objects on Windows.
32838d0a81SNick Mathewson */
330915ca0aSKevin Bowling #include "event2/event-config.h"
340915ca0aSKevin Bowling #include "evconfig-private.h"
35ca737ff3SNick Mathewson
36ca737ff3SNick Mathewson #include "event2/buffer.h"
37af8b8222SNick Mathewson #include "event2/buffer_compat.h"
3893d4f884SNick Mathewson #include "event2/util.h"
3993d4f884SNick Mathewson #include "event2/thread.h"
4093d4f884SNick Mathewson #include "util-internal.h"
4193d4f884SNick Mathewson #include "evthread-internal.h"
4293d4f884SNick Mathewson #include "evbuffer-internal.h"
4393d4f884SNick Mathewson #include "iocp-internal.h"
4493d4f884SNick Mathewson #include "mm-internal.h"
45ca737ff3SNick Mathewson
465b5b880bSNick Mathewson #include <winsock2.h>
47*9559349cSyuangongji #include <winerror.h>
48e865eb93SNick Mathewson #include <windows.h>
49e865eb93SNick Mathewson #include <stdio.h>
50e865eb93SNick Mathewson
51ca737ff3SNick Mathewson #define MAX_WSABUFS 16
52ca737ff3SNick Mathewson
53838d0a81SNick Mathewson /** An evbuffer that can handle overlapped IO. */
54ca737ff3SNick Mathewson struct evbuffer_overlapped {
55ca737ff3SNick Mathewson struct evbuffer buffer;
56838d0a81SNick Mathewson /** The socket that we're doing overlapped IO on. */
57ca737ff3SNick Mathewson evutil_socket_t fd;
58ca737ff3SNick Mathewson
59d7d1f1daSNick Mathewson /** pending I/O type */
60d7d1f1daSNick Mathewson unsigned read_in_progress : 1;
61d7d1f1daSNick Mathewson unsigned write_in_progress : 1;
62d7d1f1daSNick Mathewson
63d7d1f1daSNick Mathewson /** The first pinned chain in the buffer. */
64d7d1f1daSNick Mathewson struct evbuffer_chain *first_pinned;
65d7d1f1daSNick Mathewson
66d7d1f1daSNick Mathewson /** How many chains are pinned; how many of the fields in buffers
67d7d1f1daSNick Mathewson * are we using. */
68d7d1f1daSNick Mathewson int n_buffers;
69d7d1f1daSNick Mathewson WSABUF buffers[MAX_WSABUFS];
70ca737ff3SNick Mathewson };
71ca737ff3SNick Mathewson
72838d0a81SNick Mathewson /** Given an evbuffer, return the correponding evbuffer structure, or NULL if
73838d0a81SNick Mathewson * the evbuffer isn't overlapped. */
74ca737ff3SNick Mathewson static inline struct evbuffer_overlapped *
upcast_evbuffer(struct evbuffer * buf)75ca737ff3SNick Mathewson upcast_evbuffer(struct evbuffer *buf)
76ca737ff3SNick Mathewson {
7723121bfbSNick Mathewson if (!buf || !buf->is_overlapped)
78ca737ff3SNick Mathewson return NULL;
79ca737ff3SNick Mathewson return EVUTIL_UPCAST(buf, struct evbuffer_overlapped, buffer);
80ca737ff3SNick Mathewson }
81ca737ff3SNick Mathewson
82838d0a81SNick Mathewson /** Unpin all the chains noted as pinned in 'eo'. */
83ca737ff3SNick Mathewson static void
pin_release(struct evbuffer_overlapped * eo,unsigned flag)84d7d1f1daSNick Mathewson pin_release(struct evbuffer_overlapped *eo, unsigned flag)
85ca737ff3SNick Mathewson {
86ca737ff3SNick Mathewson int i;
8703afa209SChristopher Davis struct evbuffer_chain *next, *chain = eo->first_pinned;
88ca737ff3SNick Mathewson
89d7d1f1daSNick Mathewson for (i = 0; i < eo->n_buffers; ++i) {
902e36dbe1SNick Mathewson EVUTIL_ASSERT(chain);
9103afa209SChristopher Davis next = chain->next;
92cb9da0bfSNick Mathewson evbuffer_chain_unpin_(chain, flag);
9303afa209SChristopher Davis chain = next;
94ca737ff3SNick Mathewson }
95ca737ff3SNick Mathewson }
96ca737ff3SNick Mathewson
97d7d1f1daSNick Mathewson void
evbuffer_commit_read_(struct evbuffer * evbuf,ev_ssize_t nBytes)988ac3c4c2SNick Mathewson evbuffer_commit_read_(struct evbuffer *evbuf, ev_ssize_t nBytes)
99ca737ff3SNick Mathewson {
100d7d1f1daSNick Mathewson struct evbuffer_overlapped *buf = upcast_evbuffer(evbuf);
10103afa209SChristopher Davis struct evbuffer_chain **chainp;
10203afa209SChristopher Davis size_t remaining, len;
10303afa209SChristopher Davis unsigned i;
104ca737ff3SNick Mathewson
10576cd2b70SNick Mathewson EVBUFFER_LOCK(evbuf);
106d7d1f1daSNick Mathewson EVUTIL_ASSERT(buf->read_in_progress && !buf->write_in_progress);
107e2ca403fSNick Mathewson EVUTIL_ASSERT(nBytes >= 0); /* XXXX Can this be false? */
108a84c87d7SNick Mathewson
109ca737ff3SNick Mathewson evbuffer_unfreeze(evbuf, 0);
110ca737ff3SNick Mathewson
11103afa209SChristopher Davis chainp = evbuf->last_with_datap;
11203afa209SChristopher Davis if (!((*chainp)->flags & EVBUFFER_MEM_PINNED_R))
11303afa209SChristopher Davis chainp = &(*chainp)->next;
11403afa209SChristopher Davis remaining = nBytes;
115e06f514dSNick Mathewson for (i = 0; remaining > 0 && i < (unsigned)buf->n_buffers; ++i) {
11603afa209SChristopher Davis EVUTIL_ASSERT(*chainp);
11703afa209SChristopher Davis len = buf->buffers[i].len;
11803afa209SChristopher Davis if (remaining < len)
11903afa209SChristopher Davis len = remaining;
12003afa209SChristopher Davis (*chainp)->off += len;
12103afa209SChristopher Davis evbuf->last_with_datap = chainp;
12203afa209SChristopher Davis remaining -= len;
12303afa209SChristopher Davis chainp = &(*chainp)->next;
124ca737ff3SNick Mathewson }
125ca737ff3SNick Mathewson
126d7d1f1daSNick Mathewson pin_release(buf, EVBUFFER_MEM_PINNED_R);
127d7d1f1daSNick Mathewson
128d7d1f1daSNick Mathewson buf->read_in_progress = 0;
129ca737ff3SNick Mathewson
13003afa209SChristopher Davis evbuf->total_len += nBytes;
1316acfbdd8SNick Mathewson evbuf->n_add_for_cb += nBytes;
1326acfbdd8SNick Mathewson
1338ac3c4c2SNick Mathewson evbuffer_invoke_callbacks_(evbuf);
13403afa209SChristopher Davis
135cb9da0bfSNick Mathewson evbuffer_decref_and_unlock_(evbuf);
136ca737ff3SNick Mathewson }
137ca737ff3SNick Mathewson
138d7d1f1daSNick Mathewson void
evbuffer_commit_write_(struct evbuffer * evbuf,ev_ssize_t nBytes)1398ac3c4c2SNick Mathewson evbuffer_commit_write_(struct evbuffer *evbuf, ev_ssize_t nBytes)
140ca737ff3SNick Mathewson {
141d7d1f1daSNick Mathewson struct evbuffer_overlapped *buf = upcast_evbuffer(evbuf);
142ca737ff3SNick Mathewson
14376cd2b70SNick Mathewson EVBUFFER_LOCK(evbuf);
144d7d1f1daSNick Mathewson EVUTIL_ASSERT(buf->write_in_progress && !buf->read_in_progress);
14593d4f884SNick Mathewson evbuffer_unfreeze(evbuf, 1);
146ca737ff3SNick Mathewson evbuffer_drain(evbuf, nBytes);
147d7d1f1daSNick Mathewson pin_release(buf,EVBUFFER_MEM_PINNED_W);
148d7d1f1daSNick Mathewson buf->write_in_progress = 0;
149cb9da0bfSNick Mathewson evbuffer_decref_and_unlock_(evbuf);
150ca737ff3SNick Mathewson }
151ca737ff3SNick Mathewson
152ca737ff3SNick Mathewson struct evbuffer *
evbuffer_overlapped_new_(evutil_socket_t fd)1538ac3c4c2SNick Mathewson evbuffer_overlapped_new_(evutil_socket_t fd)
154ca737ff3SNick Mathewson {
155ca737ff3SNick Mathewson struct evbuffer_overlapped *evo;
156ca737ff3SNick Mathewson
157ca737ff3SNick Mathewson evo = mm_calloc(1, sizeof(struct evbuffer_overlapped));
15889d5e09eSNick Mathewson if (!evo)
15989d5e09eSNick Mathewson return NULL;
160ca737ff3SNick Mathewson
161d313c293SNick Mathewson LIST_INIT(&evo->buffer.callbacks);
162efc24f7cSNick Mathewson evo->buffer.refcnt = 1;
163a0983b67SNick Mathewson evo->buffer.last_with_datap = &evo->buffer.first;
164ca737ff3SNick Mathewson
165ca737ff3SNick Mathewson evo->buffer.is_overlapped = 1;
166efc24f7cSNick Mathewson evo->fd = fd;
167ca737ff3SNick Mathewson
168ca737ff3SNick Mathewson return &evo->buffer;
169ca737ff3SNick Mathewson }
170ca737ff3SNick Mathewson
171ca737ff3SNick Mathewson int
evbuffer_launch_write_(struct evbuffer * buf,ev_ssize_t at_most,struct event_overlapped * ol)1728ac3c4c2SNick Mathewson evbuffer_launch_write_(struct evbuffer *buf, ev_ssize_t at_most,
173d7d1f1daSNick Mathewson struct event_overlapped *ol)
174ca737ff3SNick Mathewson {
175ca737ff3SNick Mathewson struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
176ca737ff3SNick Mathewson int r = -1;
17793d4f884SNick Mathewson int i;
178ca737ff3SNick Mathewson struct evbuffer_chain *chain;
17993d4f884SNick Mathewson DWORD bytesSent;
180ca737ff3SNick Mathewson
181ca737ff3SNick Mathewson if (!buf) {
182ca737ff3SNick Mathewson /* No buffer, or it isn't overlapped */
183ca737ff3SNick Mathewson return -1;
184ca737ff3SNick Mathewson }
185ca737ff3SNick Mathewson
18676cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
187d7d1f1daSNick Mathewson EVUTIL_ASSERT(!buf_o->read_in_progress);
188ca737ff3SNick Mathewson if (buf->freeze_start || buf_o->write_in_progress)
189ca737ff3SNick Mathewson goto done;
190ca737ff3SNick Mathewson if (!buf->total_len) {
191ca737ff3SNick Mathewson /* Nothing to write */
192ca737ff3SNick Mathewson r = 0;
193ca737ff3SNick Mathewson goto done;
194e865eb93SNick Mathewson } else if (at_most < 0 || (size_t)at_most > buf->total_len) {
195ca737ff3SNick Mathewson at_most = buf->total_len;
196ca737ff3SNick Mathewson }
197ca737ff3SNick Mathewson evbuffer_freeze(buf, 1);
198ca737ff3SNick Mathewson
19903afa209SChristopher Davis buf_o->first_pinned = NULL;
200d7d1f1daSNick Mathewson buf_o->n_buffers = 0;
201d7d1f1daSNick Mathewson memset(buf_o->buffers, 0, sizeof(buf_o->buffers));
202d7d1f1daSNick Mathewson
203d7d1f1daSNick Mathewson chain = buf_o->first_pinned = buf->first;
204ca737ff3SNick Mathewson
205ca737ff3SNick Mathewson for (i=0; i < MAX_WSABUFS && chain; ++i, chain=chain->next) {
206d7d1f1daSNick Mathewson WSABUF *b = &buf_o->buffers[i];
2075b7a3706SNick Mathewson b->buf = (char*)( chain->buffer + chain->misalign );
208cb9da0bfSNick Mathewson evbuffer_chain_pin_(chain, EVBUFFER_MEM_PINNED_W);
209ca737ff3SNick Mathewson
210e865eb93SNick Mathewson if ((size_t)at_most > chain->off) {
2117484df61SNick Mathewson /* XXXX Cast is safe for now, since win32 has no
2127484df61SNick Mathewson mmaped chains. But later, we need to have this
2137484df61SNick Mathewson add more WSAbufs if chain->off is greater than
2147484df61SNick Mathewson ULONG_MAX */
2157484df61SNick Mathewson b->len = (unsigned long)chain->off;
216ca737ff3SNick Mathewson at_most -= chain->off;
217ca737ff3SNick Mathewson } else {
2187484df61SNick Mathewson b->len = (unsigned long)at_most;
219ca737ff3SNick Mathewson ++i;
220ca737ff3SNick Mathewson break;
221ca737ff3SNick Mathewson }
222ca737ff3SNick Mathewson }
223ca737ff3SNick Mathewson
224d7d1f1daSNick Mathewson buf_o->n_buffers = i;
225cb9da0bfSNick Mathewson evbuffer_incref_(buf);
226d7d1f1daSNick Mathewson if (WSASend(buf_o->fd, buf_o->buffers, i, &bytesSent, 0,
227d7d1f1daSNick Mathewson &ol->overlapped, NULL)) {
228ca737ff3SNick Mathewson int error = WSAGetLastError();
229ca737ff3SNick Mathewson if (error != WSA_IO_PENDING) {
230ca737ff3SNick Mathewson /* An actual error. */
231d7d1f1daSNick Mathewson pin_release(buf_o, EVBUFFER_MEM_PINNED_W);
232ca737ff3SNick Mathewson evbuffer_unfreeze(buf, 1);
233ca737ff3SNick Mathewson evbuffer_free(buf); /* decref */
234ca737ff3SNick Mathewson goto done;
235ca737ff3SNick Mathewson }
236ca737ff3SNick Mathewson }
237ca737ff3SNick Mathewson
238ca737ff3SNick Mathewson buf_o->write_in_progress = 1;
239ca737ff3SNick Mathewson r = 0;
240ca737ff3SNick Mathewson done:
24176cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
242ca737ff3SNick Mathewson return r;
243ca737ff3SNick Mathewson }
244ca737ff3SNick Mathewson
245ca737ff3SNick Mathewson int
evbuffer_launch_read_(struct evbuffer * buf,size_t at_most,struct event_overlapped * ol)2468ac3c4c2SNick Mathewson evbuffer_launch_read_(struct evbuffer *buf, size_t at_most,
247d7d1f1daSNick Mathewson struct event_overlapped *ol)
248ca737ff3SNick Mathewson {
249ca737ff3SNick Mathewson struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
25023243b8aSNick Mathewson int r = -1, i;
251ca737ff3SNick Mathewson int nvecs;
252efc24f7cSNick Mathewson int npin=0;
253b7442f8eSNick Mathewson struct evbuffer_chain *chain=NULL, **chainp;
25493d4f884SNick Mathewson DWORD bytesRead;
25593d4f884SNick Mathewson DWORD flags = 0;
2567a844735SNick Mathewson struct evbuffer_iovec vecs[MAX_WSABUFS];
257ca737ff3SNick Mathewson
258efc24f7cSNick Mathewson if (!buf_o)
259ca737ff3SNick Mathewson return -1;
26076cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
261d7d1f1daSNick Mathewson EVUTIL_ASSERT(!buf_o->write_in_progress);
262ca737ff3SNick Mathewson if (buf->freeze_end || buf_o->read_in_progress)
263ca737ff3SNick Mathewson goto done;
264ca737ff3SNick Mathewson
26503afa209SChristopher Davis buf_o->first_pinned = NULL;
266d7d1f1daSNick Mathewson buf_o->n_buffers = 0;
267d7d1f1daSNick Mathewson memset(buf_o->buffers, 0, sizeof(buf_o->buffers));
268d7d1f1daSNick Mathewson
269cb9da0bfSNick Mathewson if (evbuffer_expand_fast_(buf, at_most, MAX_WSABUFS) == -1)
270ca737ff3SNick Mathewson goto done;
271ca737ff3SNick Mathewson evbuffer_freeze(buf, 0);
272ca737ff3SNick Mathewson
273cb9da0bfSNick Mathewson nvecs = evbuffer_read_setup_vecs_(buf, at_most,
27403afa209SChristopher Davis vecs, MAX_WSABUFS, &chainp, 1);
27523243b8aSNick Mathewson for (i=0;i<nvecs;++i) {
2768997f234SNick Mathewson WSABUF_FROM_EVBUFFER_IOV(
277d7d1f1daSNick Mathewson &buf_o->buffers[i],
27823243b8aSNick Mathewson &vecs[i]);
27923243b8aSNick Mathewson }
28023243b8aSNick Mathewson
281d7d1f1daSNick Mathewson buf_o->n_buffers = nvecs;
282b7442f8eSNick Mathewson buf_o->first_pinned = chain = *chainp;
28303afa209SChristopher Davis
284efc24f7cSNick Mathewson npin=0;
285efc24f7cSNick Mathewson for ( ; chain; chain = chain->next) {
286cb9da0bfSNick Mathewson evbuffer_chain_pin_(chain, EVBUFFER_MEM_PINNED_R);
287efc24f7cSNick Mathewson ++npin;
288efc24f7cSNick Mathewson }
2892e36dbe1SNick Mathewson EVUTIL_ASSERT(npin == nvecs);
290ca737ff3SNick Mathewson
291cb9da0bfSNick Mathewson evbuffer_incref_(buf);
292d7d1f1daSNick Mathewson if (WSARecv(buf_o->fd, buf_o->buffers, nvecs, &bytesRead, &flags,
293d7d1f1daSNick Mathewson &ol->overlapped, NULL)) {
294ca737ff3SNick Mathewson int error = WSAGetLastError();
295ca737ff3SNick Mathewson if (error != WSA_IO_PENDING) {
296ca737ff3SNick Mathewson /* An actual error. */
297d7d1f1daSNick Mathewson pin_release(buf_o, EVBUFFER_MEM_PINNED_R);
298ca737ff3SNick Mathewson evbuffer_unfreeze(buf, 0);
299ca737ff3SNick Mathewson evbuffer_free(buf); /* decref */
300ca737ff3SNick Mathewson goto done;
301ca737ff3SNick Mathewson }
302ca737ff3SNick Mathewson }
303ca737ff3SNick Mathewson
304ca737ff3SNick Mathewson buf_o->read_in_progress = 1;
305ca737ff3SNick Mathewson r = 0;
306ca737ff3SNick Mathewson done:
30776cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
308ca737ff3SNick Mathewson return r;
309ca737ff3SNick Mathewson }
310ca737ff3SNick Mathewson
31131d89f27SNick Mathewson evutil_socket_t
evbuffer_overlapped_get_fd_(struct evbuffer * buf)312cb9da0bfSNick Mathewson evbuffer_overlapped_get_fd_(struct evbuffer *buf)
31331d89f27SNick Mathewson {
31431d89f27SNick Mathewson struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
31531d89f27SNick Mathewson return buf_o ? buf_o->fd : -1;
31631d89f27SNick Mathewson }
31786db1c85SNick Mathewson
31886db1c85SNick Mathewson void
evbuffer_overlapped_set_fd_(struct evbuffer * buf,evutil_socket_t fd)319cb9da0bfSNick Mathewson evbuffer_overlapped_set_fd_(struct evbuffer *buf, evutil_socket_t fd)
32086db1c85SNick Mathewson {
32186db1c85SNick Mathewson struct evbuffer_overlapped *buf_o = upcast_evbuffer(buf);
32276cd2b70SNick Mathewson EVBUFFER_LOCK(buf);
32386db1c85SNick Mathewson /* XXX is this right?, should it cancel current I/O operations? */
32486db1c85SNick Mathewson if (buf_o)
32586db1c85SNick Mathewson buf_o->fd = fd;
32676cd2b70SNick Mathewson EVBUFFER_UNLOCK(buf);
32786db1c85SNick Mathewson }
328