1 /* $OpenBSD: sigwait.c,v 1.7 2017/05/27 14:24:28 mpi Exp $ */
2 /*
3 * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Daniel M. Eischen.
17 * 4. Neither the name of the author nor the names of any co-contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN AND CONTRIBUTORS ``AS IS''
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 */
34 #include <stdlib.h>
35 #include <unistd.h>
36
37 #include <errno.h>
38 #include <pthread.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <string.h>
42
43 #include <pthread_np.h>
44 #include "test.h"
45
46 static int sigcounts[NSIG + 1];
47 static sigset_t wait_mask;
48 static pthread_mutex_t waiter_mutex;
49
50
51 static void *
sigwaiter(void * arg)52 sigwaiter (void *arg)
53 {
54 int signo;
55
56 SET_NAME("sigwaiter");
57
58 while (sigcounts[SIGINT] == 0) {
59 printf("Sigwait waiting (thread %p)\n", pthread_self());
60 CHECKe(sigwait (&wait_mask, &signo));
61 sigcounts[signo]++;
62 printf ("Sigwait caught signal %d (%s)\n", signo,
63 strsignal(signo));
64
65 /* Allow the main thread to prevent the sigwait. */
66 CHECKr(pthread_mutex_lock (&waiter_mutex));
67 CHECKr(pthread_mutex_unlock (&waiter_mutex));
68 }
69
70 return (arg);
71 }
72
73
74 static void
sighandler(int signo)75 sighandler (int signo)
76 {
77 int save_errno = errno;
78 char buf[8192];
79
80 snprintf(buf, sizeof buf,
81 " -> Signal handler caught signal %d (%s) in thread %p\n",
82 signo, strsignal(signo), pthread_self());
83 write(STDOUT_FILENO, buf, strlen(buf));
84
85 if ((signo >= 0) && (signo <= NSIG))
86 sigcounts[signo]++;
87 errno = save_errno;
88 }
89
main(int argc,char * argv[])90 int main (int argc, char *argv[])
91 {
92 pthread_mutexattr_t mattr;
93 pthread_attr_t pattr;
94 pthread_t tid;
95 struct sigaction act;
96
97 /* Initialize our signal counts. */
98 memset ((void *) sigcounts, 0, NSIG * sizeof (int));
99
100 /* Setup our wait mask. */
101 sigemptyset (&wait_mask); /* Default action */
102 sigaddset (&wait_mask, SIGHUP); /* terminate */
103 sigaddset (&wait_mask, SIGINT); /* terminate */
104 sigaddset (&wait_mask, SIGQUIT); /* create core image */
105 sigaddset (&wait_mask, SIGURG); /* ignore */
106 sigaddset (&wait_mask, SIGIO); /* ignore */
107 sigaddset (&wait_mask, SIGUSR1); /* terminate */
108
109 /* Block all of the signals that will be waited for */
110 CHECKe(sigprocmask (SIG_BLOCK, &wait_mask, NULL));
111
112 /* Ignore signals SIGHUP and SIGIO. */
113 sigemptyset (&act.sa_mask);
114 sigaddset (&act.sa_mask, SIGHUP);
115 sigaddset (&act.sa_mask, SIGIO);
116 act.sa_handler = SIG_IGN;
117 act.sa_flags = 0;
118 CHECKe(sigaction (SIGHUP, &act, NULL));
119 CHECKe(sigaction (SIGIO, &act, NULL));
120
121 /* Install a signal handler for SIGURG */
122 sigemptyset (&act.sa_mask);
123 sigaddset (&act.sa_mask, SIGURG);
124 act.sa_handler = sighandler;
125 act.sa_flags = SA_RESTART;
126 CHECKe(sigaction (SIGURG, &act, NULL));
127
128 /* Install a signal handler for SIGXCPU */
129 sigemptyset (&act.sa_mask);
130 sigaddset (&act.sa_mask, SIGXCPU);
131 CHECKe(sigaction (SIGXCPU, &act, NULL));
132
133 /*
134 * Initialize the thread attribute.
135 */
136 CHECKr(pthread_attr_init (&pattr));
137 CHECKr(pthread_attr_setdetachstate (&pattr, PTHREAD_CREATE_JOINABLE));
138
139 /*
140 * Initialize and create a mutex.
141 */
142 CHECKr(pthread_mutexattr_init (&mattr));
143 CHECKr(pthread_mutex_init (&waiter_mutex, &mattr));
144
145 /*
146 * Create the sigwaiter thread.
147 */
148 CHECKr(pthread_create (&tid, &pattr, sigwaiter, NULL));
149
150 #if 0 /* XXX To quote POSIX 2008, XSH, from section 2.4.1
151 * (Signal Generation and Delivery) paragraph 4:
152 * If the action associated with a blocked signal is to
153 * ignore the signal and if that signal is generated for
154 * the process, it is unspecified whether the signal is
155 * discarded immediately upon generation or remains pending.
156 * So, SIGIO may remain pending here and be accepted by the sigwait()
157 * in the other thread, even though its disposition is "ignored".
158 */
159 /*
160 * Verify that an ignored signal doesn't cause a wakeup.
161 * We don't have a handler installed for SIGIO.
162 */
163 CHECKr(pthread_kill (tid, SIGIO));
164 sleep (1);
165 CHECKe(kill(getpid(), SIGIO));
166 sleep (1);
167 /* sigwait should not wake up for ignored signal SIGIO */
168 ASSERT(sigcounts[SIGIO] == 0);
169 #endif
170
171 /*
172 * Verify that a signal with a default action of ignore, for
173 * which we have a signal handler installed, will release a sigwait.
174 */
175 CHECKr(pthread_kill (tid, SIGURG));
176 sleep (1);
177 CHECKe(kill(getpid(), SIGURG));
178 sleep (1);
179 /* sigwait should wake up for SIGURG */
180 ASSERT(sigcounts[SIGURG] == 2);
181
182 /*
183 * Verify that a signal with a default action that terminates
184 * the process will release a sigwait.
185 */
186 CHECKr(pthread_kill (tid, SIGUSR1));
187 sleep (1);
188 CHECKe(kill(getpid(), SIGUSR1));
189 sleep (1);
190 if (sigcounts[SIGUSR1] != 2)
191 printf ("FAIL: sigwait doesn't wake up for SIGUSR1.\n");
192
193 /*
194 * Verify that if we install a signal handler for a previously
195 * ignored signal, an occurrence of this signal will release
196 * the (already waiting) sigwait.
197 */
198
199 /* Install a signal handler for SIGHUP. */
200 sigemptyset (&act.sa_mask);
201 sigaddset (&act.sa_mask, SIGHUP);
202 act.sa_handler = sighandler;
203 act.sa_flags = SA_RESTART;
204 CHECKe(sigaction (SIGHUP, &act, NULL));
205
206 /* Sending SIGHUP should release the sigwait. */
207 CHECKe(kill(getpid(), SIGHUP));
208 sleep (1);
209 CHECKr(pthread_kill (tid, SIGHUP));
210 sleep (1);
211 /* sigwait should wake up for SIGHUP */
212 ASSERT(sigcounts[SIGHUP] == 2);
213
214 /*
215 * Verify that a pending signal in the waiters mask will
216 * cause sigwait to return the pending signal. We do this
217 * by taking the waiters mutex and signaling the waiter to
218 * release him from the sigwait. The waiter will block
219 * on taking the mutex, and we can then send the waiter a
220 * signal which should be added to his pending signals.
221 * The next time the waiter does a sigwait, he should
222 * return with the pending signal.
223 */
224 sigcounts[SIGHUP] = 0;
225 CHECKr(pthread_mutex_lock (&waiter_mutex));
226 /* Release the waiter from sigwait. */
227 CHECKe(kill(getpid(), SIGHUP));
228 sleep (1);
229 /* signal waiter should wake up for SIGHUP */
230 ASSERT(sigcounts[SIGHUP] == 1);
231 /* Release the waiter thread and allow him to run. */
232 CHECKr(pthread_mutex_unlock (&waiter_mutex));
233 sleep (1);
234
235 /*
236 * Repeat the above test using pthread_kill and SIGUSR1
237 */
238 sigcounts[SIGUSR1] = 0;
239 CHECKr(pthread_mutex_lock (&waiter_mutex));
240 /* Release the waiter from sigwait. */
241 CHECKr(pthread_kill (tid, SIGUSR1));
242 sleep (1);
243 /* sigwait should wake up for SIGUSR1 */
244 ASSERT(sigcounts[SIGUSR1] == 1);
245 /* Add SIGUSR1 to the waiters pending signals. */
246 CHECKr(pthread_kill (tid, SIGUSR1));
247 /* Release the waiter thread and allow him to run. */
248 CHECKr(pthread_mutex_unlock (&waiter_mutex));
249 sleep (1);
250 /* sigwait should return for pending SIGUSR1 */
251 ASSERT(sigcounts[SIGUSR1] == 2);
252
253 #if 0
254 /*
255 * Verify that we can still kill the process for a signal
256 * not being waited on by sigwait.
257 */
258 CHECKe(kill(getpid(), SIGPIPE));
259 PANIC("SIGPIPE did not terminate process");
260
261 /*
262 * Wait for the thread to finish.
263 */
264 CHECKr(pthread_join (tid, NULL));
265 #endif
266
267 SUCCEED;
268 }
269