diff options
Diffstat (limited to 'src/bar.c')
-rw-r--r-- | src/bar.c | 179 |
1 files changed, 47 insertions, 132 deletions
@@ -3,11 +3,12 @@ #include "prelude.h" #include "bar.h" +#include "alloc.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" -#include "diag.h" #include "dstring.h" +#include "sighook.h" #include <errno.h> #include <fcntl.h> #include <signal.h> @@ -16,82 +17,26 @@ #include <string.h> #include <sys/ioctl.h> -/** - * Sentinel values for bfs_bar::refcount. - */ -enum { - /** The bar is currently uninitialized. */ - BFS_BAR_UNINIT = 0, - /** The bar is being initialized/destroyed. */ - BFS_BAR_BUSY = 1, - /** Values >= this are valid refcounts. */ - BFS_BAR_READY = 2, -}; - struct bfs_bar { - /** - * A file descriptor to the TTY. Must be atomic because signal handlers - * can race with bfs_bar_hide(). - */ - atomic int fd; - /** A reference count protecting `fd`. */ - atomic unsigned int refcount; - - /** The width of the TTY. */ + int fd; atomic unsigned int width; - /** The height of the TTY. */ atomic unsigned int height; -}; -/** The global status bar instance. */ -static struct bfs_bar the_bar = { - .fd = -1, + struct sighook *exit_hook; + struct sighook *winch_hook; }; -/** Try to acquire a reference to bar->fd. */ -static int bfs_bar_getfd(struct bfs_bar *bar) { - unsigned int refs = load(&bar->refcount, relaxed); - do { - if (refs < BFS_BAR_READY) { - errno = EAGAIN; - return -1; - } - } while (!compare_exchange_weak(&bar->refcount, &refs, refs + 1, acquire, relaxed)); - - return load(&bar->fd, relaxed); -} - -/** Release a reference to bar->fd. */ -static void bfs_bar_putfd(struct bfs_bar *bar) { - int fd = load(&bar->fd, relaxed); - - unsigned int refs = fetch_sub(&bar->refcount, 1, release); - bfs_assert(refs >= BFS_BAR_READY); - - if (refs == 2) { - close_quietly(fd); - store(&bar->fd, -1, relaxed); - store(&bar->refcount, BFS_BAR_UNINIT, release); - } -} - /** Get the terminal size, if possible. */ static int bfs_bar_getsize(struct bfs_bar *bar) { #ifdef TIOCGWINSZ - int fd = bfs_bar_getfd(bar); - if (fd < 0) { - return -1; - } - struct winsize ws; - int ret = ioctl(fd, TIOCGWINSZ, &ws); - if (ret == 0) { - store(&bar->width, ws.ws_col, relaxed); - store(&bar->height, ws.ws_row, relaxed); + if (ioctl(bar->fd, TIOCGWINSZ, &ws) != 0) { + return -1; } - bfs_bar_putfd(bar); - return ret; + store(&bar->width, ws.ws_col, relaxed); + store(&bar->height, ws.ws_row, relaxed); + return 0; #else errno = ENOTSUP; return -1; @@ -100,14 +45,7 @@ static int bfs_bar_getsize(struct bfs_bar *bar) { /** Write a string to the status bar (async-signal-safe). */ static int bfs_bar_write(struct bfs_bar *bar, const char *str, size_t len) { - int fd = bfs_bar_getfd(bar); - if (fd < 0) { - return -1; - } - - int ret = xwrite(fd, str, len) == len ? 0 : -1; - bfs_bar_putfd(bar); - return ret; + return xwrite(bar->fd, str, len) == len ? 0 : -1; } /** Write a string to the status bar (async-signal-safe). */ @@ -160,13 +98,10 @@ static int bfs_bar_resize(struct bfs_bar *bar) { #ifdef SIGWINCH /** SIGWINCH handler. */ -static void sighand_winch(int sig) { - int error = errno; - - bfs_bar_getsize(&the_bar); - bfs_bar_resize(&the_bar); - - errno = error; +static void bfs_bar_sigwinch(int sig, siginfo_t *info, void *arg) { + struct bfs_bar *bar = arg; + bfs_bar_getsize(bar); + bfs_bar_resize(bar); } #endif @@ -181,19 +116,9 @@ static int bfs_bar_reset(struct bfs_bar *bar) { } /** Signal handler for process-terminating signals. */ -static void sighand_reset(int sig) { - bfs_bar_reset(&the_bar); - raise(sig); -} - -/** Register sighand_reset() for a signal. */ -static void reset_before_death_by(int sig) { - struct sigaction sa = { - .sa_handler = sighand_reset, - .sa_flags = SA_RESETHAND, - }; - sigemptyset(&sa.sa_mask); - sigaction(sig, &sa, NULL); +static void bfs_bar_sigexit(int sig, siginfo_t *info, void *arg) { + struct bfs_bar *bar = arg; + bfs_bar_reset(bar); } /** printf() to the status bar with a single write(). */ @@ -214,54 +139,48 @@ static int bfs_bar_printf(struct bfs_bar *bar, const char *format, ...) { } struct bfs_bar *bfs_bar_show(void) { - unsigned int refs = BFS_BAR_UNINIT; - if (!compare_exchange_strong(&the_bar.refcount, &refs, BFS_BAR_BUSY, acq_rel, relaxed)) { - errno = EBUSY; - goto fail; + struct bfs_bar *bar = ALLOC(struct bfs_bar); + if (!bar) { + return NULL; } char term[L_ctermid]; ctermid(term); if (strlen(term) == 0) { errno = ENOTTY; - goto unref; + goto fail; } - int fd = open(term, O_RDWR | O_CLOEXEC); - if (fd < 0) { - goto unref; + bar->fd = open(term, O_RDWR | O_CLOEXEC); + if (bar->fd < 0) { + goto fail; } - store(&the_bar.fd, fd, relaxed); - store(&the_bar.refcount, BFS_BAR_READY, release); - if (bfs_bar_getsize(&the_bar) != 0) { - goto put; + if (bfs_bar_getsize(bar) != 0) { + goto fail_close; } - reset_before_death_by(SIGABRT); - reset_before_death_by(SIGINT); - reset_before_death_by(SIGPIPE); - reset_before_death_by(SIGQUIT); - reset_before_death_by(SIGTERM); + bar->exit_hook = atsigexit(bfs_bar_sigexit, bar); + if (!bar->exit_hook) { + goto fail_close; + } #ifdef SIGWINCH - struct sigaction sa = { - .sa_handler = sighand_winch, - .sa_flags = SA_RESTART, - }; - sigemptyset(&sa.sa_mask); - sigaction(SIGWINCH, &sa, NULL); + bar->winch_hook = sighook(SIGWINCH, bfs_bar_sigwinch, bar, 0); + if (!bar->winch_hook) { + goto fail_hook; + } #endif - bfs_bar_resize(&the_bar); - return &the_bar; + bfs_bar_resize(bar); + return bar; -put: - bfs_bar_putfd(&the_bar); - return NULL; -unref: - store(&the_bar.refcount, 0, release); +fail_hook: + sigunhook(bar->exit_hook); +fail_close: + close_quietly(bar->fd); fail: + free(bar); return NULL; } @@ -289,15 +208,11 @@ void bfs_bar_hide(struct bfs_bar *bar) { return; } - signal(SIGABRT, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGPIPE, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGTERM, SIG_DFL); -#ifdef SIGWINCH - signal(SIGWINCH, SIG_DFL); -#endif + sigunhook(bar->winch_hook); + sigunhook(bar->exit_hook); bfs_bar_reset(bar); - bfs_bar_putfd(bar); + + xclose(bar->fd); + free(bar); } |