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