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