commit 0a53bd62d11ed31ce71da1fc9993bc25a6c93755 from: Omar Polo date: Wed Aug 16 10:04:17 2023 UTC switch from libevent to a smaller, thin wrapper around poll() libevent is a very cool library, I like its APIs and enjoy using it. However, for amused it is maybe a bit too much since it doesn't deal with too many file descriptors. Amused only needs to monitor one fd for the player process and the currently connected clients. Given that it runs per-user locally, having more than a dozen of fds open is very, very rare. commit - 87eb9c1e04bf4eeb12ede12213222168b6367609 commit + 0a53bd62d11ed31ce71da1fc9993bc25a6c93755 blob - 647e90f6b2832f3f432436f260b7540792b60216 blob + d9ca2da96350de15b9425d84e166a8276ad064df --- Makefile +++ Makefile @@ -8,6 +8,7 @@ SOURCES = amused.c \ compats.c \ control.c \ ctl.c \ + ev.c \ log.c \ player.c \ player_123.c \ @@ -100,6 +101,7 @@ ${DISTNAME}.tar.gz: ${DISTFILES} -include compats.d -include control.d -include ctl.d +-include ev.d -include log.d -include player.d -include player_123.d blob - 2f55e56f45781c5b460d3297da6bdc5374e3f262 blob + 83dcac522cc6d73a9d38e86f10d5e68f1bfa21a1 --- amused.c +++ amused.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include "amused.h" #include "control.h" +#include "ev.h" #include "log.h" #include "playlist.h" #include "xmalloc.h" @@ -44,8 +46,6 @@ struct imsgev *iev_player; const char *argv0; pid_t player_pid; -struct event ev_sigint; -struct event ev_sigterm; enum amused_process { PROC_MAIN, @@ -79,10 +79,10 @@ main_shutdown(void) } static void -main_sig_handler(int sig, short event, void *arg) +main_sig_handler(int sig, int event, void *arg) { /* - * Normal signal handler rules don't apply because libevent + * Normal signal handler rules don't apply because ev.c * decouples for us. */ @@ -97,7 +97,7 @@ main_sig_handler(int sig, short event, void *arg) } static void -main_dispatch_player(int sig, short event, void *d) +main_dispatch_player(int sig, int event, void *d) { char *errstr; struct imsgev *iev = d; @@ -107,13 +107,13 @@ main_dispatch_player(int sig, short event, void *d) ssize_t n; int shut = 0; - if (event & EV_READ) { + if (event & POLLIN) { if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatal("imsg_read error"); if (n == 0) /* Connection closed */ shut = 1; } - if (event & EV_WRITE) { + if (event & POLLOUT) { if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) fatal("msgbuf_write"); if (n == 0) /* Connection closed */ @@ -181,13 +181,10 @@ main_dispatch_player(int sig, short event, void *d) imsg_free(&imsg); } - if (!shut) + if (shut) + ev_break(); + else imsg_event_add(iev); - else { - /* This pipe is dead. Remove its event handler. */ - event_del(&iev->ev); - event_loopexit(NULL); - } } static pid_t @@ -261,25 +258,22 @@ amused_main(void) player_pid = start_child(PROC_PLAYER, pipe_main2player[1]); - event_init(); - - signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); - signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); + if (ev_init() == -1) + fatal("ev_init"); - signal_add(&ev_sigint, NULL); - signal_add(&ev_sigterm, NULL); - signal(SIGHUP, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGPIPE, SIG_IGN); + ev_signal(SIGINT, main_sig_handler, NULL); + ev_signal(SIGTERM, main_sig_handler, NULL); + iev_player = xmalloc(sizeof(*iev_player)); imsg_init(&iev_player->ibuf, pipe_main2player[0]); iev_player->handler = main_dispatch_player; - iev_player->events = EV_READ; - event_set(&iev_player->ev, iev_player->ibuf.fd, iev_player->events, + iev_player->events = POLLIN; + ev_add(iev_player->ibuf.fd, iev_player->events, iev_player->handler, iev_player); - event_add(&iev_player->ev, NULL); if ((control_fd = control_init(csock)) == -1) fatal("control socket setup failed %s", csock); @@ -289,7 +283,7 @@ amused_main(void) fatal("pledge"); log_info("startup"); - event_dispatch(); + ev_loop(); main_shutdown(); } @@ -359,13 +353,11 @@ spawn_daemon(void) void imsg_event_add(struct imsgev *iev) { - iev->events = EV_READ; + iev->events = POLLIN; if (iev->ibuf.w.queued) - iev->events |= EV_WRITE; + iev->events |= POLLOUT; - event_del(&iev->ev); - event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); - event_add(&iev->ev, NULL); + ev_add(iev->ibuf.fd, iev->events, iev->handler, iev); } int blob - b6bba1c01bc22266594c48a8df59d27e7d75f905 blob + 7677dd73b3b2ce7b87ee121d2c7763c5c6bc1985 --- amused.h +++ amused.h @@ -60,9 +60,8 @@ enum imsg_type { struct imsgev { struct imsgbuf ibuf; - void (*handler)(int, short, void *); - struct event ev; - short events; + void (*handler)(int, int, void *); + int events; }; enum actions { blob - 3a1fb877284fcbb050aad27733366694a01a3137 blob + 103a329cb59ad1e055637c6f8fe491b08c0cf717 --- configure +++ configure @@ -63,8 +63,6 @@ Variables available: BACKEND audio backend to use; can be "sndio" or "alsa" LDADD generic linker flags LDADD_IMSG linker flags for libimsg - LDADD_LIBEVENT linker flags for libevent - LDADD_LIBEVENT2 linker flags for libevent2 LDADD_LIBFLAC linker flags for libflac LDADD_LIBMPG123 linker flags for libmpg123 LDADD_LIBOPUSFILE linker flags for libopusfile @@ -139,8 +137,6 @@ CFLAGS="${CFLAGS} -Wstrict-prototypes -Wmissing-declar CFLAGS="${CFLAGS} -Wno-unused-parameter -Wno-sign-compare" LDADD= LDADD_IMSG= -LDADD_LIBEVENT= -LDADD_LIBEVENT2= LDADD_LIB_FLAC= LDADD_LIB_MPG123= LDADD_LIB_VORBISFILE= @@ -244,10 +240,6 @@ while [ $# -gt 0 ]; do LDADD="$val" ;; LDADD_IMSG) LDADD_IMSG="$val" ;; - LDADD_LIBEVENT) - LDADD_LIBEVENT="$val" ;; - LDADD_LIBEVENT2) - LDADD_LIBEVENT2="$val" ;; LDADD_LIBFLAC) LDADD_LIBFLAC="$val" ;; LDADD_LIBMPG123) @@ -308,8 +300,6 @@ HAVE_GETPROGNAME= HAVE_INFTIM= HAVE_IMSG= HAVE_LANDLOCK= -HAVE_LIBEVENT= -HAVE_LIBEVENT2=0 # may not be checked, set to zero HAVE_LIB_FLAC= HAVE_LIB_MPG123= HAVE_LIB_OPUSFILE= @@ -508,9 +498,6 @@ runtest imsg IMSG "" "" "-lutil" || true runtest INFTIM INFTIM || true runtest landlock LANDLOCK || true -runtest libevent LIBEVENT "" "" "-levent" || \ -runtest libevent2 LIBEVENT2 "" "" "-levent_extra -levent_core" "libevent" || true - runtest lib_flac LIB_FLAC "" "" "-lFLAC" "flac" || true runtest lib_mpg123 LIB_MPG123 "" "" "-lmpg123" "libmpg123" || true runtest lib_opusfile LIB_OPUSFILE "" "" "-lopusfile" "opusfile" || true @@ -571,12 +558,6 @@ if [ $BACKEND != sndio ]; then # auto or alsa exit 1 fi BACKEND=alsa -fi - -if [ "${HAVE_LIBEVENT}" -eq 0 -a "${HAVE_LIBEVENT2}" -eq 0 ]; then - echo "Fatal: missing libevent" 1>&2 - echo "Fatal: missing libevent" 1>&3 - exit 1 fi if [ "${HAVE_LIB_FLAC}" -eq 0 -o \ @@ -897,25 +878,6 @@ else fi echo -if [ "${HAVE_LIBEVENT2}" -eq 1 ]; then - cat << __HEREDOC__ -#include -#include -#include -#include -#include -#include -#include -#include - -__HEREDOC__ -elif [ "${HAVE_LIBEVENT}" -eq 1 ]; then - cat << __HEREDOC__ -#include - -__HEREDOC__ -fi - cat << __HEREDOC__ #ifndef __dead # define __dead __attribute__((noreturn)) @@ -977,8 +939,7 @@ CFLAGS = ${CFLAGS} CPPFLAGS = ${CPPFLAGS} LDADD = ${LDADD} ${LDADD_IMSG} ${LDADD_LIB_FLAC} ${LDADD_LIB_MPG123} \ ${LDADD_LIB_OPUSFILE} ${LDADD_LIB_VORBISFILE} \ - ${LDADD_LIB_SOCKET} ${LDADD_LIBEVENT} ${LDADD_LIBEVENT2} \ - ${LDADD_LIB_SNDIO} ${LDADD_LIB_ASOUND} + ${LDADD_LIB_SOCKET} ${LDADD_LIB_SNDIO} ${LDADD_LIB_ASOUND} LDADD_STATIC = ${LDADD_STATIC} LDFLAGS = ${LDFLAGS} STATIC = ${STATIC} blob - 413a97373d3e4b391ffa5b665dc515ef1f145404 blob + 55013fb5da5f02362fa64295f888aee9478c4913 --- control.c +++ control.c @@ -28,12 +28,14 @@ #include #include +#include #include #include #include #include #include "amused.h" +#include "ev.h" #include "log.h" #include "control.h" #include "playlist.h" @@ -41,8 +43,6 @@ #define CONTROL_BACKLOG 5 struct { - struct event ev; - struct event evt; int fd; struct playlist play; int tx; @@ -115,26 +115,18 @@ control_listen(int fd) return (-1); } - event_set(&control_state.ev, control_state.fd, EV_READ, - control_accept, NULL); - event_add(&control_state.ev, NULL); - evtimer_set(&control_state.evt, control_accept, NULL); - + ev_add(control_state.fd, POLLIN, control_accept, NULL); return (0); } void -control_accept(int listenfd, short event, void *bula) +control_accept(int listenfd, int event, void *bula) { int connfd; socklen_t len; struct sockaddr_un sun; struct ctl_conn *c; - event_add(&control_state.ev, NULL); - if ((event & EV_TIMEOUT)) - return; - len = sizeof(sun); if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len, SOCK_CLOEXEC | SOCK_NONBLOCK)) == -1) { @@ -145,8 +137,8 @@ control_accept(int listenfd, short event, void *bula) if (errno == ENFILE || errno == EMFILE) { struct timeval evtpause = { 1, 0 }; - event_del(&control_state.ev); - evtimer_add(&control_state.evt, &evtpause); + ev_del(control_state.fd); + ev_timer(&evtpause, control_accept, NULL); } else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED) log_warn("%s: accept4", __func__); @@ -161,10 +153,8 @@ control_accept(int listenfd, short event, void *bula) imsg_init(&c->iev.ibuf, connfd); c->iev.handler = control_dispatch_imsg; - c->iev.events = EV_READ; - event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, - c->iev.handler, &c->iev); - event_add(&c->iev.ev, NULL); + c->iev.events = POLLIN; + ev_add(c->iev.ibuf.fd, c->iev.events, c->iev.handler, &c->iev); TAILQ_INSERT_TAIL(&ctl_conns, c, entry); } @@ -214,13 +204,13 @@ control_close(int fd) msgbuf_clear(&c->iev.ibuf.w); TAILQ_REMOVE(&ctl_conns, c, entry); - event_del(&c->iev.ev); + ev_del(c->iev.ibuf.fd); close(c->iev.ibuf.fd); /* Some file descriptors are available again. */ - if (evtimer_pending(&control_state.evt, NULL)) { - evtimer_del(&control_state.evt); - event_add(&control_state.ev, NULL); + if (ev_timer_pending()) { + ev_timer(NULL, NULL, NULL); + ev_add(control_state.fd, POLLIN, control_accept, NULL); } free(c); @@ -260,7 +250,7 @@ new_mode(int val, int newval) } void -control_dispatch_imsg(int fd, short event, void *bula) +control_dispatch_imsg(int fd, int event, void *bula) { struct ctl_conn *c; struct imsg imsg; @@ -273,14 +263,14 @@ control_dispatch_imsg(int fd, short event, void *bula) return; } - if (event & EV_READ) { + if (event & POLLIN) { if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || n == 0) { control_close(fd); return; } } - if (event & EV_WRITE) { + if (event & POLLOUT) { if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) { control_close(fd); return; blob - e3772bf83e82cca9e978ed3e2818dfefd4d04c45 blob + 9f1d8a0280abbe87ddad1836d83d0a0f3040d736 --- control.h +++ control.h @@ -18,7 +18,7 @@ int control_init(char *); int control_listen(int fd); -void control_accept(int, short, void *); +void control_accept(int, int, void *); void control_notify(int); -void control_dispatch_imsg(int, short, void *); +void control_dispatch_imsg(int, int, void *); int control_imsg_relay(struct imsg *); blob - 19aba59115e5a060b133cf6c79cb02357582a623 blob + 9f122590c84b79afe723e1091223dfa12112d7de --- ctl.c +++ ctl.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "amused.h" blob - /dev/null blob + 47d2115c9ce7cf6f9400f0501bc311937f3fa6af (mode 644) --- /dev/null +++ ev.c @@ -0,0 +1,259 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "ev.h" + +struct evcb { + void (*cb)(int, int, void *); + void *udata; +}; + +struct evbase { + size_t len; + + struct pollfd *pfds; + size_t pfdlen; + + struct evcb *cbs; + size_t cblen; + + int sigpipe[2]; + struct evcb sigcb; + + int timeout; + struct evcb toutcb; +}; + +static struct evbase *base; +static int ev_stop; + +static int +ev_resize(size_t len) +{ + void *t; + size_t i; + + t = recallocarray(base->pfds, base->pfdlen, len, sizeof(*base->pfds)); + if (t == NULL) + return -1; + base->pfds = t; + base->pfdlen = len; + + for (i = base->len; i < len; ++i) + base->pfds[i].fd = -1; + + t = recallocarray(base->cbs, base->cblen, len, sizeof(*base->cbs)); + if (t == NULL) + return -1; + base->cbs = t; + base->cblen = len; + + base->len = len; + return 0; +} + +int +ev_init(void) +{ + if (base != NULL) { + errno = EINVAL; + return -1; + } + + if ((base = calloc(1, sizeof(*base))) == NULL) + return -1; + + base->sigpipe[0] = -1; + base->sigpipe[1] = -1; + base->timeout = INFTIM; + + if (ev_resize(16) == -1) { + free(base->pfds); + free(base->cbs); + free(base); + base = NULL; + return -1; + } + + return 0; +} + +int +ev_add(int fd, int ev, void (*cb)(int, int, void *), void *udata) +{ + if (fd >= base->len) { + if (ev_resize(fd + 1) == -1) + return -1; + } + + base->pfds[fd].fd = fd; + base->pfds[fd].events = ev; + + base->cbs[fd].cb = cb; + base->cbs[fd].udata = udata; + + return 0; +} + +static void +ev_sigcatch(int signo) +{ + unsigned char s; + int err; + + err = errno; + + /* + * We should be able to write up to PIPE_BUF bytes without + * blocking. + */ + s = signo; + (void) write(base->sigpipe[1], &s, sizeof(s)); + + errno = err; +} + +static void +ev_sigdispatch(int fd, int ev, void *data) +{ + unsigned char signo; + + if (read(fd, &signo, sizeof(signo)) != sizeof(signo)) + return; + + base->sigcb.cb(signo, 0, base->sigcb.udata); +} + +int +ev_signal(int sig, void (*cb)(int, int, void *), void *udata) +{ + if (base->sigpipe[0] == -1) { + if (pipe2(base->sigpipe, O_NONBLOCK) == -1) + return -1; + if (ev_add(base->sigpipe[0], POLLIN, ev_sigdispatch, NULL) + == -1) + return -1; + } + + base->sigcb.cb = cb; + base->sigcb.udata = udata; + + signal(sig, ev_sigcatch); + return 0; +} + +int +ev_timer(const struct timeval *tv, void (*cb)(int, int, void*), void *udata) +{ + base->timeout = INFTIM; + if (tv) { + base->timeout = tv->tv_sec * 1000; + base->timeout += tv->tv_usec / 1000; + } + + base->toutcb.cb = cb; + base->toutcb.udata = udata; + + return 0; +} + +int +ev_timer_pending(void) +{ + return base->timeout != INFTIM; +} + +int +ev_del(int fd) +{ + if (fd >= base->len) { + errno = ERANGE; + return -1; + } + + base->pfds[fd].fd = -1; + base->pfds[fd].events = 0; + + base->cbs[fd].cb = NULL; + base->cbs[fd].udata = NULL; + + return 0; +} + +int +ev_loop(void) +{ + struct timespec elapsed, beg, end; + int n, em; + size_t i; + + while (!ev_stop) { + clock_gettime(CLOCK_MONOTONIC, &beg); + if ((n = poll(base->pfds, base->len, base->timeout)) == -1) { + if (errno != EINTR) + return -1; + } + clock_gettime(CLOCK_MONOTONIC, &end); + + timespecsub(&end, &beg, &elapsed); + em = elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000; + if (base->timeout != INFTIM) { + if (base->timeout - em < 0 || n == 0) { + base->timeout = INFTIM; + base->toutcb.cb(-1, 0, base->toutcb.udata); + } else + base->timeout -= em; + } + + for (i = 0; i < base->len && n > 0 && !ev_stop; ++i) { + if (base->pfds[i].fd == -1) + continue; + if (base->pfds[i].revents & (POLLIN|POLLOUT|POLLHUP)) { + n--; + base->cbs[i].cb(base->pfds[i].fd, + base->pfds[i].revents, + base->cbs[i].udata); + } + } + } + + return 0; +} + +void +ev_break(void) +{ + ev_stop = 1; +} blob - /dev/null blob + 7acacfe73721b2c98e67bc1df641f3c9510c1c61 (mode 644) --- /dev/null +++ ev.h @@ -0,0 +1,35 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +struct timeval; + +int ev_init(void); +int ev_add(int, int, void(*)(int, int, void *), void *); +int ev_signal(int, void(*)(int, int, void * ), void *); +int ev_timer(const struct timeval *, void(*)(int, int, void *), void *); +int ev_timer_pending(void); +int ev_del(int); +int ev_loop(void); +void ev_break(void); blob - 89cb73563fbf609ab47ee8ea2843e7755536bb01 blob + 15cea454351f145671064f4180f55456954d6f07 --- tests.c +++ tests.c @@ -848,41 +848,6 @@ main(void) return waitpid(WAIT_ANY, &st, WNOHANG) != -1; } #endif /* TEST_WAIT_ANY */ -#if TEST_LIBEVENT -#include - -int -main(void) -{ - struct event ev; - - event_set(&ev, 0, EV_READ, NULL, NULL); - event_add(&ev, NULL); - event_del(&ev); - return 0; -} -#endif /* TEST_LIBEVENT */ -#if TEST_LIBEVENT2 -#include -#include -#include -#include -#include -#include -#include -#include - -int -main(void) -{ - struct event ev; - - event_set(&ev, 0, EV_READ, NULL, NULL); - event_add(&ev, NULL); - event_del(&ev); - return 0; -} -#endif /* TEST_LIBEVENT2 */ #if TEST_LIB_FLAC #include