summaryrefslogtreecommitdiff
path: root/regress
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2019-05-13 19:40:23 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2019-05-13 19:40:23 +0000
commit594262b94f2e5f0087473495e5afd7650e31f1c4 (patch)
tree07f1d7700b7ee878e3e9ad44a5f279dc2befcc8d /regress
parent2f9098949367fc1d23961282191f3e641f89e957 (diff)
Test the interaction of signals with multiple posix threads. It
covers blocking with signal mask, killing process or thread, invoking handler or waiting for signal.
Diffstat (limited to 'regress')
-rw-r--r--regress/sys/kern/sigpthread/LICENSE15
-rw-r--r--regress/sys/kern/sigpthread/Makefile447
-rw-r--r--regress/sys/kern/sigpthread/README27
-rw-r--r--regress/sys/kern/sigpthread/sigpthread.c282
4 files changed, 771 insertions, 0 deletions
diff --git a/regress/sys/kern/sigpthread/LICENSE b/regress/sys/kern/sigpthread/LICENSE
new file mode 100644
index 00000000000..897cd3e6996
--- /dev/null
+++ b/regress/sys/kern/sigpthread/LICENSE
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
diff --git a/regress/sys/kern/sigpthread/Makefile b/regress/sys/kern/sigpthread/Makefile
new file mode 100644
index 00000000000..d921d768526
--- /dev/null
+++ b/regress/sys/kern/sigpthread/Makefile
@@ -0,0 +1,447 @@
+# $OpenBSD: Makefile,v 1.1.1.1 2019/05/13 19:40:22 bluhm Exp $
+
+PROG = sigpthread
+WARNINGS = yes
+LDADD = -lpthread
+DPADD = ${LIBPTHREAD}
+CLEANFILES += out
+
+# first test signal delivery while they are blocked
+
+.for t in 0 1 2
+
+REGRESS_TARGETS += run-block-thread-3-unblock-$t
+run-block-thread-3-unblock-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill process
+ # suspend threads until signaled
+ # unblock thread $t
+ # handle signal
+ ./sigpthread -b -t 3 -u $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-sleep-main-unblock-$t
+run-block-thread-3-sleep-main-unblock-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # suspend threads until signaled
+ # sleep in main thread, signal should be received while suspended
+ # kill process
+ # unblock thread $t
+ # handle signal
+ ./sigpthread -b -s -t 3 -u $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-unblock-$t-sleep-thread
+run-block-thread-3-sleep-thread-unblock-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill process
+ # sleep in threads, signal should be pending when suspending
+ # suspend threads until signaled
+ # unblock thread $t
+ # handle signal
+ ./sigpthread -b -S -t 3 -u $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-sleep-unblock-unblock-$t
+run-block-thread-3-sleep-unblock-unblock-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill process
+ # suspend threads until signaled
+ # sleep in thread $t, others should be exited when unblocking
+ # unblock thread $t
+ # handle signal
+ ./sigpthread -b -t 3 -U -u $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-kill-$t-unblock-$t
+run-block-thread-3-kill-$t-unblock-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill thread $t
+ # suspend threads until signaled
+ # unblock thread $t
+ # handle signal
+ ./sigpthread -b -k $t -t 3 -u $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-sleep-main-kill-$t-unblock-$t
+run-block-thread-3-sleep-main-kill-$t-unblock-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # suspend threads until signaled
+ # sleep in main thread, signal should be received while suspended
+ # kill thread $t
+ # unblock thread $t
+ # handle signal
+ ./sigpthread -b -k $t -s -t 3 -u $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-kill-$t-sleep-thread-unblock-$t
+run-block-thread-3-kill-$t-sleep-thread-unblock-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill thread $t
+ # sleep in threads, signal should be pending when suspending
+ # suspend threads until signaled
+ # unblock thread $t
+ # handle signal
+ ./sigpthread -b -k $t -S -t 3 -u $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-kill-$t-unblock-$t-sleep-unblock
+run-block-thread-3-kill-$t-unblock-$t-sleep-unblock:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill thread $t
+ # suspend threads until signaled
+ # sleep in thread $t, others should be exited when unblocking
+ # unblock thread $t
+ # handle signal
+ ./sigpthread -b -k $t -t 3 -U -u $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-kill-$t
+run-block-thread-3-kill-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill thread $t
+ # suspend threads until signaled
+ # unblock all threads
+ # handle signal
+ ./sigpthread -b -k $t -t 3 >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-sleep-main-kill-$t
+run-block-thread-3-sleep-main-kill-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # suspend threads until signaled
+ # sleep in main thread, signal should be received while suspended
+ # kill thread $t
+ # unblock all threads
+ # handle signal
+ ./sigpthread -b -k $t -s -t 3 >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-kill-$t-sleep-thread
+run-block-thread-3-kill-$t-sleep-thread:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill thread $t
+ # sleep in threads, signal should be pending when suspending
+ # suspend threads until signaled
+ # unblock all threads
+ # handle signal
+ ./sigpthread -b -k $t -S -t 3 >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-kill-$t-sleep-unblock
+run-block-thread-3-kill-$t-sleep-unblock:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill thread $t
+ # suspend threads until signaled
+ # sleep in all threads
+ # unblock all threads
+ # handle signal
+ ./sigpthread -b -k $t -t 3 -U >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+.endfor
+
+REGRESS_TARGETS += run-block-thread-3
+run-block-thread-3:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill process
+ # suspend threads until signaled
+ # unblock all threads
+ # handle signal
+ ./sigpthread -b -t 3 >out
+ grep 'signal [0-2]' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-sleep-main
+run-block-thread-3-sleep-main:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # suspend threads until signaled
+ # sleep in main thread, signal should be received while suspended
+ # kill process
+ # unblock all threads
+ # handle signal
+ ./sigpthread -b -s -t 3 >out
+ grep 'signal [0-2]' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-sleep-thread
+run-block-thread-3-sleep-thread:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill process
+ # sleep in threads, signal should be pending when suspending
+ # suspend threads until signaled
+ # unblock all threads
+ # handle signal
+ ./sigpthread -b -S -t 3 >out
+ grep 'signal [0-2]' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-sleep-unblock
+run-block-thread-3-sleep-unblock:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill process
+ # suspend threads until signaled
+ # sleep in all threads
+ # unblock all threads
+ # handle signal
+ ./sigpthread -b -t 3 -U >out
+ grep 'signal [0-2]' out
+ test `wc -l <out` = 1
+
+# check what happens if signals are not blocked but delivered immediately
+
+.for t in 0 1 2
+
+REGRESS_TARGETS += run-thread-3-kill-$t
+run-thread-3-kill-$t:
+ @echo '\n======== $@ ========'
+ # run 3 threads
+ # kill thread $t
+ # handle signal
+ # suspend threads until signaled
+ ./sigpthread -k $t -t 3 >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-thread-3-sleep-main-kill-$t
+run-thread-3-sleep-main-kill-$t:
+ @echo '\n======== $@ ========'
+ # run 3 threads
+ # suspend threads until signaled
+ # sleep in main thread, signal should be received while suspended
+ # kill thread $t
+ # handle signal
+ ./sigpthread -k $t -s -t 3 >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-thread-3-kill-$t-sleep-thread
+run-thread-3-kill-$t-sleep-thread:
+ @echo '\n======== $@ ========'
+ # run 3 threads
+ # kill thread $t
+ # sleep in threads, signal should be received while sleeping
+ # handle signal
+ # suspend threads until signaled
+ ./sigpthread -k $t -S -t 3 >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-thread-3-kill-$t-sleep-unblock
+run-thread-3-kill-$t-sleep-unblock:
+ @echo '\n======== $@ ========'
+ # run 3 threads
+ # kill thread $t
+ # handle signal
+ # suspend threads until signaled
+ # sleep in all threads
+ ./sigpthread -k $t -t 3 -U >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+.endfor
+
+REGRESS_TARGETS += run-thread-3
+run-thread-3:
+ @echo '\n======== $@ ========'
+ # run 3 threads
+ # kill process
+ # handle signal
+ # suspend threads until signaled
+ ./sigpthread -t 3 >out
+ grep 'signal [0-2]' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-thread-3-sleep-main
+run-thread-3-sleep-main:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # suspend threads until signaled
+ # sleep in main thread, signal should be received while suspended
+ # kill process
+ # handle signal
+ ./sigpthread -s -t 3 >out
+ grep 'signal [0-2]' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-thread-3-sleep-thread
+run-thread-3-sleep-thread:
+ @echo '\n======== $@ ========'
+ # run 3 threads
+ # kill process
+ # sleep in threads, signal should be received while sleeping
+ # handle signal
+ # suspend threads until signaled
+ ./sigpthread -S -t 3 >out
+ grep 'signal [0-2]' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-thread-3-sleep-unblock
+run-thread-3-sleep-unblock:
+ @echo '\n======== $@ ========'
+ # run 3 threads
+ # kill process
+ # handle signal
+ # suspend threads until signaled
+ # sleep in all threads
+ ./sigpthread -t 3 -U >out
+ grep 'signal [0-2]' out
+ test `wc -l <out` = 1
+
+# signals are blocked and received by sigwait
+
+.for t in 0 1 2
+
+REGRESS_TARGETS += run-block-thread-3-waiter-$t
+run-block-thread-3-waiter-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill process
+ # wait for signal in thread $t
+ # suspend threads until signaled
+ ./sigpthread -b -t 3 -w $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-sleep-main-waiter-$t
+run-block-thread-3-sleep-main-waiter-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # wait for signal in thread $t
+ # suspend threads until signaled
+ # sleep in main thread, signal should be received while waiting
+ # kill process
+ ./sigpthread -b -s -t 3 -w $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-waiter-$t-sleep-thread
+run-block-thread-3-sleep-thread-waiter-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill process
+ # sleep in threads, signal should be pending when waiting
+ # wait for signal in thread $t
+ # suspend threads until signaled
+ ./sigpthread -b -S -t 3 -w $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-kill-$t-waiter-$t
+run-block-thread-3-kill-$t-waiter-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill thread $t
+ # wait for signal in thread $t
+ # suspend threads until signaled
+ ./sigpthread -b -k $t -t 3 -w $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-sleep-main-kill-$t-waiter-$t
+run-block-thread-3-sleep-main-kill-$t-waiter-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # wait for signal in thread $t
+ # suspend threads until signaled
+ # sleep in main thread, signal should be received while waiting
+ # kill thread $t
+ ./sigpthread -b -k $t -s -t 3 -w $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-3-kill-$t-sleep-thread-waiter-$t
+run-block-thread-3-kill-$t-sleep-thread-waiter-$t:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 3 threads
+ # kill thread $t
+ # sleep in threads, signal should be pending when waiting
+ # wait for signal in thread $t
+ # suspend threads until signaled
+ ./sigpthread -b -k $t -S -t 3 -w $t >out
+ grep 'signal $t' out
+ test `wc -l <out` = 1
+
+.endfor
+
+# simple tests with much more threads
+
+REGRESS_TARGETS += run-block-thread-100-unblock-23
+run-block-thread-100-unblock-23:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 100 threads
+ # kill process
+ # suspend threads until signaled
+ # unblock thread 23
+ # handle signal
+ ./sigpthread -b -t 100 -u 23 >out
+ grep 'signal 23' out
+ test `wc -l <out` = 1
+
+REGRESS_TARGETS += run-block-thread-100-waiter-42
+run-block-thread-100-waiter-42:
+ @echo '\n======== $@ ========'
+ # block signal
+ # run 100 threads
+ # kill process
+ # wait for signal in thread 42
+ # suspend threads until signaled
+ ./sigpthread -b -t 100 -w 42 >out
+ grep 'signal 42' out
+ test `wc -l <out` = 1
+
+${REGRESS_TARGETS}: ${PROG}
+
+.include <bsd.regress.mk>
diff --git a/regress/sys/kern/sigpthread/README b/regress/sys/kern/sigpthread/README
new file mode 100644
index 00000000000..ec286ce8fda
--- /dev/null
+++ b/regress/sys/kern/sigpthread/README
@@ -0,0 +1,27 @@
+Test the interaction of signals with multiple posix threads.
+
+Signal SIGUSR1 is used for thread coordination, SIGUSR2 to test
+signal delivery. First SIGUSR1 and SIGUSR2 get blocked. Then a
+given number of threads are created, the main thread is also counted
+as a thread.
+
+Signal SIGUSR2 is send to the process with kill(2), but it is not
+delivered yet as it is blocked in all threads. Meanwhile the threads
+wait for SIGUSR1 in sigsuspend(2). This enforces that SIGUSR2 is
+marked as pending at the process. To continue, SIGUSR1 is sent to
+all threads and they wake up. Only one thread is configured to
+handle SIGUSR2, this one unblocks it with pthread_sigmask(3). The
+signal should be delivered immediately, the handler records it.
+
+The test is considered successful if the thread that unblocks SIGUSR2
+actually handles it.
+
+To test different race conditions, sleeps can be inserted. If the
+kill(2) is delayed, SIGUSR2 hits the threads when they are in
+sigsuspend(2). If the sleep is before sigsuspend(2), the threads
+are in nanosleep(2). The unblocking pthread_sigmask(3) can be
+delayed so that the other threads have been joined already.
+
+It is also possible to avoid blocking the signals and check which
+handler catches it. Alternatively sigwait(3) can be used to test
+signal reception.
diff --git a/regress/sys/kern/sigpthread/sigpthread.c b/regress/sys/kern/sigpthread/sigpthread.c
new file mode 100644
index 00000000000..6c59e94e96d
--- /dev/null
+++ b/regress/sys/kern/sigpthread/sigpthread.c
@@ -0,0 +1,282 @@
+/* $OpenBSD: sigpthread.c,v 1.1.1.1 2019/05/13 19:40:22 bluhm Exp $ */
+/*
+ * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void __dead usage(void);
+void handler(int);
+void *runner(void *);
+
+void __dead
+usage(void)
+{
+ fprintf(stderr, "sigpthread [-bSsU] [-k kill] -t threads [-u unblock] "
+ "[-w waiter]\n"
+ " -b block signal to make it pending\n"
+ " -k kill thread to kill, else process\n"
+ " -S sleep in each thread before suspend\n"
+ " -s sleep in main before kill\n"
+ " -t threads number of threads to run\n"
+ " -U sleep in thread before unblock\n"
+ " -u unblock thread to unblock, else unblock all\n"
+ " -w waiter use sigwait in thread\n"
+ );
+ exit(1);
+}
+
+int blocksignal = 0;
+int threadmax, threadunblock = -1, threadwaiter = -1;
+int sleepthread, sleepmain, sleepunblock;
+sigset_t set, oset;
+pthread_t *threads;
+volatile sig_atomic_t *signaled;
+
+int
+main(int argc, char *argv[])
+{
+ struct sigaction act;
+ int ch, ret, tnum, threadkill = -1;
+ long arg;
+ void *val;
+ const char *errstr;
+
+ while ((ch = getopt(argc, argv, "bk:Sst:Uu:w:")) != -1) {
+ switch (ch) {
+ case 'b':
+ blocksignal = 1;
+ break;
+ case 'k':
+ threadkill = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "thread to kill is %s: %s",
+ errstr, optarg);
+ break;
+ case 'S':
+ sleepthread = 1;
+ break;
+ case 's':
+ sleepmain = 1;
+ break;
+ case 't':
+ threadmax = strtonum(optarg, 1, INT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "number of threads is %s: %s",
+ errstr, optarg);
+ break;
+ case 'U':
+ sleepunblock = 1;
+ break;
+ case 'u':
+ threadunblock = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "thread to unblock is %s: %s",
+ errstr, optarg);
+ break;
+ case 'w':
+ threadwaiter = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "thread to wait is %s: %s",
+ errstr, optarg);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0)
+ errx(1, "more arguments than expected");
+ if (threadmax == 0)
+ errx(1, "number of threads required");
+ if (threadkill >= threadmax)
+ errx(1, "thread to kill greater than number of threads");
+ if (threadunblock >= threadmax)
+ errx(1, "thread to unblock greater than number of threads");
+ if (threadwaiter >= threadmax)
+ errx(1, "thread to wait greater than number of threads");
+ if (!blocksignal && threadunblock >= 0)
+ errx(1, "do not unblock thread without blocked signals");
+ if (!blocksignal && threadwaiter >= 0)
+ errx(1, "do not wait in thread without blocked signals");
+ if (threadunblock >= 0 && threadwaiter >= 0)
+ errx(1, "do not unblock and wait together");
+ if (sleepunblock && threadwaiter >= 0)
+ errx(1, "do not sleep before unblock and wait together");
+
+ /* Make sure that we do not hang forever. */
+ ret = alarm(10);
+ if (ret == -1)
+ err(1, "alarm");
+
+ if (sigemptyset(&set) == -1)
+ err(1, "sigemptyset");
+ if (sigaddset(&set, SIGUSR1) == -1)
+ err(1, "sigaddset");
+ /* Either deliver SIGUSR2 immediately, or mark it pending. */
+ if (blocksignal) {
+ if (sigaddset(&set, SIGUSR2) == -1)
+ err(1, "sigaddset");
+ }
+ /* Block both SIGUSR1 and SIGUSR2 with set. */
+ if (sigprocmask(SIG_BLOCK, &set, &oset) == -1)
+ err(1, "sigprocmask");
+
+ /* Prepare to wait for SIGUSR1, but block SIGUSR2 with oset. */
+ if (sigaddset(&oset, SIGUSR2) == -1)
+ err(1, "sigaddset");
+ /* Unblock or wait for SIGUSR2 */
+ if (sigemptyset(&set) == -1)
+ err(1, "sigemptyset");
+ if (sigaddset(&set, SIGUSR2) == -1)
+ err(1, "sigaddset");
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = handler;
+ if (sigaction(SIGUSR1, &act, NULL) == -1)
+ err(1, "sigaction SIGUSR1");
+ if (sigaction(SIGUSR2, &act, NULL) == -1)
+ err(1, "sigaction SIGUSR2");
+
+ signaled = calloc(threadmax, sizeof(*signaled));
+ if (signaled == NULL)
+ err(1, "calloc signaled");
+ threads = calloc(threadmax, sizeof(*threads));
+ if (threads == NULL)
+ err(1, "calloc threads");
+
+ for (tnum = 1; tnum < threadmax; tnum++) {
+ arg = tnum;
+ errno = pthread_create(&threads[tnum], NULL, runner,
+ (void *)arg);
+ if (errno)
+ err(1, "pthread_create %d", tnum);
+ }
+ /* Handle the main thread like thread 0. */
+ threads[0] = pthread_self();
+
+ /* Test what happens if thread is running when killed. */
+ if (sleepmain)
+ sleep(1);
+
+ /* All threads are still alive. */
+ if (threadkill < 0) {
+ if (kill(getpid(), SIGUSR2) == -1)
+ err(1, "kill SIGUSR2");
+ } else {
+ errno = pthread_kill(threads[threadkill], SIGUSR2);
+ if (errno)
+ err(1, "pthread_kill %d SIGUSR2", tnum);
+ }
+
+ /* Sending SIGUSR1 means threads can continue and finish. */
+ for (tnum = 0; tnum < threadmax; tnum++) {
+ errno = pthread_kill(threads[tnum], SIGUSR1);
+ if (errno)
+ err(1, "pthread_kill %d SIGUSR1", tnum);
+ }
+
+ val = runner(0);
+ ret = (int)val;
+
+ for (tnum = 1; tnum < threadmax; tnum++) {
+ errno = pthread_join(threads[tnum], &val);
+ if (errno)
+ err(1, "pthread_join %d", tnum);
+ ret = (int)val;
+ if (ret)
+ errx(1, "pthread %d returned %d", tnum, ret);
+ }
+ free(threads);
+
+ for (tnum = 0; tnum < threadmax; tnum++) {
+ int i;
+
+ for (i = 0; i < signaled[tnum]; i++)
+ printf("signal %d\n", tnum);
+ }
+ free((void *)signaled);
+
+ return 0;
+}
+
+void
+handler(int sig)
+{
+ pthread_t tid;
+ int tnum;
+
+ tid = pthread_self();
+ for (tnum = 0; tnum < threadmax; tnum++) {
+ if (tid == threads[tnum])
+ break;
+ }
+ switch (sig) {
+ case SIGUSR1:
+ break;
+ case SIGUSR2:
+ signaled[tnum]++;
+ break;
+ default:
+ errx(1, "unexpected signal %d thread %d", sig, tnum);
+ }
+}
+
+void *
+runner(void *arg)
+{
+ int tnum = (int)arg;
+
+ /* Test what happens if thread is running when killed. */
+ if (sleepthread)
+ sleep(1);
+
+ if (tnum == threadwaiter) {
+ int sig;
+
+ if (sigwait(&set, &sig) != 0)
+ err(1, "sigwait thread %d", tnum);
+ if (sig != SIGUSR2)
+ errx(1, "unexpected signal %d thread %d", sig, tnum);
+ signaled[tnum]++;
+ }
+
+ /*
+ * Wait for SIGUSER1, continue to block SIGUSER2.
+ * The thread is keeps running until it gets SIGUSER1.
+ */
+ if (sigsuspend(&oset) != -1 || errno != EINTR)
+ err(1, "sigsuspend thread %d", tnum);
+ if ((threadunblock < 0 || tnum == threadunblock) && threadwaiter < 0) {
+ /* Test what happens if other threads exit before unblock. */
+ if (sleepunblock)
+ sleep(1);
+
+ /* Also unblock SIGUSER2, if this thread should get it. */
+ if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) == -1)
+ err(1, "pthread_sigmask thread %d", tnum);
+ }
+
+ return (void *)0;
+}