summaryrefslogtreecommitdiffstats
path: root/src/bar.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bar.c')
-rw-r--r--src/bar.c179
1 files changed, 47 insertions, 132 deletions
diff --git a/src/bar.c b/src/bar.c
index 6eba78f..b928373 100644
--- a/src/bar.c
+++ b/src/bar.c
@@ -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);
}