xref: /dragonfly/contrib/dhcpcd/src/eloop.c (revision 7d3e9a5b)
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 *
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
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
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
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
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
315 eloop_event_count(const struct eloop *eloop)
316 {
317 
318 	return eloop->nevents;
319 }
320 
321 int
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
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
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
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
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
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
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
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
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
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
574 eloop_enter(struct eloop *eloop)
575 {
576 
577 	eloop->exitnow = 0;
578 }
579 
580 void
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
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
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 *
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
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
699 eloop_free(struct eloop *eloop)
700 {
701 
702 	eloop_clear(eloop);
703 	free(eloop);
704 }
705 
706 int
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