Mercurial > hg > dhcpcd
comparison src/eloop.c @ 5578:57e4bf2cc9e7 draft
eloop: Allow eloop to process all fds returned from poll(2)
We do this by ensuring the events list or pollfd struct storage
is not modified during the revent processing.
An event with a fd of -1 means it's been deleted and one without
a pollfd struct reference has been newly added.
This also allows us to count down the number of fd's that
returned a revent so we can break the loop early if possible.
This is a really minor optimisation that at best only applies if
more than one revent is returned via poll(2).
In the case on dhcpcd on NetBSD with privsep, the number of
fd's is really low. And on other platforms or without privsep it's
low also (just not as low).
It's only when you run dhcpcd per interface that the number
of fd's starts to creep upwards as you then need one per address
dhcpcd is monitoring (as well as the ARP listener per IPv4 address
for non NetBSD).
However, I use eloop in other code where this could be a good saving
and dhcpcd is where the master version of this lives!
| author | Roy Marples <roy@marples.name> |
|---|---|
| date | Sun, 24 Jan 2021 22:22:25 +0000 |
| parents | 9edfc000a89b |
| children |
comparison
equal
deleted
inserted
replaced
| 5577:4da45107d87a | 5578:57e4bf2cc9e7 |
|---|---|
| 30 | 30 |
| 31 #include <assert.h> | 31 #include <assert.h> |
| 32 #include <errno.h> | 32 #include <errno.h> |
| 33 #include <limits.h> | 33 #include <limits.h> |
| 34 #include <poll.h> | 34 #include <poll.h> |
| 35 #include <stdbool.h> | |
| 35 #include <signal.h> | 36 #include <signal.h> |
| 36 #include <stdint.h> | 37 #include <stdint.h> |
| 37 #include <stdlib.h> | 38 #include <stdlib.h> |
| 38 #include <string.h> | 39 #include <string.h> |
| 39 #include <unistd.h> | 40 #include <unistd.h> |
| 134 | 135 |
| 135 struct eloop { | 136 struct eloop { |
| 136 TAILQ_HEAD (event_head, eloop_event) events; | 137 TAILQ_HEAD (event_head, eloop_event) events; |
| 137 size_t nevents; | 138 size_t nevents; |
| 138 struct event_head free_events; | 139 struct event_head free_events; |
| 140 bool events_need_setup; | |
| 139 | 141 |
| 140 struct timespec now; | 142 struct timespec now; |
| 141 TAILQ_HEAD (timeout_head, eloop_timeout) timeouts; | 143 TAILQ_HEAD (timeout_head, eloop_timeout) timeouts; |
| 142 struct timeout_head free_timeouts; | 144 struct timeout_head free_timeouts; |
| 143 | 145 |
| 280 } | 282 } |
| 281 | 283 |
| 282 static void | 284 static void |
| 283 eloop_event_setup_fds(struct eloop *eloop) | 285 eloop_event_setup_fds(struct eloop *eloop) |
| 284 { | 286 { |
| 285 struct eloop_event *e; | 287 struct eloop_event *e, *ne; |
| 286 struct pollfd *pfd; | 288 struct pollfd *pfd; |
| 287 | 289 |
| 288 pfd = eloop->fds; | 290 pfd = eloop->fds; |
| 289 TAILQ_FOREACH(e, &eloop->events, next) { | 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 } | |
| 290 #ifdef ELOOP_DEBUG | 297 #ifdef ELOOP_DEBUG |
| 291 fprintf(stderr, "%s(%d) fd=%d, rcb=%p, wcb=%p\n", | 298 fprintf(stderr, "%s(%d) fd=%d, rcb=%p, wcb=%p\n", |
| 292 __func__, getpid(), e->fd, e->read_cb, e->write_cb); | 299 __func__, getpid(), e->fd, e->read_cb, e->write_cb); |
| 293 #endif | 300 #endif |
| 294 e->pollfd = pfd; | 301 e->pollfd = pfd; |
| 299 if (e->write_cb != NULL) | 306 if (e->write_cb != NULL) |
| 300 pfd->events |= POLLOUT; | 307 pfd->events |= POLLOUT; |
| 301 pfd->revents = 0; | 308 pfd->revents = 0; |
| 302 pfd++; | 309 pfd++; |
| 303 } | 310 } |
| 311 eloop->events_need_setup = false; | |
| 304 } | 312 } |
| 305 | 313 |
| 306 size_t | 314 size_t |
| 307 eloop_event_count(const struct eloop *eloop) | 315 eloop_event_count(const struct eloop *eloop) |
| 308 { | 316 { |
| 366 e->write_cb = write_cb; | 374 e->write_cb = write_cb; |
| 367 e->write_cb_arg = write_cb_arg; | 375 e->write_cb_arg = write_cb_arg; |
| 368 } | 376 } |
| 369 | 377 |
| 370 setup: | 378 setup: |
| 371 eloop_event_setup_fds(eloop); | 379 e->pollfd = NULL; |
| 380 eloop->events_need_setup = true; | |
| 372 return 0; | 381 return 0; |
| 373 } | 382 } |
| 374 | 383 |
| 375 int | 384 int |
| 376 eloop_event_add(struct eloop *eloop, int fd, | 385 eloop_event_add(struct eloop *eloop, int fd, |
| 392 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) |
| 393 { | 402 { |
| 394 struct eloop_event *e; | 403 struct eloop_event *e; |
| 395 | 404 |
| 396 assert(eloop != NULL); | 405 assert(eloop != NULL); |
| 406 if (fd == -1) { | |
| 407 errno = EINVAL; | |
| 408 return -1; | |
| 409 } | |
| 397 | 410 |
| 398 TAILQ_FOREACH(e, &eloop->events, next) { | 411 TAILQ_FOREACH(e, &eloop->events, next) { |
| 399 if (e->fd == fd) | 412 if (e->fd == fd) |
| 400 break; | 413 break; |
| 401 } | 414 } |
| 407 if (write_only) { | 420 if (write_only) { |
| 408 if (e->read_cb == NULL) | 421 if (e->read_cb == NULL) |
| 409 goto remove; | 422 goto remove; |
| 410 e->write_cb = NULL; | 423 e->write_cb = NULL; |
| 411 e->write_cb_arg = NULL; | 424 e->write_cb_arg = NULL; |
| 412 goto done; | 425 if (e->pollfd != NULL) { |
| 426 e->pollfd->events &= ~POLLOUT; | |
| 427 e->pollfd->revents &= ~POLLOUT; | |
| 428 } | |
| 429 return 1; | |
| 413 } | 430 } |
| 414 | 431 |
| 415 remove: | 432 remove: |
| 416 TAILQ_REMOVE(&eloop->events, e, next); | 433 e->fd = -1; |
| 417 TAILQ_INSERT_TAIL(&eloop->free_events, e, next); | |
| 418 eloop->nevents--; | 434 eloop->nevents--; |
| 419 | 435 eloop->events_need_setup = true; |
| 420 done: | |
| 421 eloop_event_setup_fds(eloop); | |
| 422 return 1; | 436 return 1; |
| 423 } | 437 } |
| 424 | 438 |
| 425 /* | 439 /* |
| 426 * This implementation should cope with UINT_MAX seconds on a system | 440 * This implementation should cope with UINT_MAX seconds on a system |
| 734 } | 748 } |
| 735 tsp = &ts; | 749 tsp = &ts; |
| 736 } else | 750 } else |
| 737 tsp = NULL; | 751 tsp = NULL; |
| 738 | 752 |
| 753 if (eloop->events_need_setup) | |
| 754 eloop_event_setup_fds(eloop); | |
| 755 | |
| 739 n = ppoll(eloop->fds, (nfds_t)eloop->nevents, tsp, signals); | 756 n = ppoll(eloop->fds, (nfds_t)eloop->nevents, tsp, signals); |
| 740 if (n == -1) { | 757 if (n == -1) { |
| 741 if (errno == EINTR) | 758 if (errno == EINTR) |
| 742 continue; | 759 continue; |
| 743 return -errno; | 760 return -errno; |
| 744 } | 761 } |
| 745 if (n == 0) | 762 if (n == 0) |
| 746 continue; | 763 continue; |
| 747 | 764 |
| 748 TAILQ_FOREACH(e, &eloop->events, next) { | 765 TAILQ_FOREACH(e, &eloop->events, next) { |
| 749 if (e->pollfd->revents & POLLOUT) { | 766 /* Skip freshly added events */ |
| 750 if (e->write_cb != NULL) { | 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) | |
| 751 e->write_cb(e->write_cb_arg); | 773 e->write_cb(e->write_cb_arg); |
| 752 break; | |
| 753 } | |
| 754 } | 774 } |
| 755 if (e->pollfd->revents) { | 775 if (e->fd != -1 && |
| 756 if (e->read_cb != NULL) { | 776 e->pollfd != NULL && e->pollfd->revents) |
| 777 { | |
| 778 if (e->read_cb != NULL) | |
| 757 e->read_cb(e->read_cb_arg); | 779 e->read_cb(e->read_cb_arg); |
| 758 break; | |
| 759 } | |
| 760 } | 780 } |
| 781 if (n == 0) | |
| 782 break; | |
| 761 } | 783 } |
| 762 } | 784 } |
| 763 | 785 |
| 764 return eloop->exitcode; | 786 return eloop->exitcode; |
| 765 } | 787 } |
