libuev 2.4.1
uev.c
Go to the documentation of this file.
1/* libuEv - Micro event loop library
2 *
3 * Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
4 * Copyright (c) 2013-2024 Joachim Wiberg <troglobit()gmail!com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
29
30#include <errno.h>
31#include <fcntl.h> /* O_CLOEXEC */
32#include <string.h> /* memset() */
33#include <sys/epoll.h>
34#include <sys/ioctl.h>
35#include <sys/select.h> /* for select() workaround */
36#include <sys/signalfd.h> /* struct signalfd_siginfo */
37#include <unistd.h> /* close(), read() */
38
39#include "uev.h"
40
41
42static int _init(uev_ctx_t *ctx, int close_old)
43{
44 int fd;
45
46 fd = epoll_create1(EPOLL_CLOEXEC);
47 if (fd < 0)
48 return -1;
49
50 if (close_old)
51 close(ctx->fd);
52
53 ctx->fd = fd;
54
55 return 0;
56}
57
58/* Used by file i/o workaround when epoll => EPERM */
59static int has_data(int fd)
60{
61 struct timeval timeout = { 0, 0 };
62 fd_set fds;
63 int n = 0;
64
65 FD_ZERO(&fds);
66 FD_SET(fd, &fds);
67
68 if (select(1, &fds, NULL, NULL, &timeout) > 0)
69 return ioctl(0, FIONREAD, &n) == 0 && n > 0;
70
71 return 0;
72}
73
74/* Private to libuEv, do not use directly! */
75int _uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events)
76{
77 if (!ctx || !w) {
78 errno = EINVAL;
79 return -1;
80 }
81
82 w->ctx = ctx;
83 w->type = type;
84 w->active = 0;
85 w->fd = fd;
86 w->cb = cb;
87 w->arg = arg;
88 w->events = events;
89
90 return 0;
91}
92
93/* Private to libuEv, do not use directly! */
94int _uev_watcher_start(uev_t *w)
95{
96 struct epoll_event ev;
97
98 if (!w || w->fd < 0 || !w->ctx) {
99 errno = EINVAL;
100 return -1;
101 }
102
103 if (_uev_watcher_active(w))
104 return 0;
105
106 ev.events = w->events | EPOLLRDHUP;
107 ev.data.ptr = w;
108 if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
109 if (errno != EPERM)
110 return -1;
111
112 /* Handle special case: `application < file.txt` */
113 if (w->type != UEV_IO_TYPE || w->events != UEV_READ)
114 return -1;
115
116 /* Only allow this special handling for stdin */
117 if (w->fd != STDIN_FILENO)
118 return -1;
119
120 w->ctx->workaround = 1;
121 w->active = -1;
122 } else {
123 w->active = 1;
124 }
125
126 /* Add to internal list for bookkeeping */
127 _UEV_INSERT(w, w->ctx->watchers);
128
129 return 0;
130}
131
132/* Private to libuEv, do not use directly! */
133int _uev_watcher_stop(uev_t *w)
134{
135 if (!w) {
136 errno = EINVAL;
137 return -1;
138 }
139
140 if (!_uev_watcher_active(w))
141 return 0;
142
143 w->active = 0;
144
145 /* Remove from internal list */
146 _UEV_REMOVE(w, w->ctx->watchers);
147
148 /* Remove from kernel */
149 if (epoll_ctl(w->ctx->fd, EPOLL_CTL_DEL, w->fd, NULL) < 0)
150 return -1;
151
152 return 0;
153}
154
155/* Private to libuEv, do not use directly! */
156int _uev_watcher_active(uev_t *w)
157{
158 if (!w)
159 return 0;
160
161 return w->active > 0;
162}
163
164/* Private to libuEv, do not use directly! */
165int _uev_watcher_rearm(uev_t *w)
166{
167 struct epoll_event ev;
168
169 if (!w || w->fd < 0) {
170 errno = EINVAL;
171 return -1;
172 }
173
174 ev.events = w->events | EPOLLRDHUP;
175 ev.data.ptr = w;
176 if (epoll_ctl(w->ctx->fd, EPOLL_CTL_MOD, w->fd, &ev) < 0)
177 return -1;
178
179 return 0;
180}
181
192{
193 return uev_init1(ctx, UEV_MAX_EVENTS);
194}
195
218int uev_init1(uev_ctx_t *ctx, int maxevents)
219{
220 if (!ctx || maxevents < 1) {
221 errno = EINVAL;
222 return -1;
223 }
224
225 if (maxevents > UEV_MAX_EVENTS)
226 maxevents = UEV_MAX_EVENTS;
227
228 memset(ctx, 0, sizeof(*ctx));
229 ctx->maxevents = maxevents;
230
231 return _init(ctx, 0);
232}
233
241{
242 uev_t *w;
243
244 if (!ctx) {
245 errno = EINVAL;
246 return -1;
247 }
248
249 _UEV_FOREACH(w, ctx->watchers) {
250 /* Remove from internal list */
251 _UEV_REMOVE(w, ctx->watchers);
252
253 if (!_uev_watcher_active(w))
254 continue;
255
256 switch (w->type) {
257 case UEV_IO_TYPE:
258 uev_io_stop(w);
259 break;
260
261 case UEV_SIGNAL_TYPE:
263 break;
264
265 case UEV_TIMER_TYPE:
266 case UEV_CRON_TYPE:
268 break;
269
270 case UEV_EVENT_TYPE:
272 break;
273 }
274 }
275
276 ctx->watchers = NULL;
277 ctx->running = 0;
278 if (ctx->fd > -1)
279 close(ctx->fd);
280 ctx->fd = -1;
281
282 return 0;
283}
284
299int uev_run(uev_ctx_t *ctx, int flags)
300{
301 int timeout = -1;
302 uev_t *w;
303
304 if (!ctx || ctx->fd < 0) {
305 errno = EINVAL;
306 return -1;
307 }
308
309 if (flags & UEV_NONBLOCK)
310 timeout = 0;
311
312 /* Start the event loop */
313 ctx->running = 1;
314
315 /* Start all dormant timers */
316 _UEV_FOREACH(w, ctx->watchers) {
317 if (UEV_CRON_TYPE == w->type)
318 uev_cron_set(w, w->u.c.when, w->u.c.interval);
319 if (UEV_TIMER_TYPE == w->type)
320 uev_timer_set(w, w->u.t.timeout, w->u.t.period);
321 }
322
323 while (ctx->running && ctx->watchers) {
324 struct epoll_event ee[UEV_MAX_EVENTS];
325 int maxevents = ctx->maxevents;
326 int i, nfds, rerun = 0;
327
328 if (maxevents > UEV_MAX_EVENTS)
329 maxevents = UEV_MAX_EVENTS;
330
331 /* Handle special case: `application < file.txt` */
332 if (ctx->workaround) {
333 _UEV_FOREACH(w, ctx->watchers) {
334 if (w->active != -1 || !w->cb)
335 continue;
336
337 if (!has_data(w->fd)) {
338 w->active = 0;
339 _UEV_REMOVE(w, ctx->watchers);
340 }
341
342 rerun++;
343 w->cb(w, w->arg, UEV_READ);
344 }
345 }
346
347 if (rerun)
348 continue;
349 ctx->workaround = 0;
350
351 while ((nfds = epoll_wait(ctx->fd, ee, maxevents, timeout)) < 0) {
352 if (!ctx->running)
353 break;
354
355 if (EINTR == errno)
356 continue; /* Signalled, try again */
357
358 /* Unrecoverable error, cleanup and exit with error. */
359 uev_exit(ctx);
360
361 return -2;
362 }
363
364 for (i = 0; ctx->running && i < nfds; i++) {
365 struct signalfd_siginfo fdsi;
366 ssize_t sz = sizeof(fdsi);
367 uint32_t events;
368 uint64_t exp;
369
370 w = (uev_t *)ee[i].data.ptr;
371 events = ee[i].events;
372
373 switch (w->type) {
374 case UEV_IO_TYPE:
375 if (events & (EPOLLHUP | EPOLLERR))
376 uev_io_stop(w);
377 break;
378
379 case UEV_SIGNAL_TYPE:
380 if (read(w->fd, &fdsi, sz) != sz) {
381 if (uev_signal_start(w)) {
383 events = UEV_ERROR;
384 }
385 memset(&w->siginfo, 0, sizeof(w->siginfo));
386 } else
387 w->siginfo = fdsi;
388 break;
389
390 case UEV_TIMER_TYPE:
391 if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
393 events = UEV_ERROR;
394 }
395
396 if (!w->u.t.period)
397 w->u.t.timeout = 0;
398 if (!w->u.t.timeout)
400 break;
401
402 case UEV_CRON_TYPE:
403 if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
404 events = UEV_HUP;
405 if (errno != ECANCELED) {
406 uev_cron_stop(w);
407 events = UEV_ERROR;
408 }
409 }
410
411 if (!w->u.c.interval)
412 w->u.c.when = 0;
413 else
414 w->u.c.when += w->u.c.interval;
415 if (!w->u.c.when)
417 break;
418
419 case UEV_EVENT_TYPE:
420 if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp))
421 events = UEV_HUP;
422 break;
423 }
424
425 /*
426 * NOTE: Must be last action for watcher, the
427 * callback may delete itself.
428 */
429 if (w->cb)
430 w->cb(w, w->arg, events & UEV_EVENT_MASK);
431 }
432
433 if (flags & UEV_ONCE)
434 break;
435 }
436
437 return 0;
438}
439
int uev_cron_set(uev_t *w, time_t when, time_t interval)
Definition cron.c:100
int uev_cron_stop(uev_t *w)
Definition cron.c:156
int uev_event_stop(uev_t *w)
Definition event.c:92
int uev_io_stop(uev_t *w)
Definition io.c:93
int uev_signal_stop(uev_t *w)
Definition signal.c:147
int uev_signal_start(uev_t *w)
Definition signal.c:128
int fd
Definition uev.h:79
uev_ctx_t * ctx
Definition uev.h:80
struct signalfd_siginfo siginfo
Definition uev.h:83
int uev_timer_stop(uev_t *w)
Definition timer.c:179
int uev_timer_set(uev_t *w, int timeout, int period)
Definition timer.c:116
int uev_exit(uev_ctx_t *ctx)
Definition uev.c:240
int uev_run(uev_ctx_t *ctx, int flags)
Definition uev.c:299
int uev_init1(uev_ctx_t *ctx, int maxevents)
Definition uev.c:218
int uev_init(uev_ctx_t *ctx)
Definition uev.c:191
#define UEV_MAX_EVENTS
Definition uev.h:41
struct uev_ctx uev_ctx_t
Definition uev.h:70
#define UEV_HUP
Definition uev.h:49
#define UEV_READ
Definition uev.h:46
#define UEV_NONBLOCK
Definition uev.h:56
void uev_cb_t(uev_t *w, void *arg, int events)
Definition uev.h:97
#define UEV_ONCE
Definition uev.h:55
#define UEV_ERROR
Definition uev.h:45
struct uev uev_t