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