xref: /openbsd-src/regress/sys/kern/signal/sigpthread/sigpthread.c (revision 5a9255e7fde1eff660134511e5a28e2366fbe25a)
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