Split event.c into per-API files
[tinc] / src / linux / event.c
1 /*
2     event.c -- epoll support for Linux
3     Copyright (C) 2012-2022 Guus Sliepen <guus@tinc-vpn.org>
4
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.
9
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.
14
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.
18 */
19
20 #include "../system.h"
21
22 #include <sys/epoll.h>
23
24 #include "../event.h"
25 #include "../utils.h"
26 #include "../net.h"
27
28 static bool running = false;
29 static int epollset = 0;
30
31 /* NOTE: 1024 limit is only used on ancient (pre 2.6.27) kernels.
32    Decent kernels will ignore this value making it unlimited.
33    epoll_create1 might be better, but these kernels would not be supported
34    in that case. */
35 static inline void event_init(void) {
36         if(!epollset) {
37                 epollset = epoll_create(1024);
38
39                 if(epollset == -1) {
40                         logger(DEBUG_ALWAYS, LOG_EMERG, "Could not initialize epoll: %s", strerror(errno));
41                         abort();
42                 }
43         }
44 }
45
46 static void event_deinit(void) {
47         if(epollset) {
48                 close(epollset);
49                 epollset = 0;
50         }
51 }
52
53 void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags) {
54         if(io->cb) {
55                 return;
56         }
57
58         io->fd = fd;
59         io->cb = cb;
60         io->data = data;
61         io->node.data = io;
62
63         io_set(io, flags);
64 }
65
66 void io_set(io_t *io, int flags) {
67         event_init();
68
69         if(flags == io->flags) {
70                 return;
71         }
72
73         io->flags = flags;
74
75         if(io->fd == -1) {
76                 return;
77         }
78
79         epoll_ctl(epollset, EPOLL_CTL_DEL, io->fd, NULL);
80
81         struct epoll_event ev = {
82                 .events = 0,
83                 .data.ptr = io,
84         };
85
86         if(flags & IO_READ) {
87                 ev.events |= EPOLLIN;
88         }
89
90         if(flags & IO_WRITE) {
91                 ev.events |= EPOLLOUT;
92         } else if(ev.events == 0) {
93                 io_tree.generation++;
94                 return;
95         }
96
97         if(epoll_ctl(epollset, EPOLL_CTL_ADD, io->fd, &ev) < 0) {
98                 logger(DEBUG_ALWAYS, LOG_EMERG, "epoll_ctl failed: %s", strerror(errno));
99                 abort();
100         }
101 }
102
103 void io_del(io_t *io) {
104         if(io->cb) {
105                 io_set(io, 0);
106                 io->cb = NULL;
107         }
108 }
109
110 bool event_loop(void) {
111         event_init();
112         running = true;
113
114         while(running) {
115                 struct timeval diff;
116                 struct timeval *tv = timeout_execute(&diff);
117
118                 struct epoll_event events[MAX_EVENTS_PER_LOOP];
119                 long timeout = (tv->tv_sec * 1000) + (tv->tv_usec / 1000);
120
121                 if(timeout > INT_MAX) {
122                         timeout = INT_MAX;
123                 }
124
125                 int n = epoll_wait(epollset, events, MAX_EVENTS_PER_LOOP, (int)timeout);
126
127                 if(n < 0) {
128                         if(sockwouldblock(sockerrno)) {
129                                 continue;
130                         } else {
131                                 return false;
132                         }
133                 }
134
135                 if(!n) {
136                         continue;
137                 }
138
139                 unsigned int curgen = io_tree.generation;
140
141                 for(int i = 0; i < n; i++) {
142                         io_t *io = events[i].data.ptr;
143
144                         if(events[i].events & EPOLLOUT && io->flags & IO_WRITE) {
145                                 io->cb(io->data, IO_WRITE);
146                         }
147
148                         if(curgen != io_tree.generation) {
149                                 break;
150                         }
151
152                         if(events[i].events & EPOLLIN && io->flags & IO_READ) {
153                                 io->cb(io->data, IO_READ);
154                         }
155
156                         if(curgen != io_tree.generation) {
157                                 break;
158                         }
159                 }
160
161         }
162
163         event_deinit();
164         return true;
165 }
166
167 void event_exit(void) {
168         running = false;
169 }