thr_sig.c revision 51794
1/*
2 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by John Birrell.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/lib/libkse/thread/thr_sig.c 51794 1999-09-29 15:18:46Z marcel $
33 */
34#include <sys/param.h>
35#include <sys/types.h>
36#include <sys/signalvar.h>
37#include <signal.h>
38#include <fcntl.h>
39#include <unistd.h>
40#include <errno.h>
41#ifdef _THREAD_SAFE
42#include <pthread.h>
43#include "pthread_private.h"
44
45/* Static variables: */
46static spinlock_t	signal_lock = _SPINLOCK_INITIALIZER;
47unsigned int		pending_sigs[NSIG];
48unsigned int		handled_sigs[NSIG];
49int			volatile check_pending = 0;
50
51/* Initialize signal handling facility: */
52void
53_thread_sig_init(void)
54{
55	int i;
56
57	/* Clear pending and handled signal counts: */
58	for (i = 1; i < NSIG; i++) {
59		pending_sigs[i - 1] = 0;
60		handled_sigs[i - 1] = 0;
61	}
62
63	/* Clear the lock: */
64	signal_lock.access_lock = 0;
65}
66
67void
68_thread_sig_handler(int sig, int code, ucontext_t * scp)
69{
70	char	c;
71	int	i;
72
73	/* Check if an interval timer signal: */
74	if (sig == _SCHED_SIGNAL) {
75		if (_thread_kern_in_sched != 0) {
76			/*
77			 * The scheduler is already running; ignore this
78			 * signal.
79			 */
80		}
81		/*
82		 * Check if the scheduler interrupt has come when
83		 * the currently running thread has deferred thread
84		 * signals.
85		 */
86		else if (_thread_run->sig_defer_count > 0)
87			_thread_run->yield_on_sig_undefer = 1;
88
89		else {
90			/*
91			 * Schedule the next thread. This function is not
92			 * expected to return because it will do a longjmp
93			 * instead.
94			 */
95			_thread_kern_sched(scp);
96
97			/*
98			 * This point should not be reached, so abort the
99			 * process:
100			 */
101			PANIC("Returned to signal function from scheduler");
102		}
103	}
104	/*
105	 * Check if the kernel has been interrupted while the scheduler
106	 * is accessing the scheduling queues or if there is a currently
107	 * running thread that has deferred signals.
108	 */
109	else if ((_queue_signals != 0) || ((_thread_kern_in_sched == 0) &&
110	    (_thread_run->sig_defer_count > 0))) {
111		/* Cast the signal number to a character variable: */
112		c = sig;
113
114		/*
115		 * Write the signal number to the kernel pipe so that it will
116		 * be ready to read when this signal handler returns.
117		 */
118		_thread_sys_write(_thread_kern_pipe[1], &c, 1);
119
120		/* Indicate that there are queued signals in the pipe. */
121		_sigq_check_reqd = 1;
122	}
123	else {
124		if (_atomic_lock(&signal_lock.access_lock)) {
125			/* There is another signal handler running: */
126			pending_sigs[sig - 1]++;
127			check_pending = 1;
128		}
129		else {
130			/* It's safe to handle the signal now. */
131			_thread_sig_handle(sig, scp);
132
133			/* Reset the pending and handled count back to 0: */
134			pending_sigs[sig - 1] = 0;
135			handled_sigs[sig - 1] = 0;
136
137			signal_lock.access_lock = 0;
138		}
139
140		/* Enter a loop to process pending signals: */
141		while ((check_pending != 0) &&
142		    (_atomic_lock(&signal_lock.access_lock) == 0)) {
143			check_pending = 0;
144			for (i = 1; i < NSIG; i++) {
145				if (pending_sigs[i - 1] > handled_sigs[i - 1])
146					_thread_sig_handle(i, scp);
147			}
148			signal_lock.access_lock = 0;
149		}
150	}
151}
152
153void
154_thread_sig_handle(int sig, ucontext_t * scp)
155{
156	int		i;
157	pthread_t	pthread, pthread_next;
158
159	/* Check if the signal requires a dump of thread information: */
160	if (sig == SIGINFO)
161		/* Dump thread information to file: */
162		_thread_dump_info();
163
164	/* Check if an interval timer signal: */
165	else if (sig == _SCHED_SIGNAL) {
166		/*
167		 * This shouldn't ever occur (should this panic?).
168		 */
169	} else {
170		/* Check if a child has terminated: */
171		if (sig == SIGCHLD) {
172			/*
173			 * Go through the file list and set all files
174			 * to non-blocking again in case the child
175			 * set some of them to block. Sigh.
176			 */
177			for (i = 0; i < _thread_dtablesize; i++) {
178				/* Check if this file is used: */
179				if (_thread_fd_table[i] != NULL) {
180					/*
181					 * Set the file descriptor to
182					 * non-blocking:
183					 */
184					_thread_sys_fcntl(i, F_SETFL,
185					    _thread_fd_table[i]->flags |
186					    O_NONBLOCK);
187				}
188			}
189		}
190
191		/*
192		 * POSIX says that pending SIGCONT signals are
193		 * discarded when one of these signals occurs.
194		 */
195		if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) {
196			/*
197			 * Enter a loop to discard pending SIGCONT
198			 * signals:
199			 */
200			TAILQ_FOREACH(pthread, &_thread_list, tle) {
201				sigdelset(&pthread->sigpend,SIGCONT);
202			}
203		}
204
205		/*
206		 * Enter a loop to process each thread in the waiting
207		 * list that is sigwait-ing on a signal.  Since POSIX
208		 * doesn't specify which thread will get the signal
209		 * if there are multiple waiters, we'll give it to the
210		 * first one we find.
211		 */
212		for (pthread = TAILQ_FIRST(&_waitingq);
213		    pthread != NULL; pthread = pthread_next) {
214			/*
215			 * Grab the next thread before possibly destroying
216			 * the link entry.
217			 */
218			pthread_next = TAILQ_NEXT(pthread, pqe);
219
220			if ((pthread->state == PS_SIGWAIT) &&
221			    sigismember(pthread->data.sigwait, sig)) {
222				/* Change the state of the thread to run: */
223				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
224
225				/* Return the signal number: */
226				pthread->signo = sig;
227
228				/*
229				 * Do not attempt to deliver this signal
230				 * to other threads.
231				 */
232				return;
233			}
234		}
235
236		/* Check if the signal is not being ignored: */
237		if (_thread_sigact[sig - 1].sa_handler != SIG_IGN)
238			/*
239			 * Enter a loop to process each thread in the linked
240			 * list:
241			 */
242			TAILQ_FOREACH(pthread, &_thread_list, tle) {
243				pthread_t pthread_saved = _thread_run;
244
245				/* Current thread inside critical region? */
246				if (_thread_run->sig_defer_count > 0)
247					pthread->sig_defer_count++;
248
249				_thread_run = pthread;
250				_thread_signal(pthread,sig);
251
252				/*
253				 * Dispatch pending signals to the
254				 * running thread:
255				 */
256				_dispatch_signals();
257				_thread_run = pthread_saved;
258
259				/* Current thread inside critical region? */
260				if (_thread_run->sig_defer_count > 0)
261					pthread->sig_defer_count--;
262			}
263	}
264
265	/* Returns nothing. */
266	return;
267}
268
269/* Perform thread specific actions in response to a signal: */
270void
271_thread_signal(pthread_t pthread, int sig)
272{
273	/*
274	 * Flag the signal as pending. It will be dispatched later.
275	 */
276	sigaddset(&pthread->sigpend,sig);
277
278	/*
279	 * Process according to thread state:
280	 */
281	switch (pthread->state) {
282	/*
283	 * States which do not change when a signal is trapped:
284	 */
285	case PS_COND_WAIT:
286	case PS_DEAD:
287	case PS_FDLR_WAIT:
288	case PS_FDLW_WAIT:
289	case PS_FILE_WAIT:
290	case PS_JOIN:
291	case PS_MUTEX_WAIT:
292	case PS_RUNNING:
293	case PS_STATE_MAX:
294	case PS_SIGTHREAD:
295	case PS_SIGWAIT:
296	case PS_SUSPENDED:
297		/* Nothing to do here. */
298		break;
299
300	/*
301	 * The wait state is a special case due to the handling of
302	 * SIGCHLD signals.
303	 */
304	case PS_WAIT_WAIT:
305		/*
306		 * Check for signals other than the death of a child
307		 * process:
308		 */
309		if (sig != SIGCHLD)
310			/* Flag the operation as interrupted: */
311			pthread->interrupted = 1;
312
313		/* Change the state of the thread to run: */
314		PTHREAD_NEW_STATE(pthread,PS_RUNNING);
315
316		/* Return the signal number: */
317		pthread->signo = sig;
318		break;
319
320	/*
321	 * States that are interrupted by the occurrence of a signal
322	 * other than the scheduling alarm:
323	 */
324	case PS_FDR_WAIT:
325	case PS_FDW_WAIT:
326	case PS_POLL_WAIT:
327	case PS_SLEEP_WAIT:
328	case PS_SELECT_WAIT:
329		if (sig != SIGCHLD ||
330		    _thread_sigact[sig - 1].sa_handler != SIG_DFL) {
331			/* Flag the operation as interrupted: */
332			pthread->interrupted = 1;
333
334			if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
335				PTHREAD_WORKQ_REMOVE(pthread);
336
337			/* Change the state of the thread to run: */
338			PTHREAD_NEW_STATE(pthread,PS_RUNNING);
339
340			/* Return the signal number: */
341			pthread->signo = sig;
342		}
343		break;
344
345	case PS_SIGSUSPEND:
346		/*
347		 * Only wake up the thread if the signal is unblocked
348		 * and there is a handler installed for the signal.
349		 */
350		if (!sigismember(&pthread->sigmask, sig) &&
351		    _thread_sigact[sig - 1].sa_handler != SIG_DFL) {
352			/* Change the state of the thread to run: */
353			PTHREAD_NEW_STATE(pthread,PS_RUNNING);
354
355			/* Return the signal number: */
356			pthread->signo = sig;
357		}
358		break;
359	}
360}
361
362/* Dispatch pending signals to the running thread: */
363void
364_dispatch_signals()
365{
366	sigset_t sigset;
367	int i;
368
369	/*
370	 * Check if there are pending signals for the running
371	 * thread that aren't blocked:
372	 */
373	sigset = _thread_run->sigpend;
374	SIGSETNAND(sigset, _thread_run->sigmask);
375	if (SIGNOTEMPTY(sigset))
376		/* Look for all possible pending signals: */
377		for (i = 1; i < NSIG; i++)
378			/*
379			 * Check that a custom handler is installed
380			 * and if the signal is not blocked:
381			 */
382			if (_thread_sigact[i - 1].sa_handler != SIG_DFL &&
383			    _thread_sigact[i - 1].sa_handler != SIG_IGN &&
384			    sigismember(&_thread_run->sigpend,i) &&
385			    !sigismember(&_thread_run->sigmask,i)) {
386				/* Clear the pending signal: */
387				sigdelset(&_thread_run->sigpend,i);
388
389				/*
390				 * Dispatch the signal via the custom signal
391				 * handler:
392				 */
393				(*(_thread_sigact[i - 1].sa_handler))(i);
394			}
395}
396#endif
397