summaryrefslogtreecommitdiffstats
path: root/tests/sighook.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sighook.c')
-rw-r--r--tests/sighook.c126
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;
}