]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc_r/uthread/uthread_sig.c
Add RCS IDs to those files without them.
[FreeBSD/FreeBSD.git] / lib / libc_r / uthread / uthread_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 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  * $Id: uthread_sig.c,v 1.17 1999/06/20 08:28:44 jb Exp $
33  */
34 #include <signal.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #ifdef _THREAD_SAFE
39 #include <pthread.h>
40 #include "pthread_private.h"
41
42 /* Static variables: */
43 static spinlock_t       signal_lock = _SPINLOCK_INITIALIZER;
44 unsigned int            pending_sigs[NSIG];
45 unsigned int            handled_sigs[NSIG];
46 int                     volatile check_pending = 0;
47
48 /* Initialize signal handling facility: */
49 void
50 _thread_sig_init(void)
51 {
52         int i;
53
54         /* Clear pending and handled signal counts: */
55         for (i = 1; i < NSIG; i++) {
56                 pending_sigs[i - 1] = 0;
57                 handled_sigs[i - 1] = 0;
58         }
59
60         /* Clear the lock: */
61         signal_lock.access_lock = 0;
62 }
63
64 void
65 _thread_sig_handler(int sig, int code, struct sigcontext * scp)
66 {
67         char    c;
68         int     i;
69
70         /* Check if an interval timer signal: */
71         if (sig == _SCHED_SIGNAL) {
72                 if (_thread_kern_in_sched != 0) {
73                         /*
74                          * The scheduler is already running; ignore this
75                          * signal.
76                          */
77                 }
78                 /*
79                  * Check if the scheduler interrupt has come when
80                  * the currently running thread has deferred thread
81                  * signals.
82                  */
83                 else if (_thread_run->sig_defer_count > 0)
84                         _thread_run->yield_on_sig_undefer = 1;
85
86                 else {
87                         /*
88                          * Schedule the next thread. This function is not
89                          * expected to return because it will do a longjmp
90                          * instead. 
91                          */
92                         _thread_kern_sched(scp);
93
94                         /*
95                          * This point should not be reached, so abort the
96                          * process: 
97                          */
98                         PANIC("Returned to signal function from scheduler");
99                 }
100         }
101         /*
102          * Check if the kernel has been interrupted while the scheduler
103          * is accessing the scheduling queues or if there is a currently
104          * running thread that has deferred signals.
105          */
106         else if ((_queue_signals != 0) || ((_thread_kern_in_sched == 0) &&
107             (_thread_run->sig_defer_count > 0))) {
108                 /* Cast the signal number to a character variable: */
109                 c = sig;
110
111                 /*
112                  * Write the signal number to the kernel pipe so that it will
113                  * be ready to read when this signal handler returns.
114                  */
115                 _thread_sys_write(_thread_kern_pipe[1], &c, 1);
116
117                 /* Indicate that there are queued signals in the pipe. */
118                 _sigq_check_reqd = 1;
119         }
120         else {
121                 if (_atomic_lock(&signal_lock.access_lock)) {
122                         /* There is another signal handler running: */
123                         pending_sigs[sig - 1]++;
124                         check_pending = 1;
125                 }
126                 else {
127                         /* It's safe to handle the signal now. */
128                         _thread_sig_handle(sig, scp);
129
130                         /* Reset the pending and handled count back to 0: */
131                         pending_sigs[sig - 1] = 0;
132                         handled_sigs[sig - 1] = 0;
133
134                         signal_lock.access_lock = 0;
135                 }
136
137                 /* Enter a loop to process pending signals: */
138                 while ((check_pending != 0) &&
139                     (_atomic_lock(&signal_lock.access_lock) == 0)) {
140                         check_pending = 0;
141                         for (i = 1; i < NSIG; i++) {
142                                 if (pending_sigs[i - 1] > handled_sigs[i - 1])
143                                         _thread_sig_handle(i, scp);
144                         }
145                         signal_lock.access_lock = 0;
146                 }
147         }
148 }
149
150 void
151 _thread_sig_handle(int sig, struct sigcontext * scp)
152 {
153         int             i;
154         pthread_t       pthread, pthread_next;
155
156         /* Check if the signal requires a dump of thread information: */
157         if (sig == SIGINFO)
158                 /* Dump thread information to file: */
159                 _thread_dump_info();
160
161         /* Check if an interval timer signal: */
162         else if (sig == _SCHED_SIGNAL) {
163                 /*
164                  * This shouldn't ever occur (should this panic?).
165                  */
166         } else {
167                 /* Check if a child has terminated: */
168                 if (sig == SIGCHLD) {
169                         /*
170                          * Go through the file list and set all files
171                          * to non-blocking again in case the child
172                          * set some of them to block. Sigh.
173                          */
174                         for (i = 0; i < _thread_dtablesize; i++) {
175                                 /* Check if this file is used: */
176                                 if (_thread_fd_table[i] != NULL) {
177                                         /*
178                                          * Set the file descriptor to
179                                          * non-blocking:
180                                          */
181                                         _thread_sys_fcntl(i, F_SETFL,
182                                             _thread_fd_table[i]->flags |
183                                             O_NONBLOCK);
184                                 }
185                         }
186                 }
187
188                 /*
189                  * POSIX says that pending SIGCONT signals are
190                  * discarded when one of these signals occurs.
191                  */
192                 if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) {
193                         /*
194                          * Enter a loop to discard pending SIGCONT
195                          * signals:
196                          */
197                         TAILQ_FOREACH(pthread, &_thread_list, tle) {
198                                 sigdelset(&pthread->sigpend,SIGCONT);
199                         }
200                 }
201
202                 /*
203                  * Enter a loop to process each thread in the waiting
204                  * list that is sigwait-ing on a signal.  Since POSIX
205                  * doesn't specify which thread will get the signal
206                  * if there are multiple waiters, we'll give it to the
207                  * first one we find.
208                  */
209                 for (pthread = TAILQ_FIRST(&_waitingq);
210                     pthread != NULL; pthread = pthread_next) {
211                         /*
212                          * Grab the next thread before possibly destroying
213                          * the link entry.
214                          */
215                         pthread_next = TAILQ_NEXT(pthread, pqe);
216
217                         if ((pthread->state == PS_SIGWAIT) &&
218                             sigismember(pthread->data.sigwait, sig)) {
219                                 /* Change the state of the thread to run: */
220                                 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
221
222                                 /* Return the signal number: */
223                                 pthread->signo = sig;
224
225                                 /*
226                                  * Do not attempt to deliver this signal
227                                  * to other threads.
228                                  */
229                                 return;
230                         }
231                 }
232
233                 /* Check if the signal is not being ignored: */
234                 if (_thread_sigact[sig - 1].sa_handler != SIG_IGN)
235                         /*
236                          * Enter a loop to process each thread in the linked
237                          * list: 
238                          */
239                         TAILQ_FOREACH(pthread, &_thread_list, tle) {
240                                 pthread_t pthread_saved = _thread_run;
241
242                                 /* Current thread inside critical region? */
243                                 if (_thread_run->sig_defer_count > 0)
244                                         pthread->sig_defer_count++;
245
246                                 _thread_run = pthread;
247                                 _thread_signal(pthread,sig);
248
249                                 /*
250                                  * Dispatch pending signals to the
251                                  * running thread:
252                                  */
253                                 _dispatch_signals();
254                                 _thread_run = pthread_saved;
255
256                                 /* Current thread inside critical region? */
257                                 if (_thread_run->sig_defer_count > 0)
258                                         pthread->sig_defer_count--;
259                         }
260         }
261
262         /* Returns nothing. */
263         return;
264 }
265
266 /* Perform thread specific actions in response to a signal: */
267 void
268 _thread_signal(pthread_t pthread, int sig)
269 {
270         /*
271          * Flag the signal as pending. It will be dispatched later.
272          */
273         sigaddset(&pthread->sigpend,sig);
274
275         /*
276          * Process according to thread state:
277          */
278         switch (pthread->state) {
279         /*
280          * States which do not change when a signal is trapped:
281          */
282         case PS_COND_WAIT:
283         case PS_DEAD:
284         case PS_FDLR_WAIT:
285         case PS_FDLW_WAIT:
286         case PS_FILE_WAIT:
287         case PS_JOIN:
288         case PS_MUTEX_WAIT:
289         case PS_RUNNING:
290         case PS_STATE_MAX:
291         case PS_SIGTHREAD:
292         case PS_SIGWAIT:
293         case PS_SUSPENDED:
294                 /* Nothing to do here. */
295                 break;
296
297         /*
298          * The wait state is a special case due to the handling of
299          * SIGCHLD signals.
300          */
301         case PS_WAIT_WAIT:
302                 /*
303                  * Check for signals other than the death of a child
304                  * process:
305                  */
306                 if (sig != SIGCHLD)
307                         /* Flag the operation as interrupted: */
308                         pthread->interrupted = 1;
309
310                 /* Change the state of the thread to run: */
311                 PTHREAD_NEW_STATE(pthread,PS_RUNNING);
312
313                 /* Return the signal number: */
314                 pthread->signo = sig;
315                 break;
316
317         /*
318          * States that are interrupted by the occurrence of a signal
319          * other than the scheduling alarm: 
320          */
321         case PS_FDR_WAIT:
322         case PS_FDW_WAIT:
323         case PS_POLL_WAIT:
324         case PS_SLEEP_WAIT:
325         case PS_SELECT_WAIT:
326                 if (sig != SIGCHLD ||
327                     _thread_sigact[sig - 1].sa_handler != SIG_DFL) {
328                         /* Flag the operation as interrupted: */
329                         pthread->interrupted = 1;
330
331                         if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
332                                 PTHREAD_WORKQ_REMOVE(pthread);
333
334                         /* Change the state of the thread to run: */
335                         PTHREAD_NEW_STATE(pthread,PS_RUNNING);
336
337                         /* Return the signal number: */
338                         pthread->signo = sig;
339                 }
340                 break;
341
342         case PS_SIGSUSPEND:
343                 /*
344                  * Only wake up the thread if the signal is unblocked
345                  * and there is a handler installed for the signal.
346                  */
347                 if (!sigismember(&pthread->sigmask, sig) &&
348                     _thread_sigact[sig - 1].sa_handler != SIG_DFL) {
349                         /* Change the state of the thread to run: */
350                         PTHREAD_NEW_STATE(pthread,PS_RUNNING);
351
352                         /* Return the signal number: */
353                         pthread->signo = sig;
354                 }
355                 break;
356         }
357 }
358
359 /* Dispatch pending signals to the running thread: */
360 void
361 _dispatch_signals()
362 {
363         int i;
364
365         /*
366          * Check if there are pending signals for the running
367          * thread that aren't blocked:
368          */
369         if ((_thread_run->sigpend & ~_thread_run->sigmask) != 0)
370                 /* Look for all possible pending signals: */
371                 for (i = 1; i < NSIG; i++)
372                         /*
373                          * Check that a custom handler is installed
374                          * and if the signal is not blocked:
375                          */
376                         if (_thread_sigact[i - 1].sa_handler != SIG_DFL &&
377                             _thread_sigact[i - 1].sa_handler != SIG_IGN &&
378                             sigismember(&_thread_run->sigpend,i) &&
379                             !sigismember(&_thread_run->sigmask,i)) {
380                                 /* Clear the pending signal: */
381                                 sigdelset(&_thread_run->sigpend,i);
382
383                                 /*
384                                  * Dispatch the signal via the custom signal
385                                  * handler:
386                                  */
387                                 (*(_thread_sigact[i - 1].sa_handler))(i);
388                         }
389 }
390 #endif