diff options
Diffstat (limited to 'lib/libkse/thread/thr_sig.c')
-rw-r--r-- | lib/libkse/thread/thr_sig.c | 210 |
1 files changed, 119 insertions, 91 deletions
diff --git a/lib/libkse/thread/thr_sig.c b/lib/libkse/thread/thr_sig.c index e51d949..ac475a4 100644 --- a/lib/libkse/thread/thr_sig.c +++ b/lib/libkse/thread/thr_sig.c @@ -29,6 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * $Id$ */ #include <signal.h> #include <fcntl.h> @@ -38,107 +39,51 @@ #include <pthread.h> #include "pthread_private.h" -/* - * State change macro for signal handler: - */ -#define PTHREAD_SIG_NEW_STATE(thrd, newstate) { \ - if ((_thread_run->sched_defer_count == 0) && \ - (_thread_kern_in_sched == 0)) { \ - PTHREAD_NEW_STATE(thrd, newstate); \ - } else { \ - _waitingq_check_reqd = 1; \ - PTHREAD_SET_STATE(thrd, newstate); \ - } \ -} - /* Static variables: */ -static int volatile yield_on_unlock_thread = 0; -static spinlock_t thread_link_list_lock = _SPINLOCK_INITIALIZER; - -/* Lock the thread list: */ -void -_lock_thread_list() -{ - /* Lock the thread list: */ - _SPINLOCK(&thread_link_list_lock); -} +static spinlock_t signal_lock = _SPINLOCK_INITIALIZER; +unsigned int pending_sigs[NSIG]; +unsigned int handled_sigs[NSIG]; +int volatile check_pending = 0; -/* Lock the thread list: */ +/* Initialize signal handling facility: */ void -_unlock_thread_list() +_thread_sig_init(void) { - /* Unlock the thread list: */ - _SPINUNLOCK(&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; + int i; - /* This thread has overstayed it's welcome: */ - sched_yield(); + /* Clear pending and handled signal counts: */ + for (i = 1; i < NSIG; i++) { + pending_sigs[i - 1] = 0; + handled_sigs[i - 1] = 0; } + + /* Clear the lock: */ + signal_lock.access_lock = 0; } void _thread_sig_handler(int sig, int code, struct sigcontext * scp) { - char c; - int i; - pthread_t pthread; - - /* - * Check if the pthread kernel has unblocked signals (or is about to) - * and was on its way into a _select when the current - * signal interrupted it: - */ - if (_thread_kern_in_select) { - /* Cast the signal number to a character variable: */ - c = sig; - - /* - * 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 _select call will complete - * immediately. - */ - _thread_sys_write(_thread_kern_pipe[1], &c, 1); - } - /* Check if the signal requires a dump of thread information: */ - if (sig == SIGINFO) - /* Dump thread information to file: */ - _thread_dump_info(); + char c; + int i; /* Check if an interval timer signal: */ - else if (sig == _SCHED_SIGNAL) { - /* 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.access_lock) + if (sig == _SCHED_SIGNAL) { + if (_thread_kern_in_sched != 0) { /* - * Set a flag so that the thread that has - * the lock yields when it unlocks the - * thread list: + * The scheduler is already running; ignore this + * signal. */ - yield_on_unlock_thread = 1; - + } /* * Check if the scheduler interrupt has come when * the currently running thread has deferred thread - * scheduling. + * signals. */ - else if (_thread_run->sched_defer_count) - _thread_run->yield_on_sched_undefer = 1; + else if (_thread_run->sig_defer_count > 0) + _thread_run->yield_on_sig_undefer = 1; - /* - * Check if the kernel has not been interrupted while - * executing scheduler code: - */ - else if (!_thread_kern_in_sched) { + else { /* * Schedule the next thread. This function is not * expected to return because it will do a longjmp @@ -152,6 +97,72 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp) */ PANIC("Returned to signal function from scheduler"); } + } + /* + * Check if the kernel has been interrupted while the scheduler + * is accessing the scheduling queues or if there is a currently + * running thread that has deferred signals. + */ + else if ((_queue_signals != 0) || ((_thread_kern_in_sched == 0) && + (_thread_run->sig_defer_count > 0))) { + /* Cast the signal number to a character variable: */ + c = sig; + + /* + * Write the signal number to the kernel pipe so that it will + * be ready to read when this signal handler returns. + */ + _thread_sys_write(_thread_kern_pipe[1], &c, 1); + + /* Indicate that there are queued signals in the pipe. */ + _sigq_check_reqd = 1; + } + else { + if (_atomic_lock(&signal_lock.access_lock)) { + /* There is another signal handler running: */ + pending_sigs[sig - 1]++; + check_pending = 1; + } + else { + /* It's safe to handle the signal now. */ + _thread_sig_handle(sig, scp); + + /* Reset the pending and handled count back to 0: */ + pending_sigs[sig - 1] = 0; + handled_sigs[sig - 1] = 0; + + signal_lock.access_lock = 0; + } + + /* Enter a loop to process pending signals: */ + while ((check_pending != 0) && + (_atomic_lock(&signal_lock.access_lock) == 0)) { + check_pending = 0; + for (i = 1; i < NSIG; i++) { + if (pending_sigs[i - 1] > handled_sigs[i - 1]) + _thread_sig_handle(i, scp); + } + signal_lock.access_lock = 0; + } + } +} + +void +_thread_sig_handle(int sig, struct sigcontext * scp) +{ + int i; + pthread_t pthread, pthread_next; + + /* Check if the signal requires a dump of thread information: */ + if (sig == SIGINFO) + /* Dump thread information to file: */ + _thread_dump_info(); + + /* Check if an interval timer signal: */ + else if (sig == _SCHED_SIGNAL) { + /* + * This shouldn't ever occur (should this panic?). + */ } else { /* Check if a child has terminated: */ if (sig == SIGCHLD) { @@ -183,10 +194,9 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp) * Enter a loop to discard pending SIGCONT * signals: */ - for (pthread = _thread_link_list; - pthread != NULL; - pthread = pthread->nxt) + TAILQ_FOREACH(pthread, &_thread_list, tle) { sigdelset(&pthread->sigpend,SIGCONT); + } } /* @@ -196,11 +206,18 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp) * if there are multiple waiters, we'll give it to the * first one we find. */ - TAILQ_FOREACH(pthread, &_waitingq, pqe) { + for (pthread = TAILQ_FIRST(&_waitingq); + pthread != NULL; pthread = pthread_next) { + /* + * Grab the next thread before possibly destroying + * the link entry. + */ + pthread_next = TAILQ_NEXT(pthread, pqe); + if ((pthread->state == PS_SIGWAIT) && sigismember(pthread->data.sigwait, sig)) { /* Change the state of the thread to run: */ - PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING); + PTHREAD_NEW_STATE(pthread,PS_RUNNING); /* Return the signal number: */ pthread->signo = sig; @@ -219,10 +236,13 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp) * Enter a loop to process each thread in the linked * list: */ - for (pthread = _thread_link_list; pthread != NULL; - pthread = pthread->nxt) { + TAILQ_FOREACH(pthread, &_thread_list, tle) { pthread_t pthread_saved = _thread_run; + /* Current thread inside critical region? */ + if (_thread_run->sig_defer_count > 0) + pthread->sig_defer_count++; + _thread_run = pthread; _thread_signal(pthread,sig); @@ -232,6 +252,10 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp) */ _dispatch_signals(); _thread_run = pthread_saved; + + /* Current thread inside critical region? */ + if (_thread_run->sig_defer_count > 0) + pthread->sig_defer_count--; } } @@ -284,7 +308,7 @@ _thread_signal(pthread_t pthread, int sig) pthread->interrupted = 1; /* Change the state of the thread to run: */ - PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING); + PTHREAD_NEW_STATE(pthread,PS_RUNNING); /* Return the signal number: */ pthread->signo = sig; @@ -296,6 +320,7 @@ _thread_signal(pthread_t pthread, int sig) */ case PS_FDR_WAIT: case PS_FDW_WAIT: + case PS_POLL_WAIT: case PS_SLEEP_WAIT: case PS_SELECT_WAIT: if (sig != SIGCHLD || @@ -303,8 +328,11 @@ _thread_signal(pthread_t pthread, int sig) /* Flag the operation as interrupted: */ pthread->interrupted = 1; + if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ) + PTHREAD_WORKQ_REMOVE(pthread); + /* Change the state of the thread to run: */ - PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING); + PTHREAD_NEW_STATE(pthread,PS_RUNNING); /* Return the signal number: */ pthread->signo = sig; @@ -319,7 +347,7 @@ _thread_signal(pthread_t pthread, int sig) if (!sigismember(&pthread->sigmask, sig) && _thread_sigact[sig - 1].sa_handler != SIG_DFL) { /* Change the state of the thread to run: */ - PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING); + PTHREAD_NEW_STATE(pthread,PS_RUNNING); /* Return the signal number: */ pthread->signo = sig; |