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