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