1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * eloop - portable event based main loop.
4 * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
5 * All rights reserved.
6
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/time.h>
30
31 #include <assert.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <poll.h>
35 #include <stdbool.h>
36 #include <signal.h>
37 #include <stdint.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 /* config.h should define HAVE_PPOLL, etc. */
43 #if defined(HAVE_CONFIG_H) && !defined(NO_CONFIG_H)
44 #include "config.h"
45 #endif
46
47 #if defined(HAVE_PPOLL)
48 #elif defined(HAVE_POLLTS)
49 #define ppoll pollts
50 #elif !defined(HAVE_PSELECT)
51 #pragma message("Compiling eloop with pselect(2) support.")
52 #define HAVE_PSELECT
53 #define ppoll eloop_ppoll
54 #endif
55
56 #include "eloop.h"
57
58 #ifndef UNUSED
59 #define UNUSED(a) (void)((a))
60 #endif
61 #ifndef __unused
62 #ifdef __GNUC__
63 #define __unused __attribute__((__unused__))
64 #else
65 #define __unused
66 #endif
67 #endif
68
69 #ifdef HAVE_PSELECT
70 #include <sys/select.h>
71 #endif
72
73 /* Our structures require TAILQ macros, which really every libc should
74 * ship as they are useful beyond belief.
75 * Sadly some libc's don't have sys/queue.h and some that do don't have
76 * the TAILQ_FOREACH macro. For those that don't, the application using
77 * this implementation will need to ship a working queue.h somewhere.
78 * If we don't have sys/queue.h found in config.h, then
79 * allow QUEUE_H to override loading queue.h in the current directory. */
80 #ifndef TAILQ_FOREACH
81 #ifdef HAVE_SYS_QUEUE_H
82 #include <sys/queue.h>
83 #elif defined(QUEUE_H)
84 #define __QUEUE_HEADER(x) #x
85 #define _QUEUE_HEADER(x) __QUEUE_HEADER(x)
86 #include _QUEUE_HEADER(QUEUE_H)
87 #else
88 #include "queue.h"
89 #endif
90 #endif
91
92 #ifdef ELOOP_DEBUG
93 #include <stdio.h>
94 #endif
95
96 /*
97 * Allow a backlog of signals.
98 * If you use many eloops in the same process, they should all
99 * use the same signal handler or have the signal handler unset.
100 * Otherwise the signal might not behave as expected.
101 */
102 #define ELOOP_NSIGNALS 5
103
104 /*
105 * time_t is a signed integer of an unspecified size.
106 * To adjust for time_t wrapping, we need to work the maximum signed
107 * value and use that as a maximum.
108 */
109 #ifndef TIME_MAX
110 #define TIME_MAX ((1ULL << (sizeof(time_t) * NBBY - 1)) - 1)
111 #endif
112 /* The unsigned maximum is then simple - multiply by two and add one. */
113 #ifndef UTIME_MAX
114 #define UTIME_MAX (TIME_MAX * 2) + 1
115 #endif
116
117 struct eloop_event {
118 TAILQ_ENTRY(eloop_event) next;
119 int fd;
120 void (*read_cb)(void *);
121 void *read_cb_arg;
122 void (*write_cb)(void *);
123 void *write_cb_arg;
124 struct pollfd *pollfd;
125 };
126
127 struct eloop_timeout {
128 TAILQ_ENTRY(eloop_timeout) next;
129 unsigned int seconds;
130 unsigned int nseconds;
131 void (*callback)(void *);
132 void *arg;
133 int queue;
134 };
135
136 struct eloop {
137 TAILQ_HEAD (event_head, eloop_event) events;
138 size_t nevents;
139 struct event_head free_events;
140 bool events_need_setup;
141
142 struct timespec now;
143 TAILQ_HEAD (timeout_head, eloop_timeout) timeouts;
144 struct timeout_head free_timeouts;
145
146 const int *signals;
147 size_t signals_len;
148 void (*signal_cb)(int, void *);
149 void *signal_cb_ctx;
150
151 struct pollfd *fds;
152 size_t nfds;
153
154 int exitnow;
155 int exitcode;
156 };
157
158 #ifdef HAVE_REALLOCARRAY
159 #define eloop_realloca reallocarray
160 #else
161 /* Handy routing to check for potential overflow.
162 * reallocarray(3) and reallocarr(3) are not portable. */
163 #define SQRT_SIZE_MAX (((size_t)1) << (sizeof(size_t) * CHAR_BIT / 2))
164 static void *
eloop_realloca(void * ptr,size_t n,size_t size)165 eloop_realloca(void *ptr, size_t n, size_t size)
166 {
167
168 if ((n | size) >= SQRT_SIZE_MAX && n > SIZE_MAX / size) {
169 errno = EOVERFLOW;
170 return NULL;
171 }
172 return realloc(ptr, n * size);
173 }
174 #endif
175
176 #ifdef HAVE_PSELECT
177 /* Wrapper around pselect, to imitate the ppoll call. */
178 static int
eloop_ppoll(struct pollfd * fds,nfds_t nfds,const struct timespec * ts,const sigset_t * sigmask)179 eloop_ppoll(struct pollfd * fds, nfds_t nfds,
180 const struct timespec *ts, const sigset_t *sigmask)
181 {
182 fd_set read_fds, write_fds;
183 nfds_t n;
184 int maxfd, r;
185
186 FD_ZERO(&read_fds);
187 FD_ZERO(&write_fds);
188 maxfd = 0;
189 for (n = 0; n < nfds; n++) {
190 if (fds[n].events & POLLIN) {
191 FD_SET(fds[n].fd, &read_fds);
192 if (fds[n].fd > maxfd)
193 maxfd = fds[n].fd;
194 }
195 if (fds[n].events & POLLOUT) {
196 FD_SET(fds[n].fd, &write_fds);
197 if (fds[n].fd > maxfd)
198 maxfd = fds[n].fd;
199 }
200 }
201
202 r = pselect(maxfd + 1, &read_fds, &write_fds, NULL, ts, sigmask);
203 if (r > 0) {
204 for (n = 0; n < nfds; n++) {
205 fds[n].revents =
206 FD_ISSET(fds[n].fd, &read_fds) ? POLLIN : 0;
207 if (FD_ISSET(fds[n].fd, &write_fds))
208 fds[n].revents |= POLLOUT;
209 }
210 }
211
212 return r;
213 }
214 #endif
215
216 unsigned long long
eloop_timespec_diff(const struct timespec * tsp,const struct timespec * usp,unsigned int * nsp)217 eloop_timespec_diff(const struct timespec *tsp, const struct timespec *usp,
218 unsigned int *nsp)
219 {
220 unsigned long long tsecs, usecs, secs;
221 long nsecs;
222
223 if (tsp->tv_sec < 0) /* time wreapped */
224 tsecs = UTIME_MAX - (unsigned long long)(-tsp->tv_sec);
225 else
226 tsecs = (unsigned long long)tsp->tv_sec;
227 if (usp->tv_sec < 0) /* time wrapped */
228 usecs = UTIME_MAX - (unsigned long long)(-usp->tv_sec);
229 else
230 usecs = (unsigned long long)usp->tv_sec;
231
232 if (usecs > tsecs) /* time wrapped */
233 secs = (UTIME_MAX - usecs) + tsecs;
234 else
235 secs = tsecs - usecs;
236
237 nsecs = tsp->tv_nsec - usp->tv_nsec;
238 if (nsecs < 0) {
239 if (secs == 0)
240 nsecs = 0;
241 else {
242 secs--;
243 nsecs += NSEC_PER_SEC;
244 }
245 }
246 if (nsp != NULL)
247 *nsp = (unsigned int)nsecs;
248 return secs;
249 }
250
251 static void
eloop_reduce_timers(struct eloop * eloop)252 eloop_reduce_timers(struct eloop *eloop)
253 {
254 struct timespec now;
255 unsigned long long secs;
256 unsigned int nsecs;
257 struct eloop_timeout *t;
258
259 clock_gettime(CLOCK_MONOTONIC, &now);
260 secs = eloop_timespec_diff(&now, &eloop->now, &nsecs);
261
262 TAILQ_FOREACH(t, &eloop->timeouts, next) {
263 if (secs > t->seconds) {
264 t->seconds = 0;
265 t->nseconds = 0;
266 } else {
267 t->seconds -= (unsigned int)secs;
268 if (nsecs > t->nseconds) {
269 if (t->seconds == 0)
270 t->nseconds = 0;
271 else {
272 t->seconds--;
273 t->nseconds = NSEC_PER_SEC
274 - (nsecs - t->nseconds);
275 }
276 } else
277 t->nseconds -= nsecs;
278 }
279 }
280
281 eloop->now = now;
282 }
283
284 static void
eloop_event_setup_fds(struct eloop * eloop)285 eloop_event_setup_fds(struct eloop *eloop)
286 {
287 struct eloop_event *e, *ne;
288 struct pollfd *pfd;
289
290 pfd = eloop->fds;
291 TAILQ_FOREACH_SAFE(e, &eloop->events, next, ne) {
292 if (e->fd == -1) {
293 TAILQ_REMOVE(&eloop->events, e, next);
294 TAILQ_INSERT_TAIL(&eloop->free_events, e, next);
295 continue;
296 }
297 #ifdef ELOOP_DEBUG
298 fprintf(stderr, "%s(%d) fd=%d, rcb=%p, wcb=%p\n",
299 __func__, getpid(), e->fd, e->read_cb, e->write_cb);
300 #endif
301 e->pollfd = pfd;
302 pfd->fd = e->fd;
303 pfd->events = 0;
304 if (e->read_cb != NULL)
305 pfd->events |= POLLIN;
306 if (e->write_cb != NULL)
307 pfd->events |= POLLOUT;
308 pfd->revents = 0;
309 pfd++;
310 }
311 eloop->events_need_setup = false;
312 }
313
314 size_t
eloop_event_count(const struct eloop * eloop)315 eloop_event_count(const struct eloop *eloop)
316 {
317
318 return eloop->nevents;
319 }
320
321 int
eloop_event_add_rw(struct eloop * eloop,int fd,void (* read_cb)(void *),void * read_cb_arg,void (* write_cb)(void *),void * write_cb_arg)322 eloop_event_add_rw(struct eloop *eloop, int fd,
323 void (*read_cb)(void *), void *read_cb_arg,
324 void (*write_cb)(void *), void *write_cb_arg)
325 {
326 struct eloop_event *e;
327 struct pollfd *pfd;
328
329 assert(eloop != NULL);
330 assert(read_cb != NULL || write_cb != NULL);
331 if (fd == -1) {
332 errno = EINVAL;
333 return -1;
334 }
335
336 TAILQ_FOREACH(e, &eloop->events, next) {
337 if (e->fd == fd)
338 break;
339 }
340
341 if (e == NULL) {
342 if (eloop->nevents + 1 > eloop->nfds) {
343 pfd = eloop_realloca(eloop->fds, eloop->nevents + 1,
344 sizeof(*pfd));
345 if (pfd == NULL)
346 return -1;
347 eloop->fds = pfd;
348 eloop->nfds++;
349 }
350
351 e = TAILQ_FIRST(&eloop->free_events);
352 if (e != NULL)
353 TAILQ_REMOVE(&eloop->free_events, e, next);
354 else {
355 e = malloc(sizeof(*e));
356 if (e == NULL)
357 return -1;
358 }
359 TAILQ_INSERT_HEAD(&eloop->events, e, next);
360 eloop->nevents++;
361 e->fd = fd;
362 e->read_cb = read_cb;
363 e->read_cb_arg = read_cb_arg;
364 e->write_cb = write_cb;
365 e->write_cb_arg = write_cb_arg;
366 goto setup;
367 }
368
369 if (read_cb) {
370 e->read_cb = read_cb;
371 e->read_cb_arg = read_cb_arg;
372 }
373 if (write_cb) {
374 e->write_cb = write_cb;
375 e->write_cb_arg = write_cb_arg;
376 }
377
378 setup:
379 e->pollfd = NULL;
380 eloop->events_need_setup = true;
381 return 0;
382 }
383
384 int
eloop_event_add(struct eloop * eloop,int fd,void (* read_cb)(void *),void * read_cb_arg)385 eloop_event_add(struct eloop *eloop, int fd,
386 void (*read_cb)(void *), void *read_cb_arg)
387 {
388
389 return eloop_event_add_rw(eloop, fd, read_cb, read_cb_arg, NULL, NULL);
390 }
391
392 int
eloop_event_add_w(struct eloop * eloop,int fd,void (* write_cb)(void *),void * write_cb_arg)393 eloop_event_add_w(struct eloop *eloop, int fd,
394 void (*write_cb)(void *), void *write_cb_arg)
395 {
396
397 return eloop_event_add_rw(eloop, fd, NULL,NULL, write_cb, write_cb_arg);
398 }
399
400 int
eloop_event_delete_write(struct eloop * eloop,int fd,int write_only)401 eloop_event_delete_write(struct eloop *eloop, int fd, int write_only)
402 {
403 struct eloop_event *e;
404
405 assert(eloop != NULL);
406 if (fd == -1) {
407 errno = EINVAL;
408 return -1;
409 }
410
411 TAILQ_FOREACH(e, &eloop->events, next) {
412 if (e->fd == fd)
413 break;
414 }
415 if (e == NULL) {
416 errno = ENOENT;
417 return -1;
418 }
419
420 if (write_only) {
421 if (e->read_cb == NULL)
422 goto remove;
423 e->write_cb = NULL;
424 e->write_cb_arg = NULL;
425 if (e->pollfd != NULL) {
426 e->pollfd->events &= ~POLLOUT;
427 e->pollfd->revents &= ~POLLOUT;
428 }
429 return 1;
430 }
431
432 remove:
433 e->fd = -1;
434 eloop->nevents--;
435 eloop->events_need_setup = true;
436 return 1;
437 }
438
439 /*
440 * This implementation should cope with UINT_MAX seconds on a system
441 * where time_t is INT32_MAX. It should also cope with the monotonic timer
442 * wrapping, although this is highly unlikely.
443 * unsigned int should match or be greater than any on wire specified timeout.
444 */
445 static int
eloop_q_timeout_add(struct eloop * eloop,int queue,unsigned int seconds,unsigned int nseconds,void (* callback)(void *),void * arg)446 eloop_q_timeout_add(struct eloop *eloop, int queue,
447 unsigned int seconds, unsigned int nseconds,
448 void (*callback)(void *), void *arg)
449 {
450 struct eloop_timeout *t, *tt = NULL;
451
452 assert(eloop != NULL);
453 assert(callback != NULL);
454 assert(nseconds <= NSEC_PER_SEC);
455
456 /* Remove existing timeout if present. */
457 TAILQ_FOREACH(t, &eloop->timeouts, next) {
458 if (t->callback == callback && t->arg == arg) {
459 TAILQ_REMOVE(&eloop->timeouts, t, next);
460 break;
461 }
462 }
463
464 if (t == NULL) {
465 /* No existing, so allocate or grab one from the free pool. */
466 if ((t = TAILQ_FIRST(&eloop->free_timeouts))) {
467 TAILQ_REMOVE(&eloop->free_timeouts, t, next);
468 } else {
469 if ((t = malloc(sizeof(*t))) == NULL)
470 return -1;
471 }
472 }
473
474 eloop_reduce_timers(eloop);
475
476 t->seconds = seconds;
477 t->nseconds = nseconds;
478 t->callback = callback;
479 t->arg = arg;
480 t->queue = queue;
481
482 /* The timeout list should be in chronological order,
483 * soonest first. */
484 TAILQ_FOREACH(tt, &eloop->timeouts, next) {
485 if (t->seconds < tt->seconds ||
486 (t->seconds == tt->seconds && t->nseconds < tt->nseconds))
487 {
488 TAILQ_INSERT_BEFORE(tt, t, next);
489 return 0;
490 }
491 }
492 TAILQ_INSERT_TAIL(&eloop->timeouts, t, next);
493 return 0;
494 }
495
496 int
eloop_q_timeout_add_tv(struct eloop * eloop,int queue,const struct timespec * when,void (* callback)(void *),void * arg)497 eloop_q_timeout_add_tv(struct eloop *eloop, int queue,
498 const struct timespec *when, void (*callback)(void *), void *arg)
499 {
500
501 if (when->tv_sec < 0 || (unsigned long)when->tv_sec > UINT_MAX) {
502 errno = EINVAL;
503 return -1;
504 }
505 if (when->tv_nsec < 0 || when->tv_nsec > NSEC_PER_SEC) {
506 errno = EINVAL;
507 return -1;
508 }
509
510 return eloop_q_timeout_add(eloop, queue,
511 (unsigned int)when->tv_sec, (unsigned int)when->tv_sec,
512 callback, arg);
513 }
514
515 int
eloop_q_timeout_add_sec(struct eloop * eloop,int queue,unsigned int seconds,void (* callback)(void *),void * arg)516 eloop_q_timeout_add_sec(struct eloop *eloop, int queue, unsigned int seconds,
517 void (*callback)(void *), void *arg)
518 {
519
520 return eloop_q_timeout_add(eloop, queue, seconds, 0, callback, arg);
521 }
522
523 int
eloop_q_timeout_add_msec(struct eloop * eloop,int queue,unsigned long when,void (* callback)(void *),void * arg)524 eloop_q_timeout_add_msec(struct eloop *eloop, int queue, unsigned long when,
525 void (*callback)(void *), void *arg)
526 {
527 unsigned long seconds, nseconds;
528
529 seconds = when / MSEC_PER_SEC;
530 if (seconds > UINT_MAX) {
531 errno = EINVAL;
532 return -1;
533 }
534
535 nseconds = (when % MSEC_PER_SEC) * NSEC_PER_MSEC;
536 return eloop_q_timeout_add(eloop, queue,
537 (unsigned int)seconds, (unsigned int)nseconds, callback, arg);
538 }
539
540 int
eloop_q_timeout_delete(struct eloop * eloop,int queue,void (* callback)(void *),void * arg)541 eloop_q_timeout_delete(struct eloop *eloop, int queue,
542 void (*callback)(void *), void *arg)
543 {
544 struct eloop_timeout *t, *tt;
545 int n;
546
547 assert(eloop != NULL);
548
549 n = 0;
550 TAILQ_FOREACH_SAFE(t, &eloop->timeouts, next, tt) {
551 if ((queue == 0 || t->queue == queue) &&
552 t->arg == arg &&
553 (!callback || t->callback == callback))
554 {
555 TAILQ_REMOVE(&eloop->timeouts, t, next);
556 TAILQ_INSERT_TAIL(&eloop->free_timeouts, t, next);
557 n++;
558 }
559 }
560 return n;
561 }
562
563 void
eloop_exit(struct eloop * eloop,int code)564 eloop_exit(struct eloop *eloop, int code)
565 {
566
567 assert(eloop != NULL);
568
569 eloop->exitcode = code;
570 eloop->exitnow = 1;
571 }
572
573 void
eloop_enter(struct eloop * eloop)574 eloop_enter(struct eloop *eloop)
575 {
576
577 eloop->exitnow = 0;
578 }
579
580 void
eloop_signal_set_cb(struct eloop * eloop,const int * signals,size_t signals_len,void (* signal_cb)(int,void *),void * signal_cb_ctx)581 eloop_signal_set_cb(struct eloop *eloop,
582 const int *signals, size_t signals_len,
583 void (*signal_cb)(int, void *), void *signal_cb_ctx)
584 {
585
586 assert(eloop != NULL);
587
588 eloop->signals = signals;
589 eloop->signals_len = signals_len;
590 eloop->signal_cb = signal_cb;
591 eloop->signal_cb_ctx = signal_cb_ctx;
592 }
593
594 static volatile int _eloop_sig[ELOOP_NSIGNALS];
595 static volatile size_t _eloop_nsig;
596
597 static void
eloop_signal3(int sig,__unused siginfo_t * siginfo,__unused void * arg)598 eloop_signal3(int sig, __unused siginfo_t *siginfo, __unused void *arg)
599 {
600
601 if (_eloop_nsig == __arraycount(_eloop_sig)) {
602 #ifdef ELOOP_DEBUG
603 fprintf(stderr, "%s: signal storm, discarding signal %d\n",
604 __func__, sig);
605 #endif
606 return;
607 }
608
609 _eloop_sig[_eloop_nsig++] = sig;
610 }
611
612 int
eloop_signal_mask(struct eloop * eloop,sigset_t * oldset)613 eloop_signal_mask(struct eloop *eloop, sigset_t *oldset)
614 {
615 sigset_t newset;
616 size_t i;
617 struct sigaction sa = {
618 .sa_sigaction = eloop_signal3,
619 .sa_flags = SA_SIGINFO,
620 };
621
622 assert(eloop != NULL);
623
624 sigemptyset(&newset);
625 for (i = 0; i < eloop->signals_len; i++)
626 sigaddset(&newset, eloop->signals[i]);
627 if (sigprocmask(SIG_SETMASK, &newset, oldset) == -1)
628 return -1;
629
630 sigemptyset(&sa.sa_mask);
631
632 for (i = 0; i < eloop->signals_len; i++) {
633 if (sigaction(eloop->signals[i], &sa, NULL) == -1)
634 return -1;
635 }
636 return 0;
637 }
638
639 struct eloop *
eloop_new(void)640 eloop_new(void)
641 {
642 struct eloop *eloop;
643
644 eloop = calloc(1, sizeof(*eloop));
645 if (eloop == NULL)
646 return NULL;
647
648 /* Check we have a working monotonic clock. */
649 if (clock_gettime(CLOCK_MONOTONIC, &eloop->now) == -1) {
650 free(eloop);
651 return NULL;
652 }
653
654 TAILQ_INIT(&eloop->events);
655 TAILQ_INIT(&eloop->free_events);
656 TAILQ_INIT(&eloop->timeouts);
657 TAILQ_INIT(&eloop->free_timeouts);
658 eloop->exitcode = EXIT_FAILURE;
659
660 return eloop;
661 }
662
663 void
eloop_clear(struct eloop * eloop)664 eloop_clear(struct eloop *eloop)
665 {
666 struct eloop_event *e;
667 struct eloop_timeout *t;
668
669 if (eloop == NULL)
670 return;
671
672 eloop->nevents = 0;
673 eloop->signals = NULL;
674 eloop->signals_len = 0;
675
676 while ((e = TAILQ_FIRST(&eloop->events))) {
677 TAILQ_REMOVE(&eloop->events, e, next);
678 free(e);
679 }
680 while ((e = TAILQ_FIRST(&eloop->free_events))) {
681 TAILQ_REMOVE(&eloop->free_events, e, next);
682 free(e);
683 }
684 while ((t = TAILQ_FIRST(&eloop->timeouts))) {
685 TAILQ_REMOVE(&eloop->timeouts, t, next);
686 free(t);
687 }
688 while ((t = TAILQ_FIRST(&eloop->free_timeouts))) {
689 TAILQ_REMOVE(&eloop->free_timeouts, t, next);
690 free(t);
691 }
692
693 free(eloop->fds);
694 eloop->fds = NULL;
695 eloop->nfds = 0;
696 }
697
698 void
eloop_free(struct eloop * eloop)699 eloop_free(struct eloop *eloop)
700 {
701
702 eloop_clear(eloop);
703 free(eloop);
704 }
705
706 int
eloop_start(struct eloop * eloop,sigset_t * signals)707 eloop_start(struct eloop *eloop, sigset_t *signals)
708 {
709 int n;
710 struct eloop_event *e;
711 struct eloop_timeout *t;
712 struct timespec ts, *tsp;
713
714 assert(eloop != NULL);
715
716 for (;;) {
717 if (eloop->exitnow)
718 break;
719
720 if (_eloop_nsig != 0) {
721 n = _eloop_sig[--_eloop_nsig];
722 if (eloop->signal_cb != NULL)
723 eloop->signal_cb(n, eloop->signal_cb_ctx);
724 continue;
725 }
726
727 t = TAILQ_FIRST(&eloop->timeouts);
728 if (t == NULL && eloop->nevents == 0)
729 break;
730
731 if (t != NULL)
732 eloop_reduce_timers(eloop);
733
734 if (t != NULL && t->seconds == 0 && t->nseconds == 0) {
735 TAILQ_REMOVE(&eloop->timeouts, t, next);
736 t->callback(t->arg);
737 TAILQ_INSERT_TAIL(&eloop->free_timeouts, t, next);
738 continue;
739 }
740
741 if (t != NULL) {
742 if (t->seconds > INT_MAX) {
743 ts.tv_sec = (time_t)INT_MAX;
744 ts.tv_nsec = 0;
745 } else {
746 ts.tv_sec = (time_t)t->seconds;
747 ts.tv_nsec = (long)t->nseconds;
748 }
749 tsp = &ts;
750 } else
751 tsp = NULL;
752
753 if (eloop->events_need_setup)
754 eloop_event_setup_fds(eloop);
755
756 n = ppoll(eloop->fds, (nfds_t)eloop->nevents, tsp, signals);
757 if (n == -1) {
758 if (errno == EINTR)
759 continue;
760 return -errno;
761 }
762 if (n == 0)
763 continue;
764
765 TAILQ_FOREACH(e, &eloop->events, next) {
766 /* Skip freshly added events */
767 if (e->pollfd == NULL)
768 continue;
769 if (e->pollfd->revents)
770 n--;
771 if (e->fd != -1 && e->pollfd->revents & POLLOUT) {
772 if (e->write_cb != NULL)
773 e->write_cb(e->write_cb_arg);
774 }
775 if (e->fd != -1 &&
776 e->pollfd != NULL && e->pollfd->revents)
777 {
778 if (e->read_cb != NULL)
779 e->read_cb(e->read_cb_arg);
780 }
781 if (n == 0)
782 break;
783 }
784 }
785
786 return eloop->exitcode;
787 }
788