1 /* $OpenBSD: uthread_sig.c,v 1.20 2003/04/30 17:54:17 marc Exp $ */
2 /*
3 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
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 John Birrell.
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 JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
22 * 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 AUTHOR 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 * $FreeBSD: uthread_sig.c,v 1.20 1999/09/29 15:18:39 marcel Exp $
34 */
35 #include <string.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <errno.h>
40 #ifdef _THREAD_SAFE
41 #include <pthread.h>
42 #include "pthread_private.h"
43
44
45 /* Initialize signal handling facility: */
46 void
_thread_sig_init(void)47 _thread_sig_init(void)
48 {
49 int i;
50
51 /* Clear local state */
52 for (i = 1; i < NSIG; i++) {
53 _SPINLOCK_INIT(&_thread_sigq[i - 1].lock);
54 _thread_sigq[i - 1].pending = 0;
55 }
56 }
57
58 /*
59 * This is the only installed signal handler. In addition to handling
60 * thread kernel signals it is installed in place of application handlers
61 * and dispatches signals appropriately.
62 */
63 void
_thread_sig_handler(int sig,siginfo_t * info,struct sigcontext * scp)64 _thread_sig_handler(int sig, siginfo_t *info, struct sigcontext * scp)
65 {
66 struct pthread *curthread = _get_curthread();
67 int dispatch;
68 char c;
69
70 if (sig == _SCHED_SIGNAL) {
71 /* Update the scheduling clock: */
72 gettimeofday((struct timeval *)&_sched_tod, NULL);
73 _sched_ticks++;
74
75 /* only process signal when scheduler isn't running */
76 if (_thread_kern_in_sched == 0) {
77 if (curthread->sig_defer_count > 0) {
78 /*
79 * The scheduler interrupt has come when
80 * the currently running thread has deferred
81 * thread signals.
82 */
83 curthread->yield_on_sig_undefer = 1;
84 } else {
85 /* Schedule the next thread. */
86 _thread_kern_sched(scp);
87
88 /*
89 * The scheduler currently returns here instead
90 * of calling sigreturn due to a sparc sigreturn
91 * bug. We should also return. That brings
92 * us back to the sigtramp code which will
93 * sigreturn to the context stored on the
94 * current stack (which is the same as scp,
95 * above). The code originally did this:
96 *
97 * PANIC("Returned to signal function "
98 * "from scheduler");
99 */
100 return;
101 }
102 }
103 } else {
104 /*
105 * save the info for this signal in a per signal queue of depth
106 * one. Per a POSIX suggestion, only the info for the first
107 * of multiple activations of the same signal is kept.
108 */
109 _SPINLOCK(&_thread_sigq[sig - 1].lock);
110 if (_thread_sigq[sig - 1].pending == 0) {
111 sigaddset(&_process_sigpending, sig);
112 _thread_sigq[sig - 1].pending++;
113 memcpy(&_thread_sigq[sig - 1].siginfo, info,
114 sizeof *info);
115 }
116 _SPINUNLOCK(&_thread_sigq[sig - 1].lock);
117
118 if ((_queue_signals != 0) ||
119 ((_thread_kern_in_sched == 0) &&
120 (curthread->sig_defer_count > 0))) {
121 /*
122 * The kernel has been interrupted while the scheduler
123 * is accessing the scheduling queues or there is a
124 * currently running thread that has deferred signals.
125 *
126 * Cast the signal number to a character variable
127 * and Write the signal number to the kernel pipe so
128 * that it will be ready to read when this signal
129 * handler returns.
130 */
131 c = sig;
132 _thread_sys_write(_thread_kern_pipe[1], &c, 1);
133 _sigq_check_reqd = 1;
134 } else {
135 _queue_signals = 1;
136 dispatch = _thread_sig_handle(sig, scp);
137 _queue_signals = 0;
138 if (dispatch)
139 _dispatch_signals(scp);
140 }
141 }
142 }
143
144 /*
145 * Clear the pending flag for the given signal on all threads
146 * if per process, or only for the given thread if non null
147 * and the signal doesn't exist in _process_sigpending.
148 */
149 void
_thread_clear_pending(int sig,pthread_t thread)150 _thread_clear_pending(int sig, pthread_t thread)
151 {
152 pthread_t pthread;
153
154 _thread_sigq[sig - 1].pending = 0;
155 if (sigismember(&_process_sigpending, sig)) {
156 sigdelset(&_process_sigpending, sig);
157 TAILQ_FOREACH(pthread, &_thread_list, tle) {
158 sigdelset(&pthread->sigpend, sig);
159 }
160 } else if (thread)
161 sigdelset(&thread->sigpend, sig);
162 }
163
164
165 /*
166 * Process the given signal. Returns 1 if the signal may be dispatched,
167 * otherwise 0. Signals MUST be defered when this function is called.
168 */
169 int
_thread_sig_handle(int sig,struct sigcontext * scp)170 _thread_sig_handle(int sig, struct sigcontext * scp)
171 {
172 struct pthread *curthread = _get_curthread();
173 int i;
174 pthread_t pthread, pthread_next;
175
176 if (sig == SIGINFO)
177 _thread_dump_info(); /* Dump thread information */
178 else if (sig == _SCHED_SIGNAL)
179 ; /* This shouldn't ever occur (should this panic?). */
180 else {
181 if (sig == SIGCHLD) {
182 /*
183 * Go through the file list and set all files
184 * to non-blocking again in case the child
185 * set some of them to block. Sigh.
186 */
187 for (i = 0; i < _thread_dtablesize; i++)
188 if (_thread_fd_table[i] != NULL)
189 _thread_sys_fcntl(i, F_SETFL,
190 _thread_fd_table[i]->flags |
191 O_NONBLOCK);
192 }
193
194 /*
195 * If the handler is SIG_IGN the signal never happened.
196 * remove it from the pending list and return.
197 */
198 if (_thread_sigact[sig - 1].sa_handler == SIG_IGN) {
199 _thread_clear_pending(sig, curthread);
200 return 0;
201 }
202
203 /*
204 * POSIX says that pending SIGCONT signals are discarded
205 * when one of these signals occurs and vice versa.
206 */
207 if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN ||
208 sig == SIGTTOU)
209 _thread_clear_pending(SIGCONT, curthread);
210 if (sig == SIGCONT) {
211 _thread_clear_pending(SIGSTOP, curthread);
212 _thread_clear_pending(SIGTSTP, curthread);
213 _thread_clear_pending(SIGTTIN, curthread);
214 _thread_clear_pending(SIGTTOU, curthread);
215 }
216
217 /*
218 * Enter a loop to process each thread in the waiting
219 * list that is sigwait-ing on a signal. Since POSIX
220 * doesn't specify which thread will get the signal
221 * if there are multiple waiters, we'll give it to the
222 * first one we find.
223 */
224 for (pthread = TAILQ_FIRST(&_waitingq);
225 pthread != NULL; pthread = pthread_next) {
226 /*
227 * Grab the next thread before possibly destroying
228 * the link entry.
229 */
230 pthread_next = TAILQ_NEXT(pthread, pqe);
231
232 if ((pthread->state == PS_SIGWAIT) &&
233 sigismember(pthread->data.sigwait, sig)) {
234 /*
235 * found a sigwaiter. Mark its state as
236 * running, save the signal that will be
237 * returned, and mark the signal as no
238 * longer pending.
239 */
240 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
241 pthread->signo = sig;
242 _thread_clear_pending(sig, pthread);
243 return 0;
244 }
245 }
246
247 /*
248 * mark the signal as pending for each thread
249 * and give the thread a chance to update
250 * its state.
251 */
252 TAILQ_FOREACH(pthread, &_thread_list, tle) {
253 /* Current thread inside critical region? */
254 if (curthread->sig_defer_count > 0)
255 pthread->sig_defer_count++;
256 _thread_signal(pthread,sig);
257 if (curthread->sig_defer_count > 0)
258 pthread->sig_defer_count--;
259 }
260 return 1;
261 }
262 return 0;
263 }
264
265 /* Perform thread specific actions in response to a signal: */
266 void
_thread_signal(pthread_t pthread,int sig)267 _thread_signal(pthread_t pthread, int sig)
268 {
269 /* Flag the signal as pending. It may be dispatched later. */
270 sigaddset(&pthread->sigpend,sig);
271
272 /* skip this thread if signal is masked */
273 if (sigismember(&pthread->sigmask, sig))
274 return;
275
276 /* Process according to thread state: */
277 switch (pthread->state) {
278 /*
279 * States which do not change when a signal is trapped:
280 */
281 case PS_COND_WAIT:
282 case PS_DEAD:
283 case PS_FDLR_WAIT:
284 case PS_FDLW_WAIT:
285 case PS_FILE_WAIT:
286 case PS_JOIN:
287 case PS_MUTEX_WAIT:
288 case PS_RUNNING:
289 case PS_SIGTHREAD:
290 case PS_SIGWAIT:
291 case PS_SUSPENDED:
292 case PS_SPINBLOCK:
293 case PS_DEADLOCK:
294 case PS_STATE_MAX: /* only here to quell a compiler warning */
295 /* Nothing to do here. */
296 break;
297
298 /*
299 * The wait state is a special case due to the handling of
300 * SIGCHLD signals.
301 */
302 case PS_WAIT_WAIT:
303 /*
304 * Check for signals other than the death of a child
305 * process:
306 */
307 if (sig != SIGCHLD)
308 /* Flag the operation as interrupted: */
309 pthread->interrupted = 1;
310
311 /* Change the state of the thread to run: */
312 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
313
314 /* Return the signal number: */
315 pthread->signo = sig;
316 break;
317
318 /*
319 * States that are interrupted by the occurrence of a signal
320 * other than the scheduling alarm:
321 */
322 case PS_FDR_WAIT:
323 case PS_FDW_WAIT:
324 case PS_POLL_WAIT:
325 case PS_SLEEP_WAIT:
326 case PS_SELECT_WAIT:
327 if (sig != SIGCHLD ||
328 _thread_sigact[sig - 1].sa_handler != SIG_DFL) {
329 /* Flag the operation as interrupted: */
330 pthread->interrupted = 1;
331
332 if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
333 PTHREAD_WORKQ_REMOVE(pthread);
334
335 /* Change the state of the thread to run: */
336 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
337
338 /* Return the signal number: */
339 pthread->signo = sig;
340 }
341 break;
342
343 case PS_SIGSUSPEND:
344 /*
345 * Only wake up the thread if the signal is unblocked
346 * and there is a handler installed for the signal.
347 */
348 if (_thread_sigact[sig - 1].sa_handler != SIG_DFL) {
349 /* Change the state of the thread to run: */
350 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
351
352 /* Return the signal number: */
353 pthread->signo = sig;
354 }
355 break;
356 }
357 }
358
359 /*
360 * Dispatch a signal to the current thread after setting up the
361 * appropriate signal mask.
362 */
363 void
_dispatch_signal(int sig,struct sigcontext * scp)364 _dispatch_signal(int sig, struct sigcontext * scp)
365 {
366 struct pthread *curthread = _get_curthread();
367
368 sigset_t set;
369 sigset_t oset;
370 struct sigaction act;
371 void (*action)(int, siginfo_t *, void *);
372
373 /* save off the action and set the signal mask */
374 action = _thread_sigact[sig - 1].sa_sigaction;
375 set = _thread_sigact[sig - 1].sa_mask;
376 if ((_thread_sigact[sig-1].sa_flags & SA_NODEFER) == 0)
377 sigaddset(&set, sig);
378 oset = curthread->sigmask;
379 curthread->sigmask |= set;
380
381 /* clear custom handler if SA_RESETHAND set. */
382 if (_thread_sigact[sig - 1].sa_flags & SA_RESETHAND) {
383 act.sa_handler = SIG_DFL;
384 act.sa_flags = 0;
385 sigemptyset(&act.sa_mask);
386 sigaction(sig, &act, NULL);
387 }
388
389 /*
390 * clear the pending flag, deliver the signal, then reset the
391 * signal mask
392 */
393 _thread_clear_pending(sig, curthread);
394 (*action)(sig, &_thread_sigq[sig - 1].siginfo, scp);
395 curthread->sigmask = oset;
396 }
397
398 /*
399 * possibly dispatch a signal to the current thread.
400 */
401 void
_dispatch_signals(struct sigcontext * scp)402 _dispatch_signals(struct sigcontext * scp)
403 {
404 struct pthread *curthread = _get_curthread();
405 int i;
406
407 /*
408 * Check if there are pending signals for the running
409 * thread that aren't blocked:
410 */
411 if ((curthread->sigpend & ~curthread->sigmask) != 0)
412 /* Look for all possible pending signals: */
413 for (i = 1; i < NSIG; i++)
414 /*
415 * Check that a custom handler is installed
416 * and if the signal is not blocked:
417 */
418 if (_thread_sigact[i - 1].sa_handler != SIG_DFL &&
419 _thread_sigact[i - 1].sa_handler != SIG_IGN &&
420 sigismember(&curthread->sigpend,i) &&
421 !sigismember(&curthread->sigmask,i))
422 /* dispatch */
423 _dispatch_signal(i, scp);
424 }
425 #endif
426