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