summaryrefslogtreecommitdiff
path: root/src/ot_event.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ot_event.c')
-rw-r--r--src/ot_event.c357
1 files changed, 357 insertions, 0 deletions
diff --git a/src/ot_event.c b/src/ot_event.c
new file mode 100644
index 0000000..6062f70
--- /dev/null
+++ b/src/ot_event.c
@@ -0,0 +1,357 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever. */
3
4/* System */
5#include <errno.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10/* Opentracker */
11#include "ot_event.h"
12
13/* One caller waits, so the scratch the mechanism needs is kept here rather
14 than allocated per call. */
15
16/* poll works everywhere and is what the BSDs get until kqueue is written.
17 Defining OT_EVENT_FORCE_POLL picks it on a platform that would otherwise
18 use something better, which is how both are exercised by the tests. */
19#if defined(OT_EVENT_FORCE_POLL)
20#define OT_EVENT_POLL
21#elif defined(OT_EVENT_FORCE_KQUEUE)
22#define OT_EVENT_KQUEUE
23#elif defined(__linux__)
24#define OT_EVENT_EPOLL
25#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__)
26#define OT_EVENT_KQUEUE
27#else
28#define OT_EVENT_POLL
29#endif
30
31#ifdef OT_EVENT_EPOLL
32/* ------------------------------------------------------------- epoll */
33
34#include <sys/epoll.h>
35
36static int g_epoll = -1;
37static struct epoll_event *g_ready;
38static int g_ready_space;
39
40const char *event_mechanism(void) { return "epoll"; }
41/* The set lives in the kernel, and epoll_ctl during another thread's
42 epoll_wait is what it is for */
43int event_can_share(void) { return 1; }
44
45int event_init(void) {
46 g_ready = NULL;
47 g_ready_space = 0;
48 return (g_epoll = epoll_create1(EPOLL_CLOEXEC)) == -1 ? -1 : 0;
49}
50
51void event_deinit(void) {
52 if (g_epoll != -1)
53 close(g_epoll);
54 free(g_ready);
55 g_epoll = -1;
56 g_ready = NULL;
57 g_ready_space = 0;
58}
59
60static int event_ctl(int op, int fd, int what, uint32_t generation) {
61 struct epoll_event ev;
62
63 memset(&ev, 0, sizeof(ev));
64 ev.events = (what & EVENT_IN ? (uint32_t)EPOLLIN : 0) | (what & EVENT_OUT ? (uint32_t)EPOLLOUT : 0);
65 /* The generation rides along inside the kernel, so an event says which
66 connection it belongs to without anybody looking it up */
67 ev.data.u64 = ((uint64_t)generation << 32) | (uint32_t)fd;
68 return epoll_ctl(g_epoll, op, fd, &ev);
69}
70
71int event_add(int fd, int what, uint32_t generation) { return event_ctl(EPOLL_CTL_ADD, fd, what, generation); }
72int event_modify(int fd, int what, uint32_t generation) { return event_ctl(EPOLL_CTL_MOD, fd, what, generation); }
73int event_del(int fd) { return epoll_ctl(g_epoll, EPOLL_CTL_DEL, fd, NULL); }
74
75/* A descriptor leaves the interest list when the last reference to its open
76 file description is closed, and the tracker never duplicates one */
77int event_close(int fd) { return close(fd); }
78
79int event_wait(ot_event *out, int max, int timeout_ms) {
80 int count, i;
81
82 if (max > g_ready_space) {
83 struct epoll_event *grown = realloc(g_ready, (size_t)max * sizeof(struct epoll_event));
84
85 if (!grown)
86 return -1;
87 g_ready = grown;
88 g_ready_space = max;
89 }
90
91 /* An interrupted wait is reported as no events rather than retried here,
92 so that the caller gets to look at whatever the signal was about */
93 if ((count = epoll_wait(g_epoll, g_ready, max, timeout_ms)) == -1)
94 return errno == EINTR ? 0 : -1;
95 for (i = 0; i < count; ++i) {
96 uint32_t events = g_ready[i].events;
97
98 out[i].fd = (int)(uint32_t)g_ready[i].data.u64;
99 out[i].generation = (uint32_t)(g_ready[i].data.u64 >> 32);
100 /* A hangup or an error is reported as both, so whichever side the caller
101 was waiting on gets to notice */
102 out[i].what = (events & (EPOLLIN | EPOLLHUP | EPOLLERR) ? EVENT_IN : 0) |
103 (events & (EPOLLOUT | EPOLLHUP | EPOLLERR) ? EVENT_OUT : 0);
104 }
105 return count;
106}
107
108#endif /* OT_EVENT_EPOLL */
109
110#ifdef OT_EVENT_KQUEUE
111/* ------------------------------------------------------------ kqueue */
112
113#include <sys/event.h>
114#include <sys/time.h>
115#include <sys/types.h>
116
117static int g_kqueue = -1;
118static struct kevent *g_ready;
119static int g_ready_space;
120
121const char *event_mechanism(void) { return "kqueue"; }
122/* The set lives in the kernel, and kevent may be called from any thread */
123int event_can_share(void) { return 1; }
124
125int event_init(void) {
126 g_ready = NULL;
127 g_ready_space = 0;
128 return (g_kqueue = kqueue()) == -1 ? -1 : 0;
129}
130
131void event_deinit(void) {
132 if (g_kqueue != -1)
133 close(g_kqueue);
134 free(g_ready);
135 g_kqueue = -1;
136 g_ready = NULL;
137 g_ready_space = 0;
138}
139
140/* kqueue keeps one registration per filter, so reading and writing are two
141 entries. A change of interest is an add of the one wanted and a delete
142 of the one not. The generation travels in udata, which the kernel passes
143 back untouched. */
144static int event_change(int fd, int what, uint32_t generation, int deleting) {
145 struct kevent change[2], result[2];
146 void *udata = (void *)(uintptr_t)(((uint64_t)generation << 32) | (uint32_t)fd);
147 int succeeded = 0, count, i;
148
149 EV_SET(&change[0], (uintptr_t)fd, EVFILT_READ,
150 EV_RECEIPT | ((deleting || !(what & EVENT_IN)) ? EV_DELETE : EV_ADD), 0, 0, udata);
151 EV_SET(&change[1], (uintptr_t)fd, EVFILT_WRITE,
152 EV_RECEIPT | ((deleting || !(what & EVENT_OUT)) ? EV_DELETE : EV_ADD), 0, 0, udata);
153
154 if ((count = kevent(g_kqueue, change, 2, result, 2, NULL)) == -1)
155 return -1;
156
157 for (i = 0; i < count && i < 2; ++i) {
158 if (!result[i].data) {
159 ++succeeded;
160 continue;
161 }
162 /* Dropping interest we never registered is not a failure */
163 if ((change[i].flags & EV_DELETE) && result[i].data == ENOENT)
164 continue;
165 errno = (int)result[i].data;
166 return -1;
167 }
168
169 /* Nothing at all was registered or removed, so the descriptor is unknown */
170 if (!succeeded) {
171 errno = ENOENT;
172 return -1;
173 }
174 return 0;
175}
176
177int event_add(int fd, int what, uint32_t generation) { return what ? event_change(fd, what, generation, 0) : 0; }
178int event_modify(int fd, int what, uint32_t generation) { return event_change(fd, what, generation, 0); }
179int event_del(int fd) { return event_change(fd, 0, 0, 1); }
180
181/* Closing a descriptor removes every kevent that references it */
182int event_close(int fd) { return close(fd); }
183
184int event_wait(ot_event *out, int max, int timeout_ms) {
185 struct timespec timeout;
186 struct timespec *deadline = NULL;
187 int count, i;
188
189 if (max > g_ready_space) {
190 struct kevent *grown = realloc(g_ready, (size_t)max * sizeof(struct kevent));
191
192 if (!grown)
193 return -1;
194 g_ready = grown;
195 g_ready_space = max;
196 }
197
198 if (timeout_ms >= 0) {
199 timeout.tv_sec = timeout_ms / 1000;
200 timeout.tv_nsec = (long)(timeout_ms % 1000) * 1000000;
201 deadline = &timeout;
202 }
203
204 /* An interrupted wait is reported as no events rather than retried here,
205 so that the caller gets to look at whatever the signal was about */
206 if ((count = kevent(g_kqueue, NULL, 0, g_ready, max, deadline)) == -1)
207 return errno == EINTR ? 0 : -1;
208 for (i = 0; i < count; ++i) {
209 uint64_t udata = (uint64_t)(uintptr_t)g_ready[i].udata;
210 int eof = (g_ready[i].flags & (EV_EOF | EV_ERROR)) != 0;
211
212 out[i].fd = (int)(uint32_t)udata;
213 out[i].generation = (uint32_t)(udata >> 32);
214 out[i].what = ((g_ready[i].filter == EVFILT_READ || eof) ? EVENT_IN : 0) |
215 ((g_ready[i].filter == EVFILT_WRITE || eof) ? EVENT_OUT : 0);
216 }
217 return count;
218}
219
220#endif /* OT_EVENT_KQUEUE */
221
222#ifdef OT_EVENT_POLL
223/* -------------------------------------------------------------- poll */
224
225#include <poll.h>
226
227/* poll wants its descriptors packed, so the registered ones live in one
228 array and a second maps a descriptor number to its place in it. */
229static struct pollfd *g_pfds;
230static uint32_t *g_generation;
231static int *g_where;
232static int g_count, g_space, g_where_space;
233
234const char *event_mechanism(void) { return "poll"; }
235/* The arrays below are ours and nothing guards them */
236int event_can_share(void) { return 0; }
237
238int event_init(void) {
239 g_pfds = NULL, g_generation = NULL, g_where = NULL;
240 g_count = g_space = g_where_space = 0;
241 return 0;
242}
243
244void event_deinit(void) {
245 free(g_pfds), free(g_generation), free(g_where);
246 g_pfds = NULL, g_generation = NULL, g_where = NULL;
247 g_count = g_space = g_where_space = 0;
248}
249
250static short event_to_poll(int what) {
251 return (short)((what & EVENT_IN ? POLLIN : 0) | (what & EVENT_OUT ? POLLOUT : 0));
252}
253
254static int event_room_for(int fd) {
255 if (fd >= g_where_space) {
256 int space = g_where_space ? g_where_space : 64;
257 int *grown;
258
259 while (space <= fd)
260 space *= 2;
261 if (!(grown = realloc(g_where, (size_t)space * sizeof(int))))
262 return -1;
263 while (g_where_space < space)
264 grown[g_where_space++] = -1;
265 g_where = grown;
266 }
267
268 if (g_count == g_space) {
269 int space = g_space ? g_space * 2 : 64;
270 struct pollfd *pfds = realloc(g_pfds, (size_t)space * sizeof(struct pollfd));
271 uint32_t *gens;
272
273 if (!pfds)
274 return -1;
275 g_pfds = pfds;
276 if (!(gens = realloc(g_generation, (size_t)space * sizeof(uint32_t))))
277 return -1;
278 g_generation = gens;
279 g_space = space;
280 }
281 return 0;
282}
283
284int event_add(int fd, int what, uint32_t generation) {
285 if (fd < 0 || event_room_for(fd) == -1)
286 return -1;
287 if (g_where[fd] != -1) {
288 errno = EEXIST;
289 return -1;
290 }
291
292 g_pfds[g_count].fd = fd;
293 g_pfds[g_count].events = event_to_poll(what);
294 g_pfds[g_count].revents = 0;
295 g_generation[g_count] = generation;
296 g_where[fd] = g_count++;
297 return 0;
298}
299
300int event_modify(int fd, int what, uint32_t generation) {
301 if (fd < 0 || fd >= g_where_space || g_where[fd] == -1) {
302 errno = ENOENT;
303 return -1;
304 }
305 g_pfds[g_where[fd]].events = event_to_poll(what);
306 g_generation[g_where[fd]] = generation;
307 return 0;
308}
309
310int event_del(int fd) {
311 int at;
312
313 if (fd < 0 || fd >= g_where_space || (at = g_where[fd]) == -1) {
314 errno = ENOENT;
315 return -1;
316 }
317
318 /* The last one moves into the gap, so the array stays packed */
319 g_pfds[at] = g_pfds[--g_count];
320 g_generation[at] = g_generation[g_count];
321 g_where[g_pfds[at].fd] = at;
322 g_where[fd] = -1;
323 return 0;
324}
325
326/* Nothing here lives in the kernel, so the entry has to come out by hand
327 before the descriptor goes, or poll reports POLLNVAL for it forever */
328int event_close(int fd) {
329 event_del(fd);
330 return close(fd);
331}
332
333int event_wait(ot_event *out, int max, int timeout_ms) {
334 int ready, found = 0, i;
335
336 /* An interrupted wait is reported as no events rather than retried here,
337 so that the caller gets to look at whatever the signal was about */
338 if ((ready = poll(g_pfds, (nfds_t)g_count, timeout_ms)) == -1)
339 return errno == EINTR ? 0 : -1;
340 if (ready <= 0)
341 return ready;
342
343 for (i = 0; i < g_count && found < max; ++i) {
344 short revents = g_pfds[i].revents;
345
346 if (!revents)
347 continue;
348 out[found].fd = g_pfds[i].fd;
349 out[found].generation = g_generation[i];
350 out[found].what = (revents & (POLLIN | POLLHUP | POLLERR | POLLNVAL) ? EVENT_IN : 0) |
351 (revents & (POLLOUT | POLLHUP | POLLERR | POLLNVAL) ? EVENT_OUT : 0);
352 ++found;
353 }
354 return found;
355}
356
357#endif /* OT_EVENT_POLL */