2 event.c -- I/O, timeout and signal event handling
3 Copyright (C) 2012-2013 Guus Sliepen <guus@tinc-vpn.org>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 static fd_set readfds;
32 static fd_set writefds;
34 static const long READ_EVENTS = FD_READ | FD_ACCEPT | FD_CLOSE;
35 static const long WRITE_EVENTS = FD_WRITE | FD_CONNECT;
36 static DWORD event_count = 0;
40 static int io_compare(const io_t *a, const io_t *b) {
44 if(a->event < b->event) {
47 if(a->event > b->event) {
54 static int timeout_compare(const timeout_t *a, const timeout_t *b) {
56 timersub(&a->tv, &b->tv, &diff);
66 if(diff.tv_usec < 0) {
70 if(diff.tv_usec > 0) {
85 static splay_tree_t io_tree = {.compare = (splay_compare_t)io_compare};
86 static splay_tree_t timeout_tree = {.compare = (splay_compare_t)timeout_compare};
88 void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
97 io->event = WSACreateEvent();
99 if(io->event == WSA_INVALID_EVENT) {
112 if(!splay_insert_node(&io_tree, &io->node)) {
118 void io_add_event(io_t *io, io_cb_t cb, void *data, WSAEVENT event) {
120 io_add(io, cb, data, -1, 0);
124 void io_set(io_t *io, int flags) {
125 if(flags == io->flags) {
137 if(flags & IO_READ) {
138 FD_SET(io->fd, &readfds);
140 FD_CLR(io->fd, &readfds);
143 if(flags & IO_WRITE) {
144 FD_SET(io->fd, &writefds);
146 FD_CLR(io->fd, &writefds);
152 if(flags & IO_WRITE) {
153 events |= WRITE_EVENTS;
156 if(flags & IO_READ) {
157 events |= READ_EVENTS;
160 if(WSAEventSelect(io->fd, io->event, events) != 0) {
167 void io_del(io_t *io) {
175 if(io->fd != -1 && WSACloseEvent(io->event) == FALSE) {
182 splay_unlink_node(&io_tree, &io->node);
186 void timeout_add(timeout_t *timeout, timeout_cb_t cb, void *data, struct timeval *tv) {
188 timeout->data = data;
189 timeout->node.data = timeout;
191 timeout_set(timeout, tv);
194 void timeout_set(timeout_t *timeout, struct timeval *tv) {
195 if(timerisset(&timeout->tv)) {
196 splay_unlink_node(&timeout_tree, &timeout->node);
200 gettimeofday(&now, NULL);
203 timeradd(&now, tv, &timeout->tv);
205 if(!splay_insert_node(&timeout_tree, &timeout->node)) {
210 void timeout_del(timeout_t *timeout) {
215 splay_unlink_node(&timeout_tree, &timeout->node);
217 timeout->tv = (struct timeval) {
223 static int signal_compare(const signal_t *a, const signal_t *b) {
224 return a->signum - b->signum;
227 static io_t signalio;
228 static int pipefd[2] = {-1, -1};
229 static splay_tree_t signal_tree = {.compare = (splay_compare_t)signal_compare};
231 static void signal_handler(int signum) {
232 unsigned char num = signum;
233 write(pipefd[1], &num, 1);
236 static void signalio_handler(void *data, int flags) {
237 unsigned char signum;
239 if(read(pipefd[0], &signum, 1) != 1) {
243 signal_t *sig = splay_search(&signal_tree, &((signal_t) {
252 static void pipe_init(void) {
254 io_add(&signalio, signalio_handler, NULL, pipefd[0], IO_READ);
258 void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum) {
265 sig->signum = signum;
266 sig->node.data = sig;
268 if(pipefd[0] == -1) {
272 signal(sig->signum, signal_handler);
274 if(!splay_insert_node(&signal_tree, &sig->node)) {
279 void signal_del(signal_t *sig) {
284 signal(sig->signum, SIG_DFL);
286 splay_unlink_node(&signal_tree, &sig->node);
291 static struct timeval *get_time_remaining(struct timeval *diff) {
292 gettimeofday(&now, NULL);
293 struct timeval *tv = NULL;
295 while(timeout_tree.head) {
296 timeout_t *timeout = timeout_tree.head->data;
297 timersub(&timeout->tv, &now, diff);
299 if(diff->tv_sec < 0) {
300 timeout->cb(timeout->data);
302 if(timercmp(&timeout->tv, &now, <)) {
303 timeout_del(timeout);
314 bool event_loop(void) {
323 struct timeval *tv = get_time_remaining(&diff);
324 memcpy(&readable, &readfds, sizeof(readable));
325 memcpy(&writable, &writefds, sizeof(writable));
330 io_t *last = io_tree.tail->data;
334 int n = select(fds, &readable, &writable, NULL, tv);
337 if(sockwouldblock(sockerrno)) {
348 for splay_each(io_t, io, &io_tree) {
349 if(FD_ISSET(io->fd, &writable)) {
350 io->cb(io->data, IO_WRITE);
351 } else if(FD_ISSET(io->fd, &readable)) {
352 io->cb(io->data, IO_READ);
358 There are scenarios in which the callback will remove another io_t from the tree
359 (e.g. closing a double connection). Since splay_each does not support that, we
360 need to exit the loop now. That's okay, since any remaining events will get picked
361 up by the next select() call.
371 struct timeval *tv = get_time_remaining(&diff);
372 DWORD timeout_ms = tv ? (tv->tv_sec * 1000 + tv->tv_usec / 1000 + 1) : WSA_INFINITE;
380 For some reason, Microsoft decided to make the FD_WRITE event edge-triggered instead of level-triggered,
381 which is the opposite of what select() does. In practice, that means that if a FD_WRITE event triggers,
382 it will never trigger again until a send() returns EWOULDBLOCK. Since the semantics of this event loop
383 is that write events are level-triggered (i.e. they continue firing until the socket is full), we need
384 to emulate these semantics by making sure we fire each IO_WRITE that is still writeable.
386 Note that technically FD_CLOSE has the same problem, but it's okay because user code does not rely on
387 this event being fired again if ignored.
389 io_t *writeable_io = NULL;
391 for splay_each(io_t, io, &io_tree)
392 if(io->flags & IO_WRITE && send(io->fd, NULL, 0, 0) == 0) {
398 writeable_io->cb(writeable_io->data, IO_WRITE);
402 WSAEVENT *events = xmalloc(event_count * sizeof(*events));
403 DWORD event_index = 0;
405 for splay_each(io_t, io, &io_tree) {
406 events[event_index] = io->event;
410 DWORD result = WSAWaitForMultipleEvents(event_count, events, FALSE, timeout_ms, FALSE);
414 if(result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + event_count) {
415 event = events[result - WSA_WAIT_EVENT_0];
420 if(result == WSA_WAIT_TIMEOUT) {
424 if(result < WSA_WAIT_EVENT_0 || result >= WSA_WAIT_EVENT_0 + event_count) {
428 io_t *io = splay_search(&io_tree, &((io_t) {
439 WSANETWORKEVENTS network_events;
441 if(WSAEnumNetworkEvents(io->fd, io->event, &network_events) != 0) {
445 if(network_events.lNetworkEvents & READ_EVENTS) {
446 io->cb(io->data, IO_READ);
450 The fd might be available for write too. However, if we already fired the read callback, that
451 callback might have deleted the io (e.g. through terminate_connection()), so we can't fire the
452 write callback here. Instead, we loop back and let the writable io loop above handle it.
462 void event_exit(void) {