summaryrefslogtreecommitdiffstats
path: root/tests/sighook.c
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2024-05-13 13:12:40 -0400
committerTavian Barnes <tavianator@tavianator.com>2024-05-16 11:42:26 -0400
commit375caac5019aa174cf33b537335a085f43a2407d (patch)
tree61afaa4b902750efaffb36567f04ea76c4d18768 /tests/sighook.c
parentc964524f18fc5c3b7baf5a3d0eac0980f17d3cf0 (diff)
downloadbfs-375caac5019aa174cf33b537335a085f43a2407d.tar.xz
sighook: New utilities for hooking signals
This allows multiple hooks to be installed for a single signal.
Diffstat (limited to 'tests/sighook.c')
-rw-r--r--tests/sighook.c97
1 files changed, 97 insertions, 0 deletions
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;
+}