thr_sig.c revision 37021
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 REGENTS 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 */
33#include <signal.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <errno.h>
37#ifdef _THREAD_SAFE
38#include <pthread.h>
39#include "pthread_private.h"
40
41/* Static variables: */
42static int		volatile yield_on_unlock_dead	= 0;
43static int		volatile yield_on_unlock_thread	= 0;
44static spinlock_t	thread_dead_lock	= _SPINLOCK_INITIALIZER;
45static spinlock_t	thread_link_list_lock	= _SPINLOCK_INITIALIZER;
46
47/* Lock the thread list: */
48void
49_lock_thread_list()
50{
51	/* Lock the thread list: */
52	_SPINLOCK(&thread_link_list_lock);
53}
54
55/* Lock the dead thread list: */
56void
57_lock_dead_thread_list()
58{
59	/* Lock the dead thread list: */
60	_SPINLOCK(&thread_dead_lock);
61}
62
63/* Lock the thread list: */
64void
65_unlock_thread_list()
66{
67	/* Unlock the thread list: */
68	_SPINUNLOCK(&thread_link_list_lock);
69
70	/*
71	 * Check if a scheduler interrupt occurred while the thread
72	 * list was locked:
73	 */
74	if (yield_on_unlock_thread) {
75		/* Reset the interrupt flag: */
76		yield_on_unlock_thread = 0;
77
78		/* This thread has overstayed it's welcome: */
79		sched_yield();
80	}
81}
82
83/* Lock the dead thread list: */
84void
85_unlock_dead_thread_list()
86{
87	/* Unlock the dead thread list: */
88	_SPINUNLOCK(&thread_dead_lock);
89
90	/*
91	 * Check if a scheduler interrupt occurred while the dead
92	 * thread list was locked:
93	 */
94	if (yield_on_unlock_dead) {
95		/* Reset the interrupt flag: */
96		yield_on_unlock_dead = 0;
97
98		/* This thread has overstayed it's welcome: */
99		sched_yield();
100	}
101}
102
103void
104_thread_sig_handler(int sig, int code, struct sigcontext * scp)
105{
106	char            c;
107	int             i;
108	int		dispatch = 0;
109	pthread_t       pthread;
110
111	/*
112	 * Check if the pthread kernel has unblocked signals (or is about to)
113	 * and was on its way into a _select when the current
114	 * signal interrupted it:
115	 */
116	if (_thread_kern_in_select) {
117		/* Cast the signal number to a character variable: */
118		c = sig;
119
120		/*
121		 * Write the signal number to the kernel pipe so that it will
122		 * be ready to read when this signal handler returns. This
123		 * means that the _select call will complete
124		 * immediately.
125		 */
126		_thread_sys_write(_thread_kern_pipe[1], &c, 1);
127	}
128
129	/* Check if the signal requires a dump of thread information: */
130	if (sig == SIGINFO)
131		/* Dump thread information to file: */
132		_thread_dump_info();
133
134	/* Check if an interval timer signal: */
135	else if (sig == SIGVTALRM) {
136		/* Check if the scheduler interrupt has come at an
137		 * unfortunate time which one of the threads is
138		 * modifying the thread list:
139		 */
140		if (thread_link_list_lock.access_lock)
141			/*
142			 * Set a flag so that the thread that has
143			 * the lock yields when it unlocks the
144			 * thread list:
145			 */
146			yield_on_unlock_thread = 1;
147
148		/* Check if the scheduler interrupt has come at an
149		 * unfortunate time which one of the threads is
150		 * modifying the dead thread list:
151		 */
152		if (thread_dead_lock.access_lock)
153			/*
154			 * Set a flag so that the thread that has
155			 * the lock yields when it unlocks the
156			 * dead thread list:
157			 */
158			yield_on_unlock_dead = 1;
159
160		/*
161		 * Check if the kernel has not been interrupted while
162		 * executing scheduler code:
163		 */
164		else if (!_thread_kern_in_sched) {
165			/*
166			 * Schedule the next thread. This function is not
167			 * expected to return because it will do a longjmp
168			 * instead.
169			 */
170			_thread_kern_sched(scp);
171
172			/*
173			 * This point should not be reached, so abort the
174			 * process:
175			 */
176			PANIC("Returned to signal function from scheduler");
177		}
178	} else {
179		/* Check if a child has terminated: */
180		if (sig == SIGCHLD) {
181			/*
182			 * Go through the file list and set all files
183			 * to non-blocking again in case the child
184			 * set some of them to block. Sigh.
185			 */
186			for (i = 0; i < _thread_dtablesize; i++) {
187				/* Check if this file is used: */
188				if (_thread_fd_table[i] != NULL) {
189					/*
190					 * Set the file descriptor to
191					 * non-blocking:
192					 */
193					_thread_sys_fcntl(i, F_SETFL,
194					    _thread_fd_table[i]->flags |
195					    O_NONBLOCK);
196				}
197			}
198		}
199
200		/*
201		 * POSIX says that pending SIGCONT signals are
202		 * discarded when one of there signals occurs.
203		 */
204		if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) {
205			/*
206			 * Enter a loop to discard pending SIGCONT
207			 * signals:
208			 */
209			for (pthread = _thread_link_list;
210			    pthread != NULL;
211			    pthread = pthread->nxt)
212				sigdelset(&pthread->sigpend,SIGCONT);
213		}
214
215		/* Check if the signal is not being ignored: */
216		if (_thread_sigact[sig - 1].sa_handler != SIG_IGN)
217			/*
218			 * Enter a loop to process each thread in the linked
219			 * list:
220			 */
221			for (pthread = _thread_link_list; pthread != NULL;
222			     pthread = pthread->nxt)
223				_thread_signal(pthread,sig);
224
225		/* Dispatch pending signals to the running thread: */
226		_dispatch_signals();
227	}
228
229	/* Returns nothing. */
230	return;
231}
232
233/* Perform thread specific actions in response to a signal: */
234void
235_thread_signal(pthread_t pthread, int sig)
236{
237	pthread_t saved;
238	struct sigaction act;
239
240	/*
241	 * Flag the signal as pending. It will be dispatched later.
242	 */
243	sigaddset(&pthread->sigpend,sig);
244
245	/*
246	 * Process according to thread state:
247	 */
248	switch (pthread->state) {
249	/*
250	 * States which do not change when a signal is trapped:
251	 */
252	case PS_COND_WAIT:
253	case PS_DEAD:
254	case PS_FDLR_WAIT:
255	case PS_FDLW_WAIT:
256	case PS_FILE_WAIT:
257	case PS_JOIN:
258	case PS_MUTEX_WAIT:
259	case PS_RUNNING:
260	case PS_STATE_MAX:
261	case PS_SIGTHREAD:
262	case PS_SUSPENDED:
263		/* Nothing to do here. */
264		break;
265
266	/*
267	 * The wait state is a special case due to the handling of
268	 * SIGCHLD signals.
269	 */
270	case PS_WAIT_WAIT:
271		/*
272		 * Check for signals other than the death of a child
273		 * process:
274		 */
275		if (sig != SIGCHLD)
276			/* Flag the operation as interrupted: */
277			pthread->interrupted = 1;
278
279		/* Change the state of the thread to run: */
280		PTHREAD_NEW_STATE(pthread,PS_RUNNING);
281
282		/* Return the signal number: */
283		pthread->signo = sig;
284		break;
285
286	/*
287	 * States that are interrupted by the occurrence of a signal
288	 * other than the scheduling alarm:
289	 */
290	case PS_FDR_WAIT:
291	case PS_FDW_WAIT:
292	case PS_SLEEP_WAIT:
293	case PS_SIGWAIT:
294	case PS_SELECT_WAIT:
295		/* Flag the operation as interrupted: */
296		pthread->interrupted = 1;
297
298		/* Change the state of the thread to run: */
299		PTHREAD_NEW_STATE(pthread,PS_RUNNING);
300
301		/* Return the signal number: */
302		pthread->signo = sig;
303		break;
304	}
305}
306
307/* Dispatch pending signals to the running thread: */
308void
309_dispatch_signals()
310{
311	int i;
312
313	/*
314	 * Check if there are pending signals for the running
315	 * thread that aren't blocked:
316	 */
317	if ((_thread_run->sigpend & ~_thread_run->sigmask) != 0)
318		/* Look for all possible pending signals: */
319		for (i = 1; i < NSIG; i++)
320			/*
321			 * Check that a custom handler is installed
322			 * and if the signal is not blocked:
323			 */
324			if (_thread_sigact[i - 1].sa_handler != SIG_DFL &&
325			    _thread_sigact[i - 1].sa_handler != SIG_IGN &&
326			    sigismember(&_thread_run->sigpend,i) &&
327			    !sigismember(&_thread_run->sigmask,i)) {
328				/* Clear the pending signal: */
329				sigdelset(&_thread_run->sigpend,i);
330
331				/*
332				 * Dispatch the signal via the custom signal
333				 * handler:
334				 */
335				(*(_thread_sigact[i - 1].sa_handler))(i);
336			}
337}
338#endif
339