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 }