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