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);
66 static splay_tree_t io_tree = {.compare = (splay_compare_t)io_compare};
67 static splay_tree_t timeout_tree = {.compare = (splay_compare_t)timeout_compare};
69 void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
76 io->event = WSACreateEvent();
77 if (io->event == WSA_INVALID_EVENT)
88 if(!splay_insert_node(&io_tree, &io->node))
93 void io_add_event(io_t *io, io_cb_t cb, void *data, WSAEVENT event) {
95 io_add(io, cb, data, -1, 0);
99 void io_set(io_t *io, int flags) {
100 if (flags == io->flags)
108 FD_SET(io->fd, &readfds);
110 FD_CLR(io->fd, &readfds);
113 FD_SET(io->fd, &writefds);
115 FD_CLR(io->fd, &writefds);
118 if (flags & IO_WRITE)
119 events |= WRITE_EVENTS;
121 events |= READ_EVENTS;
122 if (WSAEventSelect(io->fd, io->event, events) != 0)
127 void io_del(io_t *io) {
133 if (io->fd != -1 && WSACloseEvent(io->event) == FALSE)
138 splay_unlink_node(&io_tree, &io->node);
142 void timeout_add(timeout_t *timeout, timeout_cb_t cb, void *data, struct timeval *tv) {
144 timeout->data = data;
145 timeout->node.data = timeout;
147 timeout_set(timeout, tv);
150 void timeout_set(timeout_t *timeout, struct timeval *tv) {
151 if(timerisset(&timeout->tv))
152 splay_unlink_node(&timeout_tree, &timeout->node);
155 gettimeofday(&now, NULL);
157 timeradd(&now, tv, &timeout->tv);
159 if(!splay_insert_node(&timeout_tree, &timeout->node))
163 void timeout_del(timeout_t *timeout) {
167 splay_unlink_node(&timeout_tree, &timeout->node);
169 timeout->tv = (struct timeval){0, 0};
173 static int signal_compare(const signal_t *a, const signal_t *b) {
174 return a->signum - b->signum;
177 static io_t signalio;
178 static int pipefd[2] = {-1, -1};
179 static splay_tree_t signal_tree = {.compare = (splay_compare_t)signal_compare};
181 static void signal_handler(int signum) {
182 unsigned char num = signum;
183 write(pipefd[1], &num, 1);
186 static void signalio_handler(void *data, int flags) {
187 unsigned char signum;
188 if(read(pipefd[0], &signum, 1) != 1)
191 signal_t *sig = splay_search(&signal_tree, &((signal_t){.signum = signum}));
196 static void pipe_init(void) {
198 io_add(&signalio, signalio_handler, NULL, pipefd[0], IO_READ);
201 void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum) {
207 sig->signum = signum;
208 sig->node.data = sig;
213 signal(sig->signum, signal_handler);
215 if(!splay_insert_node(&signal_tree, &sig->node))
219 void signal_del(signal_t *sig) {
223 signal(sig->signum, SIG_DFL);
225 splay_unlink_node(&signal_tree, &sig->node);
230 static struct timeval * get_time_remaining(struct timeval *diff) {
231 gettimeofday(&now, NULL);
232 struct timeval *tv = NULL;
234 while(timeout_tree.head) {
235 timeout_t *timeout = timeout_tree.head->data;
236 timersub(&timeout->tv, &now, diff);
238 if(diff->tv_sec < 0) {
239 timeout->cb(timeout->data);
240 if(timercmp(&timeout->tv, &now, <))
241 timeout_del(timeout);
251 bool event_loop(void) {
260 struct timeval *tv = get_time_remaining(&diff);
261 memcpy(&readable, &readfds, sizeof readable);
262 memcpy(&writable, &writefds, sizeof writable);
267 io_t *last = io_tree.tail->data;
271 int n = select(fds, &readable, &writable, NULL, tv);
274 if(sockwouldblock(sockerrno))
283 for splay_each(io_t, io, &io_tree) {
284 if(FD_ISSET(io->fd, &writable))
285 io->cb(io->data, IO_WRITE);
286 else if(FD_ISSET(io->fd, &readable))
287 io->cb(io->data, IO_READ);
292 There are scenarios in which the callback will remove another io_t from the tree
293 (e.g. closing a double connection). Since splay_each does not support that, we
294 need to exit the loop now. That's okay, since any remaining events will get picked
295 up by the next select() call.
303 struct timeval *tv = get_time_remaining(&diff);
304 DWORD timeout_ms = tv ? (tv->tv_sec * 1000 + tv->tv_usec / 1000 + 1) : WSA_INFINITE;
312 For some reason, Microsoft decided to make the FD_WRITE event edge-triggered instead of level-triggered,
313 which is the opposite of what select() does. In practice, that means that if a FD_WRITE event triggers,
314 it will never trigger again until a send() returns EWOULDBLOCK. Since the semantics of this event loop
315 is that write events are level-triggered (i.e. they continue firing until the socket is full), we need
316 to emulate these semantics by making sure we fire each IO_WRITE that is still writeable.
318 Note that technically FD_CLOSE has the same problem, but it's okay because user code does not rely on
319 this event being fired again if ignored.
321 io_t* writeable_io = NULL;
322 for splay_each(io_t, io, &io_tree)
323 if (io->flags & IO_WRITE && send(io->fd, NULL, 0, 0) == 0) {
328 writeable_io->cb(writeable_io->data, IO_WRITE);
332 WSAEVENT* events = xmalloc(event_count * sizeof(*events));
333 DWORD event_index = 0;
334 for splay_each(io_t, io, &io_tree) {
335 events[event_index] = io->event;
339 DWORD result = WSAWaitForMultipleEvents(event_count, events, FALSE, timeout_ms, FALSE);
342 if (result >= WSA_WAIT_EVENT_0 && result < WSA_WAIT_EVENT_0 + event_count)
343 event = events[result - WSA_WAIT_EVENT_0];
345 if (result == WSA_WAIT_TIMEOUT)
347 if (result < WSA_WAIT_EVENT_0 || result >= WSA_WAIT_EVENT_0 + event_count)
350 io_t *io = splay_search(&io_tree, &((io_t){.event = event}));
357 WSANETWORKEVENTS network_events;
358 if (WSAEnumNetworkEvents(io->fd, io->event, &network_events) != 0)
360 if (network_events.lNetworkEvents & WRITE_EVENTS)
361 io->cb(io->data, IO_WRITE);
362 if (network_events.lNetworkEvents & READ_EVENTS)
363 io->cb(io->data, IO_READ);
371 void event_exit(void) {