diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2019-05-13 19:40:23 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2019-05-13 19:40:23 +0000 |
commit | 594262b94f2e5f0087473495e5afd7650e31f1c4 (patch) | |
tree | 07f1d7700b7ee878e3e9ad44a5f279dc2befcc8d /regress | |
parent | 2f9098949367fc1d23961282191f3e641f89e957 (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/LICENSE | 15 | ||||
-rw-r--r-- | regress/sys/kern/sigpthread/Makefile | 447 | ||||
-rw-r--r-- | regress/sys/kern/sigpthread/README | 27 | ||||
-rw-r--r-- | regress/sys/kern/sigpthread/sigpthread.c | 282 |
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; +} |