diff options
Diffstat (limited to 'lib/libkse/thread/thr_sig.c')
-rw-r--r-- | lib/libkse/thread/thr_sig.c | 980 |
1 files changed, 553 insertions, 427 deletions
diff --git a/lib/libkse/thread/thr_sig.c b/lib/libkse/thread/thr_sig.c index fa63b5f..de177d9 100644 --- a/lib/libkse/thread/thr_sig.c +++ b/lib/libkse/thread/thr_sig.c @@ -35,25 +35,29 @@ #include <sys/types.h> #include <sys/signalvar.h> #include <signal.h> +#include <errno.h> #include <fcntl.h> #include <unistd.h> -#include <setjmp.h> -#include <errno.h> +#include <string.h> #include <pthread.h> #include "thr_private.h" +#include "pthread_md.h" /* Prototypes: */ -static void thread_sig_handle_special(int sig); - -static void thread_sig_add(struct pthread *pthread, int sig, int has_args); -static void thread_sig_check_state(struct pthread *pthread, int sig); -static struct pthread *thread_sig_find(int sig); -static void thread_sigframe_add(struct pthread *thread, int sig, - int has_args); -static void thread_sigframe_save(struct pthread *thread, - struct pthread_state_data *psd); -static void thread_sigframe_restore(struct pthread *thread, - struct pthread_state_data *psd); +static void build_siginfo(siginfo_t *info, int signo); +static void thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info); +static void thr_sig_check_state(struct pthread *pthread, int sig); +static struct pthread *thr_sig_find(struct kse *curkse, int sig, + siginfo_t *info); +static void handle_special_signals(struct kse *curkse, int sig); +static void thr_sigframe_add(struct pthread *thread, int sig, + siginfo_t *info); +static void thr_sigframe_restore(struct pthread *thread, + struct pthread_sigframe *psf); +static void thr_sigframe_save(struct pthread *thread, + struct pthread_sigframe *psf); +static void thr_sig_invoke_handler(struct pthread *, int sig, + siginfo_t *info, ucontext_t *ucp); /* #define DEBUG_SIGNAL */ #ifdef DEBUG_SIGNAL @@ -63,55 +67,210 @@ static void thread_sigframe_restore(struct pthread *thread, #endif /* - * Dispatch a signal to a thread, if appropriate. + * Signal setup and delivery. + * + * 1) Delivering signals to threads in the same KSE. + * These signals are sent by upcall events and are set in the + * km_sigscaught field of the KSE mailbox. Since these signals + * are received while operating on the KSE stack, they can be + * delivered either by using signalcontext() to add a stack frame + * to the target thread's stack, or by adding them in the thread's + * pending set and having the thread run them down after it + * 2) Delivering signals to threads in other KSEs/KSEGs. + * 3) Delivering signals to threads in critical regions. + * 4) Delivering signals to threads after they change their signal masks. + * + * Methods of delivering signals. + * + * 1) Add a signal frame to the thread's saved context. + * 2) Add the signal to the thread structure, mark the thread as + * having signals to handle, and let the thread run them down + * after it resumes from the KSE scheduler. + * + * Problem with 1). You can't do this to a running thread or a + * thread in a critical region. + * + * Problem with 2). You can't do this to a thread that doesn't + * yield in some way (explicitly enters the scheduler). A thread + * blocked in the kernel or a CPU hungry thread will not see the + * signal without entering the scheduler. + * + * The solution is to use both 1) and 2) to deliver signals: + * + * o Thread in critical region - use 2). When the thread + * leaves the critical region it will check to see if it + * has pending signals and run them down. + * + * o Thread enters scheduler explicitly - use 2). The thread + * can check for pending signals after it returns from the + * the scheduler. + * + * o Thread is running and not current thread - use 2). When the + * thread hits a condition specified by one of the other bullets, + * the signal will be delivered. + * + * o Thread is running and is current thread (e.g., the thread + * has just changed its signal mask and now sees that it has + * pending signals) - just run down the pending signals. + * + * o Thread is swapped out due to quantum expiration - use 1) + * + * o Thread is blocked in kernel - kse_thr_wakeup() and then + * use 1) + */ + +/* + * Rules for selecting threads for signals received: + * + * 1) If the signal is a sychronous signal, it is delivered to + * the generating (current thread). If the thread has the + * signal masked, it is added to the threads pending signal + * set until the thread unmasks it. + * + * 2) A thread in sigwait() where the signal is in the thread's + * waitset. + * + * 3) A thread in sigsuspend() where the signal is not in the + * thread's suspended signal mask. + * + * 4) Any thread (first found/easiest to deliver) that has the + * signal unmasked. + */ + +/* + * This signal handler only delivers asynchronous signals. + * This must be called with upcalls disabled and without + * holding any locks. */ void -_thread_sig_dispatch(int sig) +_thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info) +{ + struct pthread *thread; + + DBG_MSG(">>> _thr_sig_dispatch(%d)\n", sig); + + /* Some signals need special handling: */ + handle_special_signals(curkse, sig); + + if ((thread = thr_sig_find(curkse, sig, info)) != NULL) { + /* + * Setup the target thread to receive the signal: + */ + DBG_MSG("Got signal %d, selecting thread %p\n", sig, thread); + KSE_SCHED_LOCK(curkse, thread->kseg); + thr_sig_add(thread, sig, info); + KSE_SCHED_UNLOCK(curkse, thread->kseg); + } +} + +void +_thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) +{ + void (*sigfunc)(int, siginfo_t *, void *); + struct kse *curkse; + + curkse = _get_curkse(); + if ((curkse == NULL) || ((curkse->k_flags & KF_STARTED) == 0)) { + /* Upcalls are not yet started; just call the handler. */ + sigfunc = _thread_sigact[sig - 1].sa_sigaction; + ucp->uc_sigmask = _thr_proc_sigmask; + if (((__sighandler_t *)sigfunc != SIG_DFL) && + ((__sighandler_t *)sigfunc != SIG_IGN)) { + if (((_thread_sigact[sig - 1].sa_flags & SA_SIGINFO) + != 0) || (info == NULL)) + (*(sigfunc))(sig, info, ucp); + else + (*(sigfunc))(sig, (siginfo_t *)info->si_code, + ucp); + } + } + else { + /* Nothing. */ + DBG_MSG("Got signal %d\n", sig); + sigaddset(&curkse->k_mbx.km_sigscaught, sig); + ucp->uc_sigmask = _thr_proc_sigmask; + } +} + +static void +thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info, + ucontext_t *ucp) { - struct pthread *pthread; + void (*sigfunc)(int, siginfo_t *, void *); + sigset_t saved_mask; + int saved_seqno; - DBG_MSG(">>> _thread_sig_dispatch(%d)\n", sig); + /* Invoke the signal handler without going through the scheduler: + */ + DBG_MSG("Got signal %d, calling handler for current thread %p\n", + sig, curthread); - thread_sig_handle_special(sig); - if (sigismember(&_thread_sigmask, sig)) - /* Don't deliver the signal if it's masked. */ - return; - /* Mask the signal until it's handled. */ - sigaddset(&_thread_sigmask, sig); - /* This signal will be handled; clear the pending flag. */ - sigdelset(&_thread_sigpending, sig); + /* + * Setup the threads signal mask. + * + * The mask is changed in the thread's active signal mask + * (in the context) and not in the base signal mask because + * a thread is allowed to change its signal mask within a + * signal handler. If it does, the signal mask restored + * after the handler should be the same as that set by the + * thread during the handler, not the original mask from + * before calling the handler. The thread could also + * modify the signal mask in the context and expect this + * mask to be used. + */ + THR_SCHED_LOCK(curthread, curthread); + saved_mask = curthread->tmbx.tm_context.uc_sigmask; + saved_seqno = curthread->sigmask_seqno; + SIGSETOR(curthread->tmbx.tm_context.uc_sigmask, + _thread_sigact[sig - 1].sa_mask); + sigaddset(&curthread->tmbx.tm_context.uc_sigmask, sig); + THR_SCHED_UNLOCK(curthread, curthread); /* - * Deliver the signal to a thread. + * Check that a custom handler is installed and if + * the signal is not blocked: */ - if ((pthread = thread_sig_find(sig)) == NULL) { - DBG_MSG("No thread to handle signal %d\n", sig); - return; + sigfunc = _thread_sigact[sig - 1].sa_sigaction; + ucp->uc_sigmask = _thr_proc_sigmask; + if (((__sighandler_t *)sigfunc != SIG_DFL) && + ((__sighandler_t *)sigfunc != SIG_IGN)) { + if (((_thread_sigact[sig - 1].sa_flags & SA_SIGINFO) != 0) || + (info == NULL)) + (*(sigfunc))(sig, info, ucp); + else + (*(sigfunc))(sig, (siginfo_t *)info->si_code, ucp); } - DBG_MSG("Got signal %d, selecting thread %p\n", sig, pthread); - thread_sig_add(pthread, sig, /*has_args*/ 1); + + /* + * Restore the thread's signal mask. + */ + if (saved_seqno == curthread->sigmask_seqno) + curthread->tmbx.tm_context.uc_sigmask = saved_mask; + else + curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; } /* - * Find a thread that can handle the signal. + * Find a thread that can handle the signal. This must be called + * with upcalls disabled. */ -static struct pthread * -thread_sig_find(int sig) +struct pthread * +thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) { - struct pthread *curthread = _get_curthread(); + int handler_installed; struct pthread *pthread, *pthread_next; struct pthread *suspended_thread, *signaled_thread; DBG_MSG("Looking for thread to handle signal %d\n", sig); + + handler_installed = (_thread_sigact[sig - 1].sa_handler != SIG_IGN) && + (_thread_sigact[sig - 1].sa_handler != SIG_DFL); + /* Check if the signal requires a dump of thread information: */ if (sig == SIGINFO) { /* Dump thread information to file: */ _thread_dump_info(); - - /* Unblock this signal to allow further dumps: */ - sigdelset(&_thread_sigmask, sig); } - /* * Enter a loop to look for threads that have the signal * unmasked. POSIX specifies that a thread in a sigwait @@ -123,13 +282,10 @@ thread_sig_find(int sig) * installed, the signal only affects threads in sigwait. */ suspended_thread = NULL; - if ((curthread != &_thread_kern_thread) && - !sigismember(&curthread->mailbox.tm_context.uc_sigmask, sig)) - signaled_thread = curthread; - else - signaled_thread = NULL; + signaled_thread = NULL; - for (pthread = TAILQ_FIRST(&_waitingq); + KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); + for (pthread = TAILQ_FIRST(&_thread_list); pthread != NULL; pthread = pthread_next) { /* * Grab the next thread before possibly destroying @@ -139,19 +295,17 @@ thread_sig_find(int sig) if ((pthread->state == PS_SIGWAIT) && sigismember(pthread->data.sigwait, sig)) { - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread, PS_RUNNING); + /* Take the scheduling lock. */ + KSE_SCHED_LOCK(curkse, pthread->kseg); /* - * A signal handler is not invoked for threads - * in sigwait. Clear the blocked and pending - * flags. + * Return the signal number and make the + * thread runnable. */ - sigdelset(&_thread_sigmask, sig); - sigdelset(&_thread_sigpending, sig); - - /* Return the signal number: */ pthread->signo = sig; + _thr_setrunnable_unlocked(pthread); + + KSE_SCHED_UNLOCK(curkse, pthread->kseg); /* * POSIX doesn't doesn't specify which thread @@ -163,11 +317,18 @@ thread_sig_find(int sig) * to other threads and do not add the signal * to the process pending set. */ + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); + DBG_MSG("Waking thread %p in sigwait with signal %d\n", + pthread, sig); return (NULL); } - if (!sigismember( - &pthread->mailbox.tm_context.uc_sigmask, sig) && - ((pthread->flags & PTHREAD_FLAGS_SUSPENDED) == 0)) { + else if ((pthread->state == PS_DEAD) || + (pthread->state == PS_DEADLOCK) || + ((pthread->flags & THR_FLAGS_EXITING) != 0)) + ; /* Skip this thread. */ + else if ((handler_installed != 0) && + !sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig) && + ((pthread->flags & THR_FLAGS_SUSPENDED) == 0)) { if (pthread->state == PS_SIGSUSPEND) { if (suspended_thread == NULL) suspended_thread = pthread; @@ -175,34 +336,33 @@ thread_sig_find(int sig) signaled_thread = pthread; } } + KSE_LOCK_RELEASE(curkse, &_thread_list_lock); /* - * If we didn't find a thread in the waiting queue, - * check the all threads queue: + * Only perform wakeups and signal delivery if there is a + * custom handler installed: */ - if (suspended_thread == NULL && - signaled_thread == NULL) { + if (handler_installed == 0) { /* - * Enter a loop to look for other threads - * capable of receiving the signal: + * There is no handler installed; nothing to do here. */ - TAILQ_FOREACH(pthread, &_thread_list, tle) { - if (!sigismember( - &pthread->mailbox.tm_context.uc_sigmask, sig)) { - signaled_thread = pthread; - break; - } - } - } - - if (suspended_thread == NULL && - signaled_thread == NULL) + } else if (suspended_thread == NULL && + signaled_thread == NULL) { /* * Add it to the set of signals pending * on the process: */ - sigaddset(&_thread_sigpending, sig); - else { + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + if (!sigismember(&_thr_proc_sigpending, sig)) { + sigaddset(&_thr_proc_sigpending, sig); + if (info == NULL) + build_siginfo(&_thr_proc_siginfo[sig], sig); + else + memcpy(&_thr_proc_siginfo[sig], info, + sizeof(*info)); + } + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + } else { /* * We only deliver the signal to one thread; * give preference to the suspended thread: @@ -213,119 +373,187 @@ thread_sig_find(int sig) pthread = signaled_thread; return (pthread); } - - /* Returns nothing. */ return (NULL); } -#if __XXX_NOT_YET__ +static void +build_siginfo(siginfo_t *info, int signo) +{ + bzero(info, sizeof(*info)); + info->si_signo = signo; + info->si_pid = _thr_pid; +} + +/* + * This is called by a thread when it has pending signals to deliver. + * It should only be called from the context of the thread. + */ void -_thread_sig_check_pending(struct pthread *pthread) +_thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp, + struct pthread_sigframe *psf) { - sigset_t sigset; - int i; + struct pthread_sigframe psf_save; + sigset_t sigset; + int i; + + THR_SCHED_LOCK(curthread, curthread); + memcpy(&sigset, &curthread->sigpend, sizeof(sigset)); + sigemptyset(&curthread->sigpend); + if (psf != NULL) { + memcpy(&psf_save, psf, sizeof(*psf)); + SIGSETOR(sigset, psf_save.psf_sigset); + sigemptyset(&psf->psf_sigset); + } + THR_SCHED_UNLOCK(curthread, curthread); + /* Check the threads previous state: */ + if ((psf != NULL) && (psf_save.psf_state != PS_RUNNING)) { + /* + * Do a little cleanup handling for those threads in + * queues before calling the signal handler. Signals + * for these threads are temporarily blocked until + * after cleanup handling. + */ + switch (psf_save.psf_state) { + case PS_COND_WAIT: + _cond_wait_backout(curthread); + psf_save.psf_state = PS_RUNNING; + break; + + case PS_MUTEX_WAIT: + _mutex_lock_backout(curthread); + psf_save.psf_state = PS_RUNNING; + break; + + default: + break; + } + } /* - * Check if there are pending signals for the running - * thread or process that aren't blocked: + * Lower the priority before calling the handler in case + * it never returns (longjmps back): */ - sigset = pthread->sigpend; - SIGSETOR(sigset, _thread_sigpending); - SIGSETNAND(sigset, pthread->sigmask); - SIGSETNAND(sigset, _thread_sigmask); - if (SIGNOTEMPTY(sigset)) { - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - if (sigismember(&pthread->sigpend, i) != 0) - thread_sig_add(pthread, i, - /*has_args*/ 0); - else { - thread_sig_add(pthread, i, - /*has_args*/ 1); - sigdelset(&_thread_sigpending, i); - } - } + curthread->active_priority &= ~THR_SIGNAL_PRIORITY; + + for (i = 1; i < NSIG; i++) { + if (sigismember(&sigset, i) != 0) { + /* Call the handler: */ + thr_sig_invoke_handler(curthread, i, + &curthread->siginfo[i], ucp); } } + + THR_SCHED_LOCK(curthread, curthread); + if (psf != NULL) + thr_sigframe_restore(curthread, &psf_save); + /* Restore the signal mask. */ + curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; + THR_SCHED_UNLOCK(curthread, curthread); + _thr_sig_check_pending(curthread); } -#endif -#if __XXX_NOT_YET__ /* - * This can only be called from the kernel scheduler. It assumes that - * all thread contexts are saved and that a signal frame can safely be - * added to any user thread. + * This checks pending signals for the current thread. It should be + * called whenever a thread changes its signal mask. Note that this + * is called from a thread (using its stack). + * + * XXX - We might want to just check to see if there are pending + * signals for the thread here, but enter the UTS scheduler + * to actually install the signal handler(s). */ void -_thread_sig_handle_pending(void) +_thr_sig_check_pending(struct pthread *curthread) { - struct pthread *pthread; - int sig; + sigset_t sigset; + sigset_t pending_process; + sigset_t pending_thread; + kse_critical_t crit; + int i; + + curthread->check_pending = 0; /* - * Check the array of pending signals: + * Check if there are pending signals for the running + * thread or process that aren't blocked: */ - for (sig = 1; sig <= NSIG; sig++) { - if (sigismember(&_thread_sigpending, sig)) { - /* This signal is no longer pending. */ - sigdelset(&_thread_sigpending, sig); - /* Some signals need special handling. */ - thread_sig_handle_special(sig); - /* Deliver the signal. */ - if (sigismember(&_thread_sigmask, sig)) { - sigaddset(&_thread_sigmask, sig); - if ((pthread = thread_sig_find(sig)) != NULL) { - /* - * Setup the target thread to receive - * the signal: - */ - thread_sig_add(pthread, sig, - /*has_args*/ 1); + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); + sigset = _thr_proc_sigpending; + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + _kse_critical_leave(crit); + + THR_SCHED_LOCK(curthread, curthread); + SIGSETOR(sigset, curthread->sigpend); + SIGSETNAND(sigset, curthread->tmbx.tm_context.uc_sigmask); + if (SIGNOTEMPTY(sigset)) { + ucontext_t uc; + volatile int once; + + curthread->check_pending = 0; + THR_SCHED_UNLOCK(curthread, curthread); + + /* + * Split the pending signals into those that were + * pending on the process and those that were pending + * on the thread. + */ + sigfillset(&pending_process); + sigfillset(&pending_thread); + for (i = 1; i < NSIG; i++) { + if (sigismember(&sigset, i) != 0) { + if (sigismember(&curthread->sigpend, i) != 0) { + build_siginfo(&curthread->siginfo[i], i); + sigdelset(&pending_thread, i); + } else { + memcpy(&curthread->siginfo[i], + &_thr_proc_siginfo[i], + sizeof(siginfo_t)); + sigdelset(&pending_process, i); + } + } + } + /* + * Remove any process pending signals that were scheduled + * to be delivered from process' pending set. + */ + crit = _kse_critical_enter(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); + SIGSETAND(_thr_proc_sigpending, pending_process); + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + _kse_critical_leave(crit); + + /* + * Remove any thread pending signals that were scheduled + * to be delivered from thread's pending set. + */ + THR_SCHED_LOCK(curthread, curthread); + SIGSETAND(curthread->sigpend, pending_thread); + THR_SCHED_UNLOCK(curthread, curthread); + + once = 0; + THR_GETCONTEXT(&uc); + if (once == 0) { + once = 1; + for (i = 1; i < NSIG; i++) { + if (sigismember(&sigset, i) != 0) { + /* Call the handler: */ + thr_sig_invoke_handler(curthread, i, + &curthread->siginfo[i], &uc); } } } } + else + THR_SCHED_UNLOCK(curthread, curthread); } -#endif /* - * Do special processing to the thread states before we deliver - * a signal to the application. + * This must be called with upcalls disabled. */ static void -thread_sig_handle_special(int sig) +handle_special_signals(struct kse *curkse, int sig) { - struct pthread *pthread, *pthread_next; - int i; - switch (sig) { - case SIGCHLD: - /* - * Enter a loop to wake up all threads waiting - * for a process to complete: - */ - 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 this thread is waiting for a child - * process to complete, wake it up: - */ - if (pthread->state == PS_WAIT_WAIT) { - /* Make the thread runnable: */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); - - /* Return the signal number: */ - pthread->signo = sig; - } - } - break; - /* * POSIX says that pending SIGCONT signals are * discarded when one of these signals occurs. @@ -333,13 +561,9 @@ thread_sig_handle_special(int sig) case SIGTSTP: case SIGTTIN: case SIGTTOU: - /* - * Enter a loop to discard pending SIGCONT - * signals: - */ - TAILQ_FOREACH(pthread, &_thread_list, tle) { - sigdelset(&pthread->sigpend, SIGCONT); - } + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + sigdelset(&_thr_proc_sigpending, SIGCONT); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); break; default: @@ -352,12 +576,17 @@ thread_sig_handle_special(int sig) * This function is only called if there is a handler installed * for the signal, and if the target thread has the signal * unmasked. + * + * This must be called with the thread's scheduling lock held. */ static void -thread_sig_add(struct pthread *pthread, int sig, int has_args) +thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info) { + int restart; int suppress_handler = 0; + restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + /* Make sure this signal isn't still in the pending set: */ sigdelset(&pthread->sigpend, sig); @@ -370,6 +599,8 @@ thread_sig_add(struct pthread *pthread, int sig, int has_args) */ case PS_DEAD: case PS_DEADLOCK: + case PS_LOCKWAIT: + case PS_SUSPENDED: case PS_STATE_MAX: /* * You can't call a signal handler for threads in these @@ -387,56 +618,21 @@ thread_sig_add(struct pthread *pthread, int sig, int has_args) * Remove the thread from the queue before changing its * priority: */ - if ((pthread->flags & PTHREAD_FLAGS_IN_PRIOQ) != 0) - PTHREAD_PRIOQ_REMOVE(pthread); - break; - - case PS_SUSPENDED: - break; - - case PS_SPINBLOCK: - /* Remove the thread from the workq and waitq: */ - PTHREAD_WORKQ_REMOVE(pthread); - PTHREAD_WAITQ_REMOVE(pthread); - /* Make the thread runnable: */ - PTHREAD_SET_STATE(pthread, PS_RUNNING); - break; - - case PS_SIGWAIT: - /* The signal handler is not called for threads in SIGWAIT. */ - suppress_handler = 1; - /* Wake up the thread if the signal is blocked. */ - if (sigismember(pthread->data.sigwait, sig)) { - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread, PS_RUNNING); - - /* Return the signal number: */ - pthread->signo = sig; - } else - /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend, sig); - break; - - /* - * The wait state is a special case due to the handling of - * SIGCHLD signals. - */ - case PS_WAIT_WAIT: - if (sig == SIGCHLD) { - /* Change the state of the thread to run: */ - PTHREAD_WAITQ_REMOVE(pthread); - PTHREAD_SET_STATE(pthread, PS_RUNNING); - - /* Return the signal number: */ - pthread->signo = sig; - } + if ((pthread->flags & THR_FLAGS_IN_RUNQ) != 0) + THR_RUNQ_REMOVE(pthread); else { /* - * Mark the thread as interrupted only if the - * restart flag is not set on the signal action: + * This thread is active; add the signal to the + * pending set and mark it as having pending + * signals. */ - PTHREAD_WAITQ_REMOVE(pthread); - PTHREAD_SET_STATE(pthread, PS_RUNNING); + suppress_handler = 1; + sigaddset(&pthread->sigpend, sig); + build_siginfo(&pthread->siginfo[sig], sig); + pthread->check_pending = 1; + if ((pthread->blocked != 0) && + !THR_IN_CRITICAL(pthread)) + kse_thr_interrupt(&pthread->tmbx /* XXX - restart?!?! */); } break; @@ -451,18 +647,7 @@ thread_sig_add(struct pthread *pthread, int sig, int has_args) * be added back to the wait queue once all signal * handlers have been invoked. */ - PTHREAD_WAITQ_REMOVE(pthread); - break; - - case PS_JOIN: - /* - * Remove the thread from the wait queue. It will - * be added back to the wait queue once all signal - * handlers have been invoked. - */ - PTHREAD_WAITQ_REMOVE(pthread); - /* Make the thread runnable: */ - PTHREAD_SET_STATE(pthread, PS_RUNNING); + KSE_WAITQ_REMOVE(pthread->kse, pthread); break; case PS_SLEEP_WAIT: @@ -471,55 +656,76 @@ thread_sig_add(struct pthread *pthread, int sig, int has_args) * regardless of SA_RESTART: */ pthread->interrupted = 1; - /* Remove threads in poll and select from the workq: */ - if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ) != 0) - PTHREAD_WORKQ_REMOVE(pthread); - PTHREAD_WAITQ_REMOVE(pthread); - PTHREAD_SET_STATE(pthread, PS_RUNNING); + KSE_WAITQ_REMOVE(pthread->kse, pthread); + THR_SET_STATE(pthread, PS_RUNNING); break; + case PS_JOIN: case PS_SIGSUSPEND: - PTHREAD_WAITQ_REMOVE(pthread); - PTHREAD_SET_STATE(pthread, PS_RUNNING); + KSE_WAITQ_REMOVE(pthread->kse, pthread); + THR_SET_STATE(pthread, PS_RUNNING); break; - } - DBG_MSG(">>> suppress_handler = %d\n", suppress_handler); + case PS_SIGWAIT: + /* The signal handler is not called for threads in SIGWAIT. */ + suppress_handler = 1; + /* Wake up the thread if the signal is blocked. */ + if (sigismember(pthread->data.sigwait, sig)) { + /* Return the signal number: */ + pthread->signo = sig; + + /* Make the thread runnable: */ + _thr_setrunnable_unlocked(pthread); + } else + /* Increment the pending signal count. */ + sigaddset(&pthread->sigpend, sig); + break; + } if (suppress_handler == 0) { - /* Setup a signal frame and save the current threads state: */ - thread_sigframe_add(pthread, sig, has_args); - - /* - * Signals are deferred until just before the threads - * signal handler is invoked: - */ - pthread->sig_defer_count = 1; + if (pthread->curframe == NULL) { + /* + * This thread is active. Just add it to the + * thread's pending set. + */ + sigaddset(&pthread->sigpend, sig); + pthread->check_pending = 1; + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else + memcpy(&pthread->siginfo[sig], info, + sizeof(*info)); + } else { + /* + * Setup a signal frame and save the current threads + * state: + */ + thr_sigframe_add(pthread, sig, info); + } - /* Make sure the thread is runnable: */ if (pthread->state != PS_RUNNING) - PTHREAD_SET_STATE(pthread, PS_RUNNING); + THR_SET_STATE(pthread, PS_RUNNING); + /* * The thread should be removed from all scheduling - * queues at this point. Raise the priority and place - * the thread in the run queue. It is also possible - * for a signal to be sent to a suspended thread, - * mostly via pthread_kill(). If a thread is suspended, - * don't insert it into the priority queue; just set - * its state to suspended and it will run the signal - * handler when it is resumed. + * queues at this point. Raise the priority and + * place the thread in the run queue. It is also + * possible for a signal to be sent to a suspended + * thread, mostly via pthread_kill(). If a thread + * is suspended, don't insert it into the priority + * queue; just set its state to suspended and it + * will run the signal handler when it is resumed. */ - pthread->active_priority |= PTHREAD_SIGNAL_PRIORITY; - if ((pthread->flags & PTHREAD_FLAGS_SUSPENDED) != 0) - PTHREAD_SET_STATE(pthread, PS_SUSPENDED); - else - PTHREAD_PRIOQ_INSERT_TAIL(pthread); + pthread->active_priority |= THR_SIGNAL_PRIORITY; + if ((pthread->flags & THR_FLAGS_SUSPENDED) != 0) + THR_SET_STATE(pthread, PS_SUSPENDED); + else if ((pthread->flags & THR_FLAGS_IN_RUNQ) == 0) + THR_RUNQ_INSERT_TAIL(pthread); } } -#if __XXX_NOT_YET__ static void -thread_sig_check_state(struct pthread *pthread, int sig) +thr_sig_check_state(struct pthread *pthread, int sig) { /* * Process according to thread state: @@ -528,91 +734,54 @@ thread_sig_check_state(struct pthread *pthread, int sig) /* * States which do not change when a signal is trapped: */ - case PS_DEAD: - case PS_DEADLOCK: - case PS_STATE_MAX: case PS_RUNNING: - case PS_SUSPENDED: - case PS_SPINBLOCK: + case PS_LOCKWAIT: + case PS_MUTEX_WAIT: case PS_COND_WAIT: case PS_JOIN: - case PS_MUTEX_WAIT: + case PS_SUSPENDED: + case PS_DEAD: + case PS_DEADLOCK: + case PS_STATE_MAX: break; case PS_SIGWAIT: /* Wake up the thread if the signal is blocked. */ if (sigismember(pthread->data.sigwait, sig)) { - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread, PS_RUNNING); - /* Return the signal number: */ pthread->signo = sig; + + /* Change the state of the thread to run: */ + _thr_setrunnable_unlocked(pthread); } else /* Increment the pending signal count. */ sigaddset(&pthread->sigpend, sig); break; - /* - * The wait state is a special case due to the handling of - * SIGCHLD signals. - */ - case PS_WAIT_WAIT: - if (sig == SIGCHLD) { - /* - * Remove the thread from the wait queue and - * make it runnable: - */ - PTHREAD_NEW_STATE(pthread, PS_RUNNING); - - /* Return the signal number: */ - pthread->signo = sig; - } - break; - - case PS_FDLR_WAIT: - case PS_FDLW_WAIT: case PS_SIGSUSPEND: case PS_SLEEP_WAIT: /* * Remove the thread from the wait queue and make it * runnable: */ - PTHREAD_NEW_STATE(pthread, PS_RUNNING); - - /* Flag the operation as interrupted: */ - pthread->interrupted = 1; - break; - - /* - * These states are additionally in the work queue: - */ - case PS_FDR_WAIT: - case PS_FDW_WAIT: - case PS_FILE_WAIT: - case PS_POLL_WAIT: - case PS_SELECT_WAIT: - /* - * Remove the thread from the wait and work queues, and - * make it runnable: - */ - PTHREAD_WORKQ_REMOVE(pthread); - PTHREAD_NEW_STATE(pthread, PS_RUNNING); + _thr_setrunnable_unlocked(pthread); /* Flag the operation as interrupted: */ pthread->interrupted = 1; break; } } -#endif -#if __XXX_NOT_YET__ /* * Send a signal to a specific thread (ala pthread_kill): */ void -_thread_sig_send(struct pthread *pthread, int sig) +_thr_sig_send(struct pthread *pthread, int sig) { - struct pthread *curthread = _get_curthread(); + struct pthread *curthread = _get_curthread(); + + /* Lock the scheduling queue of the target thread. */ + THR_SCHED_LOCK(curthread, pthread); /* Check for signals whose actions are SIG_DFL: */ if (_thread_sigact[sig - 1].sa_handler == SIG_DFL) { @@ -620,17 +789,21 @@ _thread_sig_send(struct pthread *pthread, int sig) * Check to see if a temporary signal handler is * installed for sigwaiters: */ - if (_thread_dfl_count[sig] == 0) + if (_thread_dfl_count[sig] == 0) { /* * Deliver the signal to the process if a handler * is not installed: */ + THR_SCHED_UNLOCK(curthread, pthread); kill(getpid(), sig); + THR_SCHED_LOCK(curthread, pthread); + } /* * Assuming we're still running after the above kill(), * make any necessary state changes to the thread: */ - thread_sig_check_state(pthread, sig); + thr_sig_check_state(pthread, sig); + THR_SCHED_UNLOCK(curthread, pthread); } /* * Check that the signal is not being ignored: @@ -638,145 +811,98 @@ _thread_sig_send(struct pthread *pthread, int sig) else if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) { if (pthread->state == PS_SIGWAIT && sigismember(pthread->data.sigwait, sig)) { - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread, PS_RUNNING); - /* Return the signal number: */ pthread->signo = sig; - } else if (sigismember(&pthread->sigmask, sig)) + + /* Change the state of the thread to run: */ + _thr_setrunnable_unlocked(pthread); + THR_SCHED_UNLOCK(curthread, pthread); + } else if (sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) { /* Add the signal to the pending set: */ sigaddset(&pthread->sigpend, sig); - else if (pthread == curthread) - /* Call the signal handler for the current thread: */ - thread_sig_invoke_handler(sig, NULL, NULL); - else { - /* Protect the scheduling queues: */ - _thread_kern_sig_defer(); + THR_SCHED_UNLOCK(curthread, pthread); + } else if (pthread == curthread) { + ucontext_t uc; + siginfo_t info; + volatile int once; + + THR_SCHED_UNLOCK(curthread, pthread); + build_siginfo(&info, sig); + once = 0; + THR_GETCONTEXT(&uc); + if (once == 0) { + once = 1; + /* + * Call the signal handler for the current + * thread: + */ + thr_sig_invoke_handler(curthread, sig, + &info, &uc); + } + } else { /* * Perform any state changes due to signal * arrival: */ - thread_sig_add(pthread, sig, /* has args */ 0); - /* Unprotect the scheduling queues: */ - _thread_kern_sig_undefer(); + thr_sig_add(pthread, sig, NULL); + THR_SCHED_UNLOCK(curthread, pthread); } } } -#endif -/* - * User thread signal handler wrapper. - * - * thread - current running thread - */ -void -_thread_sig_wrapper(int sig, siginfo_t *info, ucontext_t *context) +static void +thr_sigframe_add(struct pthread *thread, int sig, siginfo_t *info) { - struct pthread_state_data psd; - struct pthread *thread = _get_curthread(); - __siginfohandler_t *handler; - - /* Save the thread's previous state. */ - thread_sigframe_save(thread, &psd); + if (thread->curframe == NULL) + PANIC("Thread doesn't have signal frame "); - /* Check the threads previous state: */ - if (psd.psd_state != PS_RUNNING) { + if (thread->check_pending == 0) { /* - * Do a little cleanup handling for those threads in - * queues before calling the signal handler. Signals - * for these threads are temporarily blocked until - * after cleanup handling. + * Multiple signals can be added to the same signal + * frame. Only save the thread's state the first time. */ - switch (psd.psd_state) { - case PS_COND_WAIT: - _cond_wait_backout(thread); - psd.psd_state = PS_RUNNING; - break; - - case PS_MUTEX_WAIT: - _mutex_lock_backout(thread); - psd.psd_state = PS_RUNNING; - break; - - default: - break; - } + thr_sigframe_save(thread, thread->curframe); + thread->check_pending = 1; + thread->flags &= THR_FLAGS_PRIVATE; } - - /* Unblock the signal in case we don't return from the handler. */ - /* - * XXX - This is totally bogus. We need to lock the signal mask - * somehow. - */ - sigdelset(&_thread_sigmask, sig); - - /* - * Lower the priority before calling the handler in case - * it never returns (longjmps back): - */ - thread->active_priority &= ~PTHREAD_SIGNAL_PRIORITY; - - /* - * Reenable interruptions without checking for the need to - * context switch. - */ - thread->sig_defer_count = 0; - - if (_thread_sigact[sig -1].sa_handler != NULL) { - handler = (__siginfohandler_t *) - _thread_sigact[sig - 1].sa_handler; - handler(sig, info, context); - } - - /* Restore the signal frame. */ - thread_sigframe_restore(thread, &psd); - - /* The signal mask was restored; check for any pending signals. */ - /* XXX - thread->check_pending = 1; */ -} - -static void -thread_sigframe_add(struct pthread *thread, int sig, int has_args) -{ - struct pthread_signal_frame *psf = NULL; - unsigned long stackp; - - /* Add a signal frame to the stack, pointing to our signal wrapper. */ - signalcontext(&thread->mailbox.tm_context, sig, - (__sighandler_t *)_thread_sig_wrapper); + sigaddset(&thread->curframe->psf_sigset, sig); + if (info != NULL) + memcpy(&thread->siginfo[sig], info, sizeof(*info)); + else + build_siginfo(&thread->siginfo[sig], sig); /* Setup the new signal mask. */ - SIGSETOR(thread->mailbox.tm_context.uc_sigmask, + SIGSETOR(thread->tmbx.tm_context.uc_sigmask, _thread_sigact[sig - 1].sa_mask); - sigaddset(&thread->mailbox.tm_context.uc_sigmask, sig); + sigaddset(&thread->tmbx.tm_context.uc_sigmask, sig); } -static void -thread_sigframe_restore(struct pthread *thread, struct pthread_state_data *psd) +void +thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf) { - thread->wakeup_time = psd->psd_wakeup_time; - thread->data = psd->psd_wait_data; - thread->state = psd->psd_state; - thread->flags = psd->psd_flags; - thread->interrupted = psd->psd_interrupted; - thread->sig_defer_count = psd->psd_sig_defer_count; + thread->flags = psf->psf_flags; + thread->interrupted = psf->psf_interrupted; + thread->signo = psf->psf_signo; + thread->state = psf->psf_state; + thread->data = psf->psf_wait_data; + thread->wakeup_time = psf->psf_wakeup_time; + if (thread->sigmask_seqno == psf->psf_seqno) + thread->tmbx.tm_context.uc_sigmask = psf->psf_sigmask; + else + thread->tmbx.tm_context.uc_sigmask = thread->sigmask; } static void -thread_sigframe_save(struct pthread *thread, struct pthread_state_data *psd) +thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf) { - psd->psd_wakeup_time = thread->wakeup_time; - psd->psd_wait_data = thread->data; - psd->psd_state = thread->state; - psd->psd_flags = thread->flags & - (PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE); - psd->psd_interrupted = thread->interrupted; - psd->psd_sig_defer_count = thread->sig_defer_count; -} - -void -_thread_sig_handler(int sig, siginfo_t *info, ucontext_t *context) -{ - - /* Nothing. */ + /* This has to initialize all members of the sigframe. */ + psf->psf_flags = thread->flags & THR_FLAGS_PRIVATE; + psf->psf_interrupted = thread->interrupted; + psf->psf_signo = thread->signo; + psf->psf_state = thread->state; + psf->psf_wait_data = thread->data; + psf->psf_wakeup_time = thread->wakeup_time; + psf->psf_sigmask = thread->tmbx.tm_context.uc_sigmask; + psf->psf_seqno = thread->sigmask_seqno; + sigemptyset(&psf->psf_sigset); } |