diff options
Diffstat (limited to 'lib/libpthread/thread/thr_sig.c')
-rw-r--r-- | lib/libpthread/thread/thr_sig.c | 299 |
1 files changed, 227 insertions, 72 deletions
diff --git a/lib/libpthread/thread/thr_sig.c b/lib/libpthread/thread/thr_sig.c index e73d5a2..1e5ee63 100644 --- a/lib/libpthread/thread/thr_sig.c +++ b/lib/libpthread/thread/thr_sig.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,16 +38,79 @@ #include <pthread.h> #include "pthread_private.h" +/* Static variables: */ +static int volatile yield_on_unlock_dead = 0; +static int volatile yield_on_unlock_thread = 0; +static long volatile thread_dead_lock = 0; +static long volatile thread_link_list_lock = 0; + +/* Lock the thread list: */ +void +_lock_thread_list() +{ + /* Lock the thread list: */ + _spinlock(&thread_link_list_lock); +} + +/* Lock the dead thread list: */ +void +_lock_dead_thread_list() +{ + /* Lock the dead thread list: */ + _spinlock(&thread_dead_lock); +} + +/* Lock the thread list: */ +void +_unlock_thread_list() +{ + /* Unlock the thread list: */ + _atomic_unlock(&thread_link_list_lock); + + /* + * Check if a scheduler interrupt occurred while the thread + * list was locked: + */ + if (yield_on_unlock_thread) { + /* Reset the interrupt flag: */ + yield_on_unlock_thread = 0; + + /* This thread has overstayed it's welcome: */ + sched_yield(); + } +} + +/* Lock the dead thread list: */ +void +_unlock_dead_thread_list() +{ + /* Unlock the dead thread list: */ + _atomic_unlock(&thread_dead_lock); + + /* + * Check if a scheduler interrupt occurred while the dead + * thread list was locked: + */ + if (yield_on_unlock_dead) { + /* Reset the interrupt flag: */ + yield_on_unlock_dead = 0; + + /* This thread has overstayed it's welcome: */ + sched_yield(); + } +} + void _thread_sig_handler(int sig, int code, struct sigcontext * scp) { char c; int i; + int dispatch = 0; pthread_t pthread; /* * Check if the pthread kernel has unblocked signals (or is about to) - * and was on its way into a _thread_sys_select when the current + * and was on its way into a _select when the current * signal interrupted it: */ if (_thread_kern_in_select) { @@ -57,51 +120,65 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp) /* * Write the signal number to the kernel pipe so that it will * be ready to read when this signal handler returns. This - * means that the _thread_sys_select call will complete + * means that the _select call will complete * immediately. */ - if (_thread_sys_write(_thread_kern_pipe[1], &c, 1) != 1) { - } + _thread_sys_write(_thread_kern_pipe[1], &c, 1); } + /* Check if the signal requires a dump of thread information: */ - if (sig == SIGINFO) { + if (sig == SIGINFO) /* Dump thread information to file: */ _thread_dump_info(); - } else { - /* Handle depending on signal type: */ - switch (sig) { - /* Interval timer used for timeslicing: */ - case SIGVTALRM: + + /* Check if an interval timer signal: */ + else if (sig == SIGVTALRM) { + /* Check if the scheduler interrupt has come at an + * unfortunate time which one of the threads is + * modifying the thread list: + */ + if (thread_link_list_lock) /* - * Don't add the signal to any thread. Just want to - * call the scheduler: + * Set a flag so that the thread that has + * the lock yields when it unlocks the + * thread list: */ - break; + yield_on_unlock_thread = 1; - /* Child termination: */ - case SIGCHLD: + /* Check if the scheduler interrupt has come at an + * unfortunate time which one of the threads is + * modifying the dead thread list: + */ + if (thread_dead_lock) /* - * Enter a loop to process each thread in the linked - * list: + * Set a flag so that the thread that has + * the lock yields when it unlocks the + * dead thread list: */ - for (pthread = _thread_link_list; pthread != NULL; - pthread = pthread->nxt) { - /* - * Add the signal to the set of pending - * signals: - */ - pthread->sigpend[sig] += 1; - if (pthread->state == PS_WAIT_WAIT) { - /* Reset the error: */ - /* There should be another flag so that this is not required! ### */ - _thread_seterrno(pthread, 0); - - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); - } - } + yield_on_unlock_dead = 1; + + /* + * Check if the kernel has not been interrupted while + * executing scheduler code: + */ + else if (!_thread_kern_in_sched) { + /* + * Schedule the next thread. This function is not + * expected to return because it will do a longjmp + * instead. + */ + _thread_kern_sched(scp); /* + * This point should not be reached, so abort the + * process: + */ + PANIC("Returned to signal function from scheduler"); + } + } else { + /* Check if a child has terminated: */ + if (sig == SIGCHLD) { + /* * Go through the file list and set all files * to non-blocking again in case the child * set some of them to block. Sigh. @@ -109,59 +186,137 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp) for (i = 0; i < _thread_dtablesize; i++) { /* Check if this file is used: */ if (_thread_fd_table[i] != NULL) { - /* Set the file descriptor to non-blocking: */ - _thread_sys_fcntl(i, F_SETFL, _thread_fd_table[i]->flags | O_NONBLOCK); + /* + * Set the file descriptor to + * non-blocking: + */ + _thread_sys_fcntl(i, F_SETFL, + _thread_fd_table[i]->flags | + O_NONBLOCK); } } - break; + } - /* Signals specific to the running thread: */ - case SIGBUS: - case SIGEMT: - case SIGFPE: - case SIGILL: - case SIGPIPE: - case SIGSEGV: - case SIGSYS: - /* Add the signal to the set of pending signals: */ - _thread_run->sigpend[sig] += 1; - break; + /* + * POSIX says that pending SIGCONT signals are + * discarded when one of there signals occurs. + */ + if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) { + /* + * Enter a loop to discard pending SIGCONT + * signals: + */ + for (pthread = _thread_link_list; + pthread != NULL; + pthread = pthread->nxt) + sigdelset(&pthread->sigpend,SIGCONT); + } - /* Signals to send to all threads: */ - default: + /* Check if the signal is not being ignored: */ + if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) /* * Enter a loop to process each thread in the linked * list: */ for (pthread = _thread_link_list; pthread != NULL; - pthread = pthread->nxt) { - /* - * Add the signal to the set of pending - * signals: - */ - pthread->sigpend[sig] += 1; - } + pthread = pthread->nxt) + _thread_signal(pthread,sig); + + /* Dispatch pending signals to the running thread: */ + _dispatch_signals(); + } + + /* Returns nothing. */ + return; +} + +/* Perform thread specific actions in response to a signal: */ +void +_thread_signal(pthread_t pthread, int sig) +{ + pthread_t saved; + struct sigaction act; + + /* + * Flag the signal as pending. It will be dispatched later. + */ + sigaddset(&pthread->sigpend,sig); + + /* Check if system calls are not restarted: */ + if ((_thread_sigact[sig - 1].sa_flags & SA_RESTART) == 0) { + /* + * Process according to thread state: + */ + switch (pthread->state) { + /* + * States which do not change when a signal is trapped: + */ + case PS_COND_WAIT: + case PS_DEAD: + case PS_FDLR_WAIT: + case PS_FDLW_WAIT: + case PS_FILE_WAIT: + case PS_JOIN: + case PS_MUTEX_WAIT: + case PS_RUNNING: + case PS_STATE_MAX: + case PS_SIGTHREAD: + case PS_SUSPENDED: + /* Nothing to do here. */ + break; + + /* + * States that are interrupted by the occurrence of a signal + * other than the scheduling alarm: + */ + case PS_FDR_WAIT: + case PS_FDW_WAIT: + case PS_SLEEP_WAIT: + case PS_SIGWAIT: + case PS_WAIT_WAIT: + case PS_SELECT_WAIT: + /* Flag the operation as interrupted: */ + pthread->interrupted = 1; + + /* Change the state of the thread to run: */ + PTHREAD_NEW_STATE(pthread,PS_RUNNING); + + /* Return the signal number: */ + pthread->signo = sig; break; } + } +} - /* Check if the kernel is not locked: */ - if (_thread_run != &_thread_kern_thread) { - /* - * Schedule the next thread. This function is not - * expected to return because it will do a longjmp - * instead. - */ - _thread_kern_sched(scp); +/* Dispatch pending signals to the running thread: */ +void +_dispatch_signals() +{ + int i; + /* + * Check if there are pending signals for the running + * thread that aren't blocked: + */ + if ((_thread_run->sigpend & ~_thread_run->sigmask) != 0) + /* Look for all possible pending signals: */ + for (i = 1; i < NSIG; i++) /* - * This point should not be reached, so abort the - * process: + * Check that a custom handler is installed + * and if the signal is not blocked: */ - PANIC("Returned to signal function from scheduler"); - } - } + if (_thread_sigact[i - 1].sa_handler != SIG_DFL && + _thread_sigact[i - 1].sa_handler != SIG_IGN && + sigismember(&_thread_run->sigpend,i) && + !sigismember(&_thread_run->sigmask,i)) { + /* Clear the pending signal: */ + sigdelset(&_thread_run->sigpend,i); - /* Returns nothing. */ - return; + /* + * Dispatch the signal via the custom signal + * handler: + */ + (*(_thread_sigact[i - 1].sa_handler))(i); + } } #endif |