diff options
Diffstat (limited to 'tests/sighook.c')
-rw-r--r-- | tests/sighook.c | 126 |
1 files changed, 73 insertions, 53 deletions
diff --git a/tests/sighook.c b/tests/sighook.c index c94526e..0cb8de2 100644 --- a/tests/sighook.c +++ b/tests/sighook.c @@ -1,97 +1,117 @@ // Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD -#include "prelude.h" #include "tests.h" -#include "sighook.h" + #include "atomic.h" +#include "sighook.h" #include "thread.h" +#include "xtime.h" + +#include <stddef.h> +#include <errno.h> #include <pthread.h> #include <signal.h> -#include <sys/time.h> +/** Counts SIGALRM deliveries. */ static atomic size_t count = 0; +/** Keeps the background thread alive. */ +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static bool done = false; + /** SIGALRM handler. */ static void alrm_hook(int sig, siginfo_t *info, void *arg) { fetch_add(&count, 1, relaxed); } -/** Swap out an old hook for a new hook. */ -static int swap_hooks(struct sighook **hook) { - struct sighook *next = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); - if (!bfs_echeck(next, "sighook(SIGALRM)")) { +/** Background thread that receives signals. */ +static void *hook_thread(void *ptr) { + mutex_lock(&mutex); + while (!done) { + cond_wait(&cond, &mutex); + } + mutex_unlock(&mutex); + return NULL; +} + +/** Block a signal in this thread. */ +static int block_signal(int sig, sigset_t *old) { + sigset_t set; + if (sigemptyset(&set) != 0) { + return -1; + } + if (sigaddset(&set, sig) != 0) { + return -1; + } + + errno = pthread_sigmask(SIG_BLOCK, &set, old); + if (errno != 0) { return -1; } - sigunhook(*hook); - *hook = next; return 0; } -/** Background thread that rapidly (un)registers signal hooks. */ -static void *hook_thread(void *ptr) { +void check_sighook(void) { struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); if (!bfs_echeck(hook, "sighook(SIGALRM)")) { - return NULL; - } - - while (load(&count, relaxed) < 1000) { - if (swap_hooks(&hook) != 0) { - sigunhook(hook); - return NULL; - } + return; } + // Check that we can unregister and re-register a hook sigunhook(hook); - return &count; -} - -bool check_sighook(void) { - bool ret = true; - - struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); - ret &= bfs_echeck(hook, "sighook(SIGALRM)"); - if (!ret) { - goto done; + hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); + if (!bfs_echeck(hook, "sighook(SIGALRM)")) { + return; } - struct itimerval ival = { - .it_value = { - .tv_usec = 100, - }, - .it_interval = { - .tv_usec = 100, - }, - }; - ret &= bfs_echeck(setitimer(ITIMER_REAL, &ival, NULL) == 0); - if (!ret) { + // Create a timer that sends SIGALRM every 100 microseconds + struct timespec ival = { .tv_nsec = 100 * 1000 }; + struct timer *timer = xtimer_start(&ival); + if (!bfs_echeck(timer)) { goto unhook; } + // Create a background thread to receive signals pthread_t thread; - ret &= bfs_echeck(thread_create(&thread, NULL, hook_thread, NULL) == 0); - if (!ret) { + if (!bfs_echeck(thread_create(&thread, NULL, hook_thread, NULL) == 0)) { goto untime; } - while (ret && load(&count, relaxed) < 1000) { - ret &= swap_hooks(&hook) == 0; + // Block SIGALRM in this thread so the handler runs concurrently with + // sighook()/sigunhook() + sigset_t mask; + if (!bfs_echeck(block_signal(SIGALRM, &mask) == 0)) { + goto untime; } - void *ptr; - thread_join(thread, &ptr); - ret &= bfs_check(ptr); + // Rapidly register/unregister SIGALRM hooks + while (load(&count, relaxed) < 1000) { + struct sighook *next = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); + if (!bfs_echeck(next, "sighook(SIGALRM)")) { + break; + } -untime: - ival.it_value.tv_usec = 0; - ret &= bfs_echeck(setitimer(ITIMER_REAL, &ival, NULL) == 0); - if (!ret) { - goto unhook; + sigunhook(hook); + hook = next; } + // Quit the background thread + mutex_lock(&mutex); + done = true; + mutex_unlock(&mutex); + cond_signal(&cond); + thread_join(thread, NULL); + + // Restore the old signal mask + errno = pthread_sigmask(SIG_SETMASK, &mask, NULL); + bfs_echeck(errno == 0, "pthread_sigmask()"); +untime: + // Stop the timer + xtimer_stop(timer); unhook: + // Unregister the SIGALRM hook sigunhook(hook); -done: - return ret; } |