xref: /openbsd-src/regress/sys/kern/signal/sigpthread/sigpthread.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: sigpthread.c,v 1.1 2020/09/16 14:02:24 mpi Exp $	*/
2 /*
3  * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <err.h>
19 #include <errno.h>
20 #include <limits.h>
21 #include <pthread.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 void __dead usage(void);
29 void handler(int);
30 void *runner(void *);
31 
32 void __dead
33 usage(void)
34 {
35 	fprintf(stderr, "sigpthread [-bSsU] [-k kill] -t threads [-u unblock] "
36 	    "[-w waiter]\n"
37 	    "    -b             block signal to make it pending\n"
38 	    "    -k kill        thread to kill, else process\n"
39 	    "    -S             sleep in each thread before suspend\n"
40 	    "    -s             sleep in main before kill\n"
41 	    "    -t threads     number of threads to run\n"
42 	    "    -U             sleep in thread before unblock\n"
43 	    "    -u unblock     thread to unblock, else unblock all\n"
44 	    "    -w waiter      use sigwait in thread\n"
45 	);
46 	exit(1);
47 }
48 
49 int blocksignal = 0;
50 int threadmax, threadunblock = -1, threadwaiter = -1;
51 int sleepthread, sleepmain, sleepunblock;
52 sigset_t set, oset;
53 pthread_t *threads;
54 volatile sig_atomic_t *signaled;
55 
56 int
57 main(int argc, char *argv[])
58 {
59 	struct sigaction act;
60 	int ch, ret, tnum, threadkill = -1;
61 	long arg;
62 	void *val;
63 	const char *errstr;
64 
65 	while ((ch = getopt(argc, argv, "bk:Sst:Uu:w:")) != -1) {
66 		switch (ch) {
67 		case 'b':
68 			blocksignal = 1;
69 			break;
70 		case 'k':
71 			threadkill = strtonum(optarg, 0, INT_MAX, &errstr);
72 			if (errstr != NULL)
73 				errx(1, "thread to kill is %s: %s",
74 				    errstr, optarg);
75 			break;
76 		case 'S':
77 			sleepthread = 1;
78 			break;
79 		case 's':
80 			sleepmain = 1;
81 			break;
82 		case 't':
83 			threadmax = strtonum(optarg, 1, INT_MAX, &errstr);
84 			if (errstr != NULL)
85 				errx(1, "number of threads is %s: %s",
86 				    errstr, optarg);
87 			break;
88 		case 'U':
89 			sleepunblock = 1;
90 			break;
91 		case 'u':
92 			threadunblock = strtonum(optarg, 0, INT_MAX, &errstr);
93 			if (errstr != NULL)
94 				errx(1, "thread to unblock is %s: %s",
95 				    errstr, optarg);
96 			break;
97 		case 'w':
98 			threadwaiter = strtonum(optarg, 0, INT_MAX, &errstr);
99 			if (errstr != NULL)
100 				errx(1, "thread to wait is %s: %s",
101 				    errstr, optarg);
102 			break;
103 		default:
104 			usage();
105 		}
106 	}
107 	argc -= optind;
108 	argv += optind;
109 	if (argc != 0)
110 		errx(1, "more arguments than expected");
111 	if (threadmax == 0)
112 		errx(1, "number of threads required");
113 	if (threadkill >= threadmax)
114 		errx(1, "thread to kill greater than number of threads");
115 	if (threadunblock >= threadmax)
116 		errx(1, "thread to unblock greater than number of threads");
117 	if (threadwaiter >= threadmax)
118 		errx(1, "thread to wait greater than number of threads");
119 	if (!blocksignal && threadunblock >= 0)
120 		errx(1, "do not unblock thread without blocked signals");
121 	if (!blocksignal && threadwaiter >= 0)
122 		errx(1, "do not wait in thread without blocked signals");
123 	if (threadunblock >= 0 && threadwaiter >= 0)
124 		errx(1, "do not unblock and wait together");
125 	if (sleepunblock && threadwaiter >= 0)
126 		errx(1, "do not sleep before unblock and wait together");
127 
128 	/* Make sure that we do not hang forever. */
129 	ret = alarm(10);
130 	if (ret == -1)
131 		err(1, "alarm");
132 
133 	if (sigemptyset(&set) == -1)
134 		err(1, "sigemptyset");
135 	if (sigaddset(&set, SIGUSR1) == -1)
136 		err(1, "sigaddset");
137 	/* Either deliver SIGUSR2 immediately, or mark it pending. */
138 	if (blocksignal) {
139 		if (sigaddset(&set, SIGUSR2) == -1)
140 			err(1, "sigaddset");
141 	}
142 	/* Block both SIGUSR1 and SIGUSR2 with set. */
143 	if (sigprocmask(SIG_BLOCK, &set, &oset) == -1)
144 		err(1, "sigprocmask");
145 
146 	/* Prepare to wait for SIGUSR1, but block SIGUSR2 with oset. */
147 	if (sigaddset(&oset, SIGUSR2) == -1)
148 		err(1, "sigaddset");
149 	/* Unblock or wait for SIGUSR2 */
150 	if (sigemptyset(&set) == -1)
151 		err(1, "sigemptyset");
152 	if (sigaddset(&set, SIGUSR2) == -1)
153 		err(1, "sigaddset");
154 
155 	memset(&act, 0, sizeof(act));
156 	act.sa_handler = handler;
157 	if (sigaction(SIGUSR1, &act, NULL) == -1)
158 		err(1, "sigaction SIGUSR1");
159 	if (sigaction(SIGUSR2, &act, NULL) == -1)
160 		err(1, "sigaction SIGUSR2");
161 
162 	signaled = calloc(threadmax, sizeof(*signaled));
163 	if (signaled == NULL)
164 		err(1, "calloc signaled");
165 	threads = calloc(threadmax, sizeof(*threads));
166 	if (threads == NULL)
167 		err(1, "calloc threads");
168 
169 	for (tnum = 1; tnum < threadmax; tnum++) {
170 		arg = tnum;
171 		errno = pthread_create(&threads[tnum], NULL, runner,
172 		    (void *)arg);
173 		if (errno)
174 			err(1, "pthread_create %d", tnum);
175 	}
176 	/* Handle the main thread like thread 0. */
177 	threads[0] = pthread_self();
178 
179 	/* Test what happens if thread is running when killed. */
180 	if (sleepmain)
181 		sleep(1);
182 
183 	/* All threads are still alive. */
184 	if (threadkill < 0) {
185 		if (kill(getpid(), SIGUSR2) == -1)
186 			err(1, "kill SIGUSR2");
187 	} else {
188 		errno = pthread_kill(threads[threadkill], SIGUSR2);
189 		if (errno)
190 			err(1, "pthread_kill %d SIGUSR2", tnum);
191 	}
192 
193 	/* Sending SIGUSR1 means threads can continue and finish. */
194 	for (tnum = 0; tnum < threadmax; tnum++) {
195 		errno = pthread_kill(threads[tnum], SIGUSR1);
196 		if (errno)
197 			err(1, "pthread_kill %d SIGUSR1", tnum);
198 	}
199 
200 	val = runner(0);
201 	ret = (int)val;
202 
203 	for (tnum = 1; tnum < threadmax; tnum++) {
204 		errno = pthread_join(threads[tnum], &val);
205 		if (errno)
206 			err(1, "pthread_join %d", tnum);
207 		ret = (int)val;
208 		if (ret)
209 			errx(1, "pthread %d returned %d", tnum, ret);
210 	}
211 	free(threads);
212 
213 	for (tnum = 0; tnum < threadmax; tnum++) {
214 		int i;
215 
216 		for (i = 0; i < signaled[tnum]; i++)
217 			printf("signal %d\n", tnum);
218 	}
219 	free((void *)signaled);
220 
221 	return 0;
222 }
223 
224 void
225 handler(int sig)
226 {
227 	pthread_t tid;
228 	int tnum;
229 
230 	tid = pthread_self();
231 	for (tnum = 0; tnum < threadmax; tnum++) {
232 		if (tid == threads[tnum])
233 			break;
234 	}
235 	switch (sig) {
236 	case SIGUSR1:
237 		break;
238 	case SIGUSR2:
239 		signaled[tnum]++;
240 		break;
241 	default:
242 		errx(1, "unexpected signal %d thread %d", sig, tnum);
243 	}
244 }
245 
246 void *
247 runner(void *arg)
248 {
249 	int tnum = (int)arg;
250 
251 	/* Test what happens if thread is running when killed. */
252 	if (sleepthread)
253 		sleep(1);
254 
255 	if (tnum == threadwaiter) {
256 		int sig;
257 
258 		if (sigwait(&set, &sig) != 0)
259 			err(1, "sigwait thread %d", tnum);
260 		if (sig != SIGUSR2)
261 			errx(1, "unexpected signal %d thread %d", sig, tnum);
262 		signaled[tnum]++;
263 	}
264 
265 	/*
266 	 * Wait for SIGUSER1, continue to block SIGUSER2.
267 	 * The thread is keeps running until it gets SIGUSER1.
268 	 */
269 	if (sigsuspend(&oset) != -1 || errno != EINTR)
270 		err(1, "sigsuspend thread %d", tnum);
271 	if ((threadunblock < 0 || tnum == threadunblock) && threadwaiter < 0) {
272 		/* Test what happens if other threads exit before unblock. */
273 		if (sleepunblock)
274 			sleep(1);
275 
276 		/* Also unblock SIGUSER2, if this thread should get it. */
277 		if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) == -1)
278 			err(1, "pthread_sigmask thread %d", tnum);
279 	}
280 
281 	return (void *)0;
282 }
283