"Fossies" - the Fresh Open Source Software Archive

Member "dhcpcd-9.4.1/src/eloop.c" (22 Oct 2021, 17426 Bytes) of package /linux/misc/dhcpcd-9.4.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "eloop.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 9.4.0_vs_9.4.1.

    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 }