1*5a9255e7Sbluhm /* $OpenBSD: sigpthread.c,v 1.2 2021/07/06 11:50:34 bluhm Exp $ */
26bcfccdaSmpi /*
36bcfccdaSmpi * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
46bcfccdaSmpi *
56bcfccdaSmpi * Permission to use, copy, modify, and distribute this software for any
66bcfccdaSmpi * purpose with or without fee is hereby granted, provided that the above
76bcfccdaSmpi * copyright notice and this permission notice appear in all copies.
86bcfccdaSmpi *
96bcfccdaSmpi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
106bcfccdaSmpi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
116bcfccdaSmpi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
126bcfccdaSmpi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
136bcfccdaSmpi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
146bcfccdaSmpi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
156bcfccdaSmpi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166bcfccdaSmpi */
176bcfccdaSmpi
186bcfccdaSmpi #include <err.h>
196bcfccdaSmpi #include <errno.h>
206bcfccdaSmpi #include <limits.h>
216bcfccdaSmpi #include <pthread.h>
226bcfccdaSmpi #include <signal.h>
236bcfccdaSmpi #include <stdio.h>
246bcfccdaSmpi #include <stdlib.h>
256bcfccdaSmpi #include <string.h>
266bcfccdaSmpi #include <unistd.h>
276bcfccdaSmpi
286bcfccdaSmpi void __dead usage(void);
296bcfccdaSmpi void handler(int);
306bcfccdaSmpi void *runner(void *);
316bcfccdaSmpi
326bcfccdaSmpi void __dead
usage(void)336bcfccdaSmpi usage(void)
346bcfccdaSmpi {
356bcfccdaSmpi fprintf(stderr, "sigpthread [-bSsU] [-k kill] -t threads [-u unblock] "
366bcfccdaSmpi "[-w waiter]\n"
376bcfccdaSmpi " -b block signal to make it pending\n"
386bcfccdaSmpi " -k kill thread to kill, else process\n"
396bcfccdaSmpi " -S sleep in each thread before suspend\n"
406bcfccdaSmpi " -s sleep in main before kill\n"
416bcfccdaSmpi " -t threads number of threads to run\n"
426bcfccdaSmpi " -U sleep in thread before unblock\n"
436bcfccdaSmpi " -u unblock thread to unblock, else unblock all\n"
446bcfccdaSmpi " -w waiter use sigwait in thread\n"
456bcfccdaSmpi );
466bcfccdaSmpi exit(1);
476bcfccdaSmpi }
486bcfccdaSmpi
496bcfccdaSmpi int blocksignal = 0;
506bcfccdaSmpi int threadmax, threadunblock = -1, threadwaiter = -1;
516bcfccdaSmpi int sleepthread, sleepmain, sleepunblock;
526bcfccdaSmpi sigset_t set, oset;
536bcfccdaSmpi pthread_t *threads;
546bcfccdaSmpi volatile sig_atomic_t *signaled;
556bcfccdaSmpi
566bcfccdaSmpi int
main(int argc,char * argv[])576bcfccdaSmpi main(int argc, char *argv[])
586bcfccdaSmpi {
596bcfccdaSmpi struct sigaction act;
606bcfccdaSmpi int ch, ret, tnum, threadkill = -1;
616bcfccdaSmpi long arg;
626bcfccdaSmpi void *val;
636bcfccdaSmpi const char *errstr;
646bcfccdaSmpi
656bcfccdaSmpi while ((ch = getopt(argc, argv, "bk:Sst:Uu:w:")) != -1) {
666bcfccdaSmpi switch (ch) {
676bcfccdaSmpi case 'b':
686bcfccdaSmpi blocksignal = 1;
696bcfccdaSmpi break;
706bcfccdaSmpi case 'k':
716bcfccdaSmpi threadkill = strtonum(optarg, 0, INT_MAX, &errstr);
726bcfccdaSmpi if (errstr != NULL)
736bcfccdaSmpi errx(1, "thread to kill is %s: %s",
746bcfccdaSmpi errstr, optarg);
756bcfccdaSmpi break;
766bcfccdaSmpi case 'S':
776bcfccdaSmpi sleepthread = 1;
786bcfccdaSmpi break;
796bcfccdaSmpi case 's':
806bcfccdaSmpi sleepmain = 1;
816bcfccdaSmpi break;
826bcfccdaSmpi case 't':
836bcfccdaSmpi threadmax = strtonum(optarg, 1, INT_MAX, &errstr);
846bcfccdaSmpi if (errstr != NULL)
856bcfccdaSmpi errx(1, "number of threads is %s: %s",
866bcfccdaSmpi errstr, optarg);
876bcfccdaSmpi break;
886bcfccdaSmpi case 'U':
896bcfccdaSmpi sleepunblock = 1;
906bcfccdaSmpi break;
916bcfccdaSmpi case 'u':
926bcfccdaSmpi threadunblock = strtonum(optarg, 0, INT_MAX, &errstr);
936bcfccdaSmpi if (errstr != NULL)
946bcfccdaSmpi errx(1, "thread to unblock is %s: %s",
956bcfccdaSmpi errstr, optarg);
966bcfccdaSmpi break;
976bcfccdaSmpi case 'w':
986bcfccdaSmpi threadwaiter = strtonum(optarg, 0, INT_MAX, &errstr);
996bcfccdaSmpi if (errstr != NULL)
1006bcfccdaSmpi errx(1, "thread to wait is %s: %s",
1016bcfccdaSmpi errstr, optarg);
1026bcfccdaSmpi break;
1036bcfccdaSmpi default:
1046bcfccdaSmpi usage();
1056bcfccdaSmpi }
1066bcfccdaSmpi }
1076bcfccdaSmpi argc -= optind;
1086bcfccdaSmpi argv += optind;
1096bcfccdaSmpi if (argc != 0)
1106bcfccdaSmpi errx(1, "more arguments than expected");
1116bcfccdaSmpi if (threadmax == 0)
1126bcfccdaSmpi errx(1, "number of threads required");
1136bcfccdaSmpi if (threadkill >= threadmax)
1146bcfccdaSmpi errx(1, "thread to kill greater than number of threads");
1156bcfccdaSmpi if (threadunblock >= threadmax)
1166bcfccdaSmpi errx(1, "thread to unblock greater than number of threads");
1176bcfccdaSmpi if (threadwaiter >= threadmax)
1186bcfccdaSmpi errx(1, "thread to wait greater than number of threads");
1196bcfccdaSmpi if (!blocksignal && threadunblock >= 0)
1206bcfccdaSmpi errx(1, "do not unblock thread without blocked signals");
1216bcfccdaSmpi if (!blocksignal && threadwaiter >= 0)
1226bcfccdaSmpi errx(1, "do not wait in thread without blocked signals");
1236bcfccdaSmpi if (threadunblock >= 0 && threadwaiter >= 0)
1246bcfccdaSmpi errx(1, "do not unblock and wait together");
1256bcfccdaSmpi if (sleepunblock && threadwaiter >= 0)
1266bcfccdaSmpi errx(1, "do not sleep before unblock and wait together");
1276bcfccdaSmpi
1286bcfccdaSmpi /* Make sure that we do not hang forever. */
129*5a9255e7Sbluhm alarm(10);
1306bcfccdaSmpi
1316bcfccdaSmpi if (sigemptyset(&set) == -1)
1326bcfccdaSmpi err(1, "sigemptyset");
1336bcfccdaSmpi if (sigaddset(&set, SIGUSR1) == -1)
1346bcfccdaSmpi err(1, "sigaddset");
1356bcfccdaSmpi /* Either deliver SIGUSR2 immediately, or mark it pending. */
1366bcfccdaSmpi if (blocksignal) {
1376bcfccdaSmpi if (sigaddset(&set, SIGUSR2) == -1)
1386bcfccdaSmpi err(1, "sigaddset");
1396bcfccdaSmpi }
1406bcfccdaSmpi /* Block both SIGUSR1 and SIGUSR2 with set. */
1416bcfccdaSmpi if (sigprocmask(SIG_BLOCK, &set, &oset) == -1)
1426bcfccdaSmpi err(1, "sigprocmask");
1436bcfccdaSmpi
1446bcfccdaSmpi /* Prepare to wait for SIGUSR1, but block SIGUSR2 with oset. */
1456bcfccdaSmpi if (sigaddset(&oset, SIGUSR2) == -1)
1466bcfccdaSmpi err(1, "sigaddset");
1476bcfccdaSmpi /* Unblock or wait for SIGUSR2 */
1486bcfccdaSmpi if (sigemptyset(&set) == -1)
1496bcfccdaSmpi err(1, "sigemptyset");
1506bcfccdaSmpi if (sigaddset(&set, SIGUSR2) == -1)
1516bcfccdaSmpi err(1, "sigaddset");
1526bcfccdaSmpi
1536bcfccdaSmpi memset(&act, 0, sizeof(act));
1546bcfccdaSmpi act.sa_handler = handler;
1556bcfccdaSmpi if (sigaction(SIGUSR1, &act, NULL) == -1)
1566bcfccdaSmpi err(1, "sigaction SIGUSR1");
1576bcfccdaSmpi if (sigaction(SIGUSR2, &act, NULL) == -1)
1586bcfccdaSmpi err(1, "sigaction SIGUSR2");
1596bcfccdaSmpi
1606bcfccdaSmpi signaled = calloc(threadmax, sizeof(*signaled));
1616bcfccdaSmpi if (signaled == NULL)
1626bcfccdaSmpi err(1, "calloc signaled");
1636bcfccdaSmpi threads = calloc(threadmax, sizeof(*threads));
1646bcfccdaSmpi if (threads == NULL)
1656bcfccdaSmpi err(1, "calloc threads");
1666bcfccdaSmpi
1676bcfccdaSmpi for (tnum = 1; tnum < threadmax; tnum++) {
1686bcfccdaSmpi arg = tnum;
1696bcfccdaSmpi errno = pthread_create(&threads[tnum], NULL, runner,
1706bcfccdaSmpi (void *)arg);
1716bcfccdaSmpi if (errno)
1726bcfccdaSmpi err(1, "pthread_create %d", tnum);
1736bcfccdaSmpi }
1746bcfccdaSmpi /* Handle the main thread like thread 0. */
1756bcfccdaSmpi threads[0] = pthread_self();
1766bcfccdaSmpi
1776bcfccdaSmpi /* Test what happens if thread is running when killed. */
1786bcfccdaSmpi if (sleepmain)
1796bcfccdaSmpi sleep(1);
1806bcfccdaSmpi
1816bcfccdaSmpi /* All threads are still alive. */
1826bcfccdaSmpi if (threadkill < 0) {
1836bcfccdaSmpi if (kill(getpid(), SIGUSR2) == -1)
1846bcfccdaSmpi err(1, "kill SIGUSR2");
1856bcfccdaSmpi } else {
1866bcfccdaSmpi errno = pthread_kill(threads[threadkill], SIGUSR2);
1876bcfccdaSmpi if (errno)
1886bcfccdaSmpi err(1, "pthread_kill %d SIGUSR2", tnum);
1896bcfccdaSmpi }
1906bcfccdaSmpi
1916bcfccdaSmpi /* Sending SIGUSR1 means threads can continue and finish. */
1926bcfccdaSmpi for (tnum = 0; tnum < threadmax; tnum++) {
1936bcfccdaSmpi errno = pthread_kill(threads[tnum], SIGUSR1);
1946bcfccdaSmpi if (errno)
1956bcfccdaSmpi err(1, "pthread_kill %d SIGUSR1", tnum);
1966bcfccdaSmpi }
1976bcfccdaSmpi
1986bcfccdaSmpi val = runner(0);
1996bcfccdaSmpi ret = (int)val;
2006bcfccdaSmpi
2016bcfccdaSmpi for (tnum = 1; tnum < threadmax; tnum++) {
2026bcfccdaSmpi errno = pthread_join(threads[tnum], &val);
2036bcfccdaSmpi if (errno)
2046bcfccdaSmpi err(1, "pthread_join %d", tnum);
2056bcfccdaSmpi ret = (int)val;
2066bcfccdaSmpi if (ret)
2076bcfccdaSmpi errx(1, "pthread %d returned %d", tnum, ret);
2086bcfccdaSmpi }
2096bcfccdaSmpi free(threads);
2106bcfccdaSmpi
2116bcfccdaSmpi for (tnum = 0; tnum < threadmax; tnum++) {
2126bcfccdaSmpi int i;
2136bcfccdaSmpi
2146bcfccdaSmpi for (i = 0; i < signaled[tnum]; i++)
2156bcfccdaSmpi printf("signal %d\n", tnum);
2166bcfccdaSmpi }
2176bcfccdaSmpi free((void *)signaled);
2186bcfccdaSmpi
2196bcfccdaSmpi return 0;
2206bcfccdaSmpi }
2216bcfccdaSmpi
2226bcfccdaSmpi void
handler(int sig)2236bcfccdaSmpi handler(int sig)
2246bcfccdaSmpi {
2256bcfccdaSmpi pthread_t tid;
2266bcfccdaSmpi int tnum;
2276bcfccdaSmpi
2286bcfccdaSmpi tid = pthread_self();
2296bcfccdaSmpi for (tnum = 0; tnum < threadmax; tnum++) {
2306bcfccdaSmpi if (tid == threads[tnum])
2316bcfccdaSmpi break;
2326bcfccdaSmpi }
2336bcfccdaSmpi switch (sig) {
2346bcfccdaSmpi case SIGUSR1:
2356bcfccdaSmpi break;
2366bcfccdaSmpi case SIGUSR2:
2376bcfccdaSmpi signaled[tnum]++;
2386bcfccdaSmpi break;
2396bcfccdaSmpi default:
2406bcfccdaSmpi errx(1, "unexpected signal %d thread %d", sig, tnum);
2416bcfccdaSmpi }
2426bcfccdaSmpi }
2436bcfccdaSmpi
2446bcfccdaSmpi void *
runner(void * arg)2456bcfccdaSmpi runner(void *arg)
2466bcfccdaSmpi {
2476bcfccdaSmpi int tnum = (int)arg;
2486bcfccdaSmpi
2496bcfccdaSmpi /* Test what happens if thread is running when killed. */
2506bcfccdaSmpi if (sleepthread)
2516bcfccdaSmpi sleep(1);
2526bcfccdaSmpi
2536bcfccdaSmpi if (tnum == threadwaiter) {
2546bcfccdaSmpi int sig;
2556bcfccdaSmpi
2566bcfccdaSmpi if (sigwait(&set, &sig) != 0)
2576bcfccdaSmpi err(1, "sigwait thread %d", tnum);
2586bcfccdaSmpi if (sig != SIGUSR2)
2596bcfccdaSmpi errx(1, "unexpected signal %d thread %d", sig, tnum);
2606bcfccdaSmpi signaled[tnum]++;
2616bcfccdaSmpi }
2626bcfccdaSmpi
2636bcfccdaSmpi /*
2646bcfccdaSmpi * Wait for SIGUSER1, continue to block SIGUSER2.
2656bcfccdaSmpi * The thread is keeps running until it gets SIGUSER1.
2666bcfccdaSmpi */
2676bcfccdaSmpi if (sigsuspend(&oset) != -1 || errno != EINTR)
2686bcfccdaSmpi err(1, "sigsuspend thread %d", tnum);
2696bcfccdaSmpi if ((threadunblock < 0 || tnum == threadunblock) && threadwaiter < 0) {
2706bcfccdaSmpi /* Test what happens if other threads exit before unblock. */
2716bcfccdaSmpi if (sleepunblock)
2726bcfccdaSmpi sleep(1);
2736bcfccdaSmpi
2746bcfccdaSmpi /* Also unblock SIGUSER2, if this thread should get it. */
2756bcfccdaSmpi if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) == -1)
2766bcfccdaSmpi err(1, "pthread_sigmask thread %d", tnum);
2776bcfccdaSmpi }
2786bcfccdaSmpi
2796bcfccdaSmpi return (void *)0;
2806bcfccdaSmpi }
281