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