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 return a->event - b->event;
48 static int timeout_compare(const timeout_t *a, const timeout_t *b) {
50 timersub(&a->tv, &b->tv, &diff);
60 if(diff.tv_usec < 0) {
64 if(diff.tv_usec > 0) {
79 static splay_tree_t io_tree = {.compare = (splay_compare_t)io_compare};
80 static splay_tree_t timeout_tree = {.compare = (splay_compare_t)timeout_compare};
82 void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
91 io->event = WSACreateEvent();
93 if(io->event == WSA_INVALID_EVENT) {
106 if(!splay_insert_node(&io_tree, &io->node)) {
112 void io_add_event(io_t *io, io_cb_t cb, void *data, WSAEVENT event) {
114 io_add(io, cb, data, -1, 0);
118 void io_set(io_t *io, int flags) {
119 if(flags == io->flags) {
131 if(flags & IO_READ) {
132 FD_SET(io->fd, &readfds);
134 FD_CLR(io->fd, &readfds);
137 if(flags & IO_WRITE) {
138 FD_SET(io->fd, &writefds);
140 FD_CLR(io->fd, &writefds);
146 if(flags & IO_WRITE) {
147 events |= WRITE_EVENTS;
150 if(flags & IO_READ) {
151 events |= READ_EVENTS;
154 if(WSAEventSelect(io->fd, io->event, events) != 0) {
161 void io_del(io_t *io) {
169 if(io->fd != -1 && WSACloseEvent(io->event) == FALSE) {
176 splay_unlink_node(&io_tree, &io->node);
180 void timeout_add(timeout_t *timeout, timeout_cb_t cb, void *data, struct timeval *tv) {
182 timeout->data = data;
183 timeout->node.data = timeout;
185 timeout_set(timeout, tv);
188 void timeout_set(timeout_t *timeout, struct timeval *tv) {
189 if(timerisset(&timeout->tv)) {
190 splay_unlink_node(&timeout_tree, &timeout->node);
194 gettimeofday(&now, NULL);
197 timeradd(&now, tv, &timeout->tv);
199 if(!splay_insert_node(&timeout_tree, &timeout->node)) {
204 void timeout_del(timeout_t *timeout) {
209 splay_unlink_node(&timeout_tree, &timeout->node);
211 timeout->tv = (struct timeval) {
217 static int signal_compare(const signal_t *a, const signal_t *b) {
218 return a->signum - b->signum;
221 static io_t signalio;
222 static int pipefd[2] = {-1, -1};
223 static splay_tree_t signal_tree = {.compare = (splay_compare_t)signal_compare};
225 static void signal_handler(int signum) {
226 unsigned char num = signum;
227 write(pipefd[1], &num, 1);
230 static void signalio_handler(void *data, int flags) {
231 unsigned char signum;
233 if(read(pipefd[0], &signum, 1) != 1) {
237 signal_t *sig = splay_search(&signal_tree, &((signal_t) {
246 static void pipe_init(void) {
248 io_add(&signalio, signalio_handler, NULL, pipefd[0], IO_READ);
252 void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum) {
259 sig->signum = signum;
260 sig->node.data = sig;
262 if(pipefd[0] == -1) {
266 signal(sig->signum, signal_handler);
268 if(!splay_insert_node(&signal_tree, &sig->node)) {
273 void signal_del(signal_t *sig) {
278 signal(sig->signum, SIG_DFL);
280 splay_unlink_node(&signal_tree, &sig->node);
285 static struct timeval *get_time_remaining(struct timeval *diff) {
286 gettimeofday(&now, NULL);
287 struct timeval *tv = NULL;
289 while(timeout_tree.head) {
290 timeout_t *timeout = timeout_tree.head->data;
291 timersub(&timeout->tv, &now, diff);
293 if(diff->tv_sec < 0) {
294 timeout->cb(timeout->data);
296 if(timercmp(&timeout->tv, &now, <)) {
297 timeout_del(timeout);
308 bool event_loop(void) {
317 struct timeval *tv = get_time_remaining(&diff);
318 memcpy(&readable, &readfds, sizeof(readable));
319 memcpy(&writable, &writefds, sizeof(writable));
324 io_t *last = io_tree.tail->data;
328 int n = select(fds, &readable, &writable, NULL, tv);
331 if(sockwouldblock(sockerrno)) {
342 for splay_each(io_t, io, &io_tree) {
343 if(FD_ISSET(io->fd, &writable)) {
344 io->cb(io->data, IO_WRITE);
345 } else if(FD_ISSET(io->fd, &readable)) {
346 io->cb(io->data, IO_READ);
352 There are scenarios in which the callback will remove another io_t from the tree
353 (e.g. closing a double connection). Since splay_each does not support that, we
354 need to exit the loop now. That's okay, since any remaining events will get picked
355 up by the next select() call.
365 struct timeval *tv = get_time_remaining(&diff);
366 DWORD timeout_ms = tv ? (tv->tv_sec * 1000 + tv->tv_usec / 1000 + 1) : WSA_INFINITE;
374 For some reason, Microsoft decided to make the FD_WRITE event edge-triggered instead of level-triggered,
375 which is the opposite of what select() does. In practice, that means that if a FD_WRITE event triggers,
376 it will never trigger again until a send() returns EWOULDBLOCK. Since the semantics of this event loop
377 is that write events are level-triggered (i.e. they continue firing until the socket is full), we need
378 to emulate these semantics by making sure we fire each IO_WRITE that is still writeable.
380 Note that technically FD_CLOSE has the same problem, but it's okay because user code does not rely on
381 this event being fired again if ignored.
383 io_t *writeable_io = NULL;
385 for splay_each(io_t, io, &io_tree)
386 if(io->flags & IO_WRITE && send(io->fd, NULL, 0, 0) == 0) {
392 writeable_io->cb(writeable_io->data, IO_WRITE);
396 WSAEVENT *events = xmalloc(event_count * sizeof(*events));
397 DWORD event_index = 0;
399 for splay_each(io_t, io, &io_tree) {
400 events[event_index] = io->event;
404 DWORD result = WSAWaitForMultipleEvents(event_count, events, FALSE, timeout_ms, FALSE);
408 if(result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + event_count) {
409 event = events[result - WSA_WAIT_EVENT_0];
414 if(result == WSA_WAIT_TIMEOUT) {
418 if(result < WSA_WAIT_EVENT_0 || result >= WSA_WAIT_EVENT_0 + event_count) {
422 io_t *io = splay_search(&io_tree, &((io_t) {
433 WSANETWORKEVENTS network_events;
435 if(WSAEnumNetworkEvents(io->fd, io->event, &network_events) != 0) {
439 if(network_events.lNetworkEvents & READ_EVENTS) {
440 io->cb(io->data, IO_READ);
444 The fd might be available for write too. However, if we already fired the read callback, that
445 callback might have deleted the io (e.g. through terminate_connection()), so we can't fire the
446 write callback here. Instead, we loop back and let the writable io loop above handle it.
456 void event_exit(void) {