xref: /openbsd-src/regress/sys/kern/signal/sigpthread/sigpthread.c (revision 5a9255e7fde1eff660134511e5a28e2366fbe25a)
1 /*	$OpenBSD: sigpthread.c,v 1.2 2021/07/06 11:50:34 bluhm 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
usage(void)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
main(int argc,char * argv[])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 	alarm(10);
130 
131 	if (sigemptyset(&set) == -1)
132 		err(1, "sigemptyset");
133 	if (sigaddset(&set, SIGUSR1) == -1)
134 		err(1, "sigaddset");
135 	/* Either deliver SIGUSR2 immediately, or mark it pending. */
136 	if (blocksignal) {
137 		if (sigaddset(&set, SIGUSR2) == -1)
138 			err(1, "sigaddset");
139 	}
140 	/* Block both SIGUSR1 and SIGUSR2 with set. */
141 	if (sigprocmask(SIG_BLOCK, &set, &oset) == -1)
142 		err(1, "sigprocmask");
143 
144 	/* Prepare to wait for SIGUSR1, but block SIGUSR2 with oset. */
145 	if (sigaddset(&oset, SIGUSR2) == -1)
146 		err(1, "sigaddset");
147 	/* Unblock or wait for SIGUSR2 */
148 	if (sigemptyset(&set) == -1)
149 		err(1, "sigemptyset");
150 	if (sigaddset(&set, SIGUSR2) == -1)
151 		err(1, "sigaddset");
152 
153 	memset(&act, 0, sizeof(act));
154 	act.sa_handler = handler;
155 	if (sigaction(SIGUSR1, &act, NULL) == -1)
156 		err(1, "sigaction SIGUSR1");
157 	if (sigaction(SIGUSR2, &act, NULL) == -1)
158 		err(1, "sigaction SIGUSR2");
159 
160 	signaled = calloc(threadmax, sizeof(*signaled));
161 	if (signaled == NULL)
162 		err(1, "calloc signaled");
163 	threads = calloc(threadmax, sizeof(*threads));
164 	if (threads == NULL)
165 		err(1, "calloc threads");
166 
167 	for (tnum = 1; tnum < threadmax; tnum++) {
168 		arg = tnum;
169 		errno = pthread_create(&threads[tnum], NULL, runner,
170 		    (void *)arg);
171 		if (errno)
172 			err(1, "pthread_create %d", tnum);
173 	}
174 	/* Handle the main thread like thread 0. */
175 	threads[0] = pthread_self();
176 
177 	/* Test what happens if thread is running when killed. */
178 	if (sleepmain)
179 		sleep(1);
180 
181 	/* All threads are still alive. */
182 	if (threadkill < 0) {
183 		if (kill(getpid(), SIGUSR2) == -1)
184 			err(1, "kill SIGUSR2");
185 	} else {
186 		errno = pthread_kill(threads[threadkill], SIGUSR2);
187 		if (errno)
188 			err(1, "pthread_kill %d SIGUSR2", tnum);
189 	}
190 
191 	/* Sending SIGUSR1 means threads can continue and finish. */
192 	for (tnum = 0; tnum < threadmax; tnum++) {
193 		errno = pthread_kill(threads[tnum], SIGUSR1);
194 		if (errno)
195 			err(1, "pthread_kill %d SIGUSR1", tnum);
196 	}
197 
198 	val = runner(0);
199 	ret = (int)val;
200 
201 	for (tnum = 1; tnum < threadmax; tnum++) {
202 		errno = pthread_join(threads[tnum], &val);
203 		if (errno)
204 			err(1, "pthread_join %d", tnum);
205 		ret = (int)val;
206 		if (ret)
207 			errx(1, "pthread %d returned %d", tnum, ret);
208 	}
209 	free(threads);
210 
211 	for (tnum = 0; tnum < threadmax; tnum++) {
212 		int i;
213 
214 		for (i = 0; i < signaled[tnum]; i++)
215 			printf("signal %d\n", tnum);
216 	}
217 	free((void *)signaled);
218 
219 	return 0;
220 }
221 
222 void
handler(int sig)223 handler(int sig)
224 {
225 	pthread_t tid;
226 	int tnum;
227 
228 	tid = pthread_self();
229 	for (tnum = 0; tnum < threadmax; tnum++) {
230 		if (tid == threads[tnum])
231 			break;
232 	}
233 	switch (sig) {
234 	case SIGUSR1:
235 		break;
236 	case SIGUSR2:
237 		signaled[tnum]++;
238 		break;
239 	default:
240 		errx(1, "unexpected signal %d thread %d", sig, tnum);
241 	}
242 }
243 
244 void *
runner(void * arg)245 runner(void *arg)
246 {
247 	int tnum = (int)arg;
248 
249 	/* Test what happens if thread is running when killed. */
250 	if (sleepthread)
251 		sleep(1);
252 
253 	if (tnum == threadwaiter) {
254 		int sig;
255 
256 		if (sigwait(&set, &sig) != 0)
257 			err(1, "sigwait thread %d", tnum);
258 		if (sig != SIGUSR2)
259 			errx(1, "unexpected signal %d thread %d", sig, tnum);
260 		signaled[tnum]++;
261 	}
262 
263 	/*
264 	 * Wait for SIGUSER1, continue to block SIGUSER2.
265 	 * The thread is keeps running until it gets SIGUSER1.
266 	 */
267 	if (sigsuspend(&oset) != -1 || errno != EINTR)
268 		err(1, "sigsuspend thread %d", tnum);
269 	if ((threadunblock < 0 || tnum == threadunblock) && threadwaiter < 0) {
270 		/* Test what happens if other threads exit before unblock. */
271 		if (sleepunblock)
272 			sleep(1);
273 
274 		/* Also unblock SIGUSER2, if this thread should get it. */
275 		if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) == -1)
276 			err(1, "pthread_sigmask thread %d", tnum);
277 	}
278 
279 	return (void *)0;
280 }
281