2 event.c -- kqueue support for the BSD family
3 Copyright (C) 2012-2022 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.
20 #include "../system.h"
22 #include <sys/event.h>
28 static bool running = false;
31 static inline void event_init(void) {
36 logger(DEBUG_ALWAYS, LOG_EMERG, "Could not initialize kqueue: %s", strerror(errno));
42 static void event_deinit(void) {
49 void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
62 void io_set(io_t *io, int flags) {
65 if(flags == io->flags) {
75 const struct kevent change[] = {
78 .filter = EVFILT_READ,
79 .flags = EV_RECEIPT | (flags & IO_READ ? EV_ADD : EV_DELETE),
84 .filter = EVFILT_WRITE,
85 .flags = EV_RECEIPT | (flags & IO_WRITE ? EV_ADD : EV_DELETE),
89 struct kevent result[2];
91 if(kevent(kq, change, 2, result, 2, NULL) < 0) {
92 logger(DEBUG_ALWAYS, LOG_EMERG, "kevent failed: %s", strerror(errno));
96 int rerr = (int)result[0].data;
97 int werr = (int)result[1].data;
99 if((rerr && rerr != ENOENT) || (werr && werr != ENOENT)) {
100 logger(DEBUG_ALWAYS, LOG_EMERG, "kevent errors: %s, %s", strerror(rerr), strerror(werr));
105 io_tree.generation++;
109 void io_del(io_t *io) {
116 bool event_loop(void) {
122 struct timeval *tv = timeout_execute(&diff);
123 struct kevent events[MAX_EVENTS_PER_LOOP];
125 const struct timespec ts = {
126 .tv_sec = tv->tv_sec,
127 .tv_nsec = tv->tv_usec * 1000,
130 int n = kevent(kq, NULL, 0, events, MAX_EVENTS_PER_LOOP, &ts);
133 if(sockwouldblock(sockerrno)) {
144 unsigned int curgen = io_tree.generation;
146 for(int i = 0; i < n; i++) {
147 const struct kevent *evt = &events[i];
148 const io_t *io = evt->udata;
150 if(evt->filter == EVFILT_WRITE) {
151 io->cb(io->data, IO_WRITE);
152 } else if(evt->filter == EVFILT_READ) {
153 io->cb(io->data, IO_READ);
158 if(curgen != io_tree.generation) {
168 void event_exit(void) {