diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2024-05-13 13:12:40 -0400 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2024-05-16 11:42:26 -0400 |
commit | 375caac5019aa174cf33b537335a085f43a2407d (patch) | |
tree | 61afaa4b902750efaffb36567f04ea76c4d18768 /tests | |
parent | c964524f18fc5c3b7baf5a3d0eac0980f17d3cf0 (diff) | |
download | bfs-375caac5019aa174cf33b537335a085f43a2407d.tar.xz |
sighook: New utilities for hooking signals
This allows multiple hooks to be installed for a single signal.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/main.c | 1 | ||||
-rw-r--r-- | tests/sighook.c | 97 | ||||
-rw-r--r-- | tests/tests.h | 3 |
3 files changed, 101 insertions, 0 deletions
diff --git a/tests/main.c b/tests/main.c index 429772b..58def32 100644 --- a/tests/main.c +++ b/tests/main.c @@ -116,6 +116,7 @@ int main(int argc, char *argv[]) { run_test(&ctx, "bfstd", check_bfstd); run_test(&ctx, "bit", check_bit); run_test(&ctx, "ioq", check_ioq); + run_test(&ctx, "sighook", check_sighook); run_test(&ctx, "trie", check_trie); run_test(&ctx, "xspawn", check_xspawn); run_test(&ctx, "xtime", check_xtime); diff --git a/tests/sighook.c b/tests/sighook.c new file mode 100644 index 0000000..e5dd47f --- /dev/null +++ b/tests/sighook.c @@ -0,0 +1,97 @@ +// Copyright © Tavian Barnes <tavianator@tavianator.com> +// SPDX-License-Identifier: 0BSD + +#include "prelude.h" +#include "tests.h" +#include "sighook.h" +#include "atomic.h" +#include "thread.h" +#include <pthread.h> +#include <signal.h> +#include <sys/time.h> + +static atomic size_t count = 0; + +/** 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_pcheck(next, "sighook(SIGALRM)")) { + return -1; + } + + sigunhook(*hook); + *hook = next; + return 0; +} + +/** Background thread that rapidly (un)registers signal hooks. */ +static void *hook_thread(void *ptr) { + struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); + if (!bfs_pcheck(hook, "sighook(SIGALRM)")) { + return NULL; + } + + while (load(&count, relaxed) < 1000) { + if (swap_hooks(&hook) != 0) { + sigunhook(hook); + return NULL; + } + } + + sigunhook(hook); + return &count; +} + +bool check_sighook(void) { + bool ret = true; + + struct sighook *hook = sighook(SIGALRM, alrm_hook, NULL, SH_CONTINUE); + ret &= bfs_pcheck(hook, "sighook(SIGALRM)"); + if (!ret) { + goto done; + } + + struct itimerval ival = { + .it_value = { + .tv_usec = 100, + }, + .it_interval = { + .tv_usec = 100, + }, + }; + ret &= bfs_pcheck(setitimer(ITIMER_REAL, &ival, NULL) == 0); + if (!ret) { + goto unhook; + } + + pthread_t thread; + ret &= bfs_pcheck(thread_create(&thread, NULL, hook_thread, NULL) == 0); + if (!ret) { + goto untime; + } + + while (ret && load(&count, relaxed) < 1000) { + ret &= swap_hooks(&hook) == 0; + } + + void *ptr; + thread_join(thread, &ptr); + ret &= bfs_check(ptr); + +untime: + ival.it_value.tv_usec = 0; + ret &= bfs_pcheck(setitimer(ITIMER_REAL, &ival, NULL) == 0); + if (!ret) { + goto unhook; + } + +unhook: + sigunhook(hook); +done: + return ret; +} diff --git a/tests/tests.h b/tests/tests.h index 9078938..d26a5a0 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -26,6 +26,9 @@ bool check_bit(void); /** I/O queue tests. */ bool check_ioq(void); +/** Signal hook tests. */ +bool check_sighook(void); + /** Trie tests. */ bool check_trie(void); |