diff options
author | deischen <deischen@FreeBSD.org> | 1999-12-17 00:56:36 +0000 |
---|---|---|
committer | deischen <deischen@FreeBSD.org> | 1999-12-17 00:56:36 +0000 |
commit | 17ee572a14054fb3c892f9dc5ee491e75bc8f3f5 (patch) | |
tree | 5d51d9a812640e396f61d1c8d3b91b7f6c0cdba7 /lib/libkse | |
parent | 3bd28ea93ef0db4dbbbf6b6671392ccbc020a864 (diff) | |
download | FreeBSD-src-17ee572a14054fb3c892f9dc5ee491e75bc8f3f5.zip FreeBSD-src-17ee572a14054fb3c892f9dc5ee491e75bc8f3f5.tar.gz |
Fixes for signal handling:
o Don't call signal handlers with the signal handler access lock
held.
o Remove pending signals before calling signal handlers. If
pending signals were not removed prior to handling them,
invocation of the handler could cause the handler to be
called more than once for the same signal. Found by: JB
o When SIGCHLD arrives, wake up all threads in PS_WAIT_WAIT
(wait4).
PR: bin/15328
Reviewed by: jasone
Diffstat (limited to 'lib/libkse')
-rw-r--r-- | lib/libkse/thread/thr_kern.c | 7 | ||||
-rw-r--r-- | lib/libkse/thread/thr_kill.c | 94 | ||||
-rw-r--r-- | lib/libkse/thread/thr_private.h | 4 | ||||
-rw-r--r-- | lib/libkse/thread/thr_sig.c | 248 | ||||
-rw-r--r-- | lib/libkse/thread/thr_wait4.c | 2 |
5 files changed, 208 insertions, 147 deletions
diff --git a/lib/libkse/thread/thr_kern.c b/lib/libkse/thread/thr_kern.c index cdb84a5..f69cb41 100644 --- a/lib/libkse/thread/thr_kern.c +++ b/lib/libkse/thread/thr_kern.c @@ -1076,6 +1076,7 @@ dequeue_signals(void) { char bufr[128]; int i, num; + pthread_t pthread; /* * Enter a loop to read and handle queued signals from the @@ -1096,7 +1097,11 @@ dequeue_signals(void) } else { /* Handle this signal: */ - _thread_sig_handle((int) bufr[i], NULL); + pthread = _thread_sig_handle((int) bufr[i], + NULL); + if (pthread != NULL) + _thread_sig_deliver(pthread, + (int) bufr[i]); } } } diff --git a/lib/libkse/thread/thr_kill.c b/lib/libkse/thread/thr_kill.c index 6b25550..4bf1761 100644 --- a/lib/libkse/thread/thr_kill.c +++ b/lib/libkse/thread/thr_kill.c @@ -46,7 +46,6 @@ pthread_kill(pthread_t pthread, int sig) if (sig < 0 || sig >= NSIG) /* Invalid signal: */ ret = EINVAL; - /* * Ensure the thread is in the list of active threads, and the * signal is valid (signal 0 specifies error checking only) and @@ -60,98 +59,7 @@ pthread_kill(pthread_t pthread, int sig) */ _thread_kern_sig_defer(); - switch (pthread->state) { - case PS_SIGSUSPEND: - /* - * Only wake up the thread if the signal is unblocked - * and there is a handler installed for the signal. - */ - if (!sigismember(&pthread->sigmask, sig) && - _thread_sigact[sig - 1].sa_handler != SIG_DFL) { - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); - - /* Return the signal number: */ - pthread->signo = sig; - } - /* Increment the pending signal count: */ - sigaddset(&pthread->sigpend,sig); - 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; - } else - /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend,sig); - break; - - case PS_FDR_WAIT: - case PS_FDW_WAIT: - case PS_POLL_WAIT: - case PS_SLEEP_WAIT: - case PS_SELECT_WAIT: - if (!sigismember(&pthread->sigmask, sig) && - (_thread_sigact[sig - 1].sa_handler != SIG_IGN)) { - /* 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_NEW_STATE(pthread,PS_RUNNING); - - /* Return the signal number: */ - pthread->signo = sig; - } else { - /* Increment the pending signal count: */ - sigaddset(&pthread->sigpend,sig); - } - break; - - default: - /* Increment the pending signal count: */ - sigaddset(&pthread->sigpend,sig); - break; - } - - - /* - * Check that a custom handler is installed - * and if the signal is not blocked: - */ - if (_thread_sigact[sig - 1].sa_handler != SIG_DFL && - _thread_sigact[sig - 1].sa_handler != SIG_IGN && - sigismember(&pthread->sigpend, sig) && - !sigismember(&pthread->sigmask, sig)) { - 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; - - /* Clear the pending signal: */ - sigdelset(&pthread->sigpend, sig); - - /* - * Dispatch the signal via the custom signal - * handler: - */ - (*(_thread_sigact[sig - 1].sa_handler))(sig); - - _thread_run = pthread_saved; - - if (_thread_run->sig_defer_count > 0) - pthread->sig_defer_count--; - } + _thread_sig_send(pthread, sig); /* * Undefer and handle pending signals, yielding if diff --git a/lib/libkse/thread/thr_private.h b/lib/libkse/thread/thr_private.h index 70c7487..4326bf6 100644 --- a/lib/libkse/thread/thr_private.h +++ b/lib/libkse/thread/thr_private.h @@ -984,8 +984,10 @@ void _thread_kern_set_timeout(struct timespec *); void _thread_kern_sig_defer(void); void _thread_kern_sig_undefer(void); void _thread_sig_handler(int, int, ucontext_t *); -void _thread_sig_handle(int, ucontext_t *); +pthread_t _thread_sig_handle(int, ucontext_t *); void _thread_sig_init(void); +void _thread_sig_send(pthread_t pthread, int sig); +void _thread_sig_deliver(pthread_t pthread, int sig); void _thread_start(void); void _thread_start_sig_handler(void); void _thread_seterrno(pthread_t,int); diff --git a/lib/libkse/thread/thr_sig.c b/lib/libkse/thread/thr_sig.c index 3830a1d..b744659 100644 --- a/lib/libkse/thread/thr_sig.c +++ b/lib/libkse/thread/thr_sig.c @@ -43,13 +43,13 @@ #include "pthread_private.h" /* Prototypes: */ -static void _thread_signal(pthread_t pthread, int sig); +static void _thread_sig_check_state(pthread_t pthread, int sig); /* Static variables: */ static spinlock_t signal_lock = _SPINLOCK_INITIALIZER; -unsigned int pending_sigs[NSIG]; -unsigned int handled_sigs[NSIG]; -int volatile check_pending = 0; +static unsigned int pending_sigs[NSIG]; +static unsigned int handled_sigs[NSIG]; +static int volatile check_pending = 0; /* Initialize signal handling facility: */ void @@ -73,8 +73,9 @@ _thread_sig_init(void) void _thread_sig_handler(int sig, int code, ucontext_t * scp) { - char c; + pthread_t pthread; int i; + char c; /* Check if an interval timer signal: */ if (sig == _SCHED_SIGNAL) { @@ -134,13 +135,20 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) } else { /* It's safe to handle the signal now. */ - _thread_sig_handle(sig, scp); + pthread = _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; + if (pthread == NULL) + signal_lock.access_lock = 0; + else { + sigaddset(&pthread->sigmask, sig); + signal_lock.access_lock = 0; + _thread_sig_deliver(pthread, sig); + sigdelset(&pthread->sigmask, sig); + } } /* Enter a loop to process pending signals: */ @@ -148,15 +156,27 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) (_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); + if (pending_sigs[i - 1] > handled_sigs[i - 1]) { + pending_sigs[i - 1] = handled_sigs[i - 1]; + pthread = _thread_sig_handle(i, scp); + if (pthread != NULL) { + sigaddset(&pthread->sigmask, i); + signal_lock.access_lock = 0; + _thread_sig_deliver(pthread, i); + sigdelset(&pthread->sigmask, i); + if (_atomic_lock(&signal_lock.access_lock)) { + check_pending = 1; + return; + } + } + } } signal_lock.access_lock = 0; } } } -void +pthread_t _thread_sig_handle(int sig, ucontext_t * scp) { int i; @@ -193,6 +213,30 @@ _thread_sig_handle(int sig, ucontext_t * scp) O_NONBLOCK); } } + /* + * 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; + } + } } /* @@ -244,7 +288,7 @@ _thread_sig_handle(int sig, ucontext_t * scp) * Do not attempt to deliver this signal * to other threads. */ - return; + return (NULL); } else if (!sigismember(&pthread->sigmask, sig)) { if (pthread->state == PS_SIGSUSPEND) { @@ -255,6 +299,23 @@ _thread_sig_handle(int sig, ucontext_t * scp) } } + /* + * If we didn't find a thread in the waiting queue, + * check the all threads queue: + */ + if (suspended_thread == NULL && signaled_thread == NULL) { + /* + * Enter a loop to look for other threads capable + * of receiving the signal: + */ + TAILQ_FOREACH(pthread, &_thread_list, tle) { + if (!sigismember(&pthread->sigmask, sig)) { + signaled_thread = pthread; + break; + } + } + } + /* Check if the signal is not being ignored: */ if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) { if (suspended_thread == NULL && @@ -265,8 +326,6 @@ _thread_sig_handle(int sig, ucontext_t * scp) */ sigaddset(&_process_sigpending, sig); else { - pthread_t pthread_saved = _thread_run; - /* * We only deliver the signal to one thread; * give preference to the suspended thread: @@ -276,42 +335,25 @@ _thread_sig_handle(int sig, ucontext_t * scp) else pthread = signaled_thread; - /* Current thread inside critical region? */ - if (_thread_run->sig_defer_count > 0) - pthread->sig_defer_count++; - - _thread_run = pthread; - _thread_signal(pthread,sig); - /* - * Dispatch pending signals to the - * running thread: + * Perform any state changes due to signal + * arrival: */ - _dispatch_signals(); - _thread_run = pthread_saved; - - /* Current thread inside critical region? */ - if (_thread_run->sig_defer_count > 0) - pthread->sig_defer_count--; + _thread_sig_check_state(pthread, sig); + return (pthread); } - } } /* Returns nothing. */ - return; + return (NULL); } /* Perform thread specific actions in response to a signal: */ static void -_thread_signal(pthread_t pthread, int sig) +_thread_sig_check_state(pthread_t pthread, int sig) { /* - * Flag the signal as pending. It will be dispatched later. - */ - sigaddset(&pthread->sigpend,sig); - - /* * Process according to thread state: */ switch (pthread->state) { @@ -329,12 +371,26 @@ _thread_signal(pthread_t pthread, int sig) case PS_RUNNING: case PS_STATE_MAX: case PS_SIGTHREAD: - case PS_SIGWAIT: case PS_SPINBLOCK: case PS_SUSPENDED: - /* Nothing to do here. */ + /* Increment the pending signal count. */ + sigaddset(&pthread->sigpend,sig); 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; + } 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. @@ -364,9 +420,7 @@ _thread_signal(pthread_t pthread, int sig) case PS_POLL_WAIT: case PS_SLEEP_WAIT: case PS_SELECT_WAIT: - if ((_thread_sigact[sig - 1].sa_flags & SA_RESTART) == 0 && - (sig != SIGCHLD || - _thread_sigact[sig - 1].sa_handler != SIG_DFL)) { + if ((_thread_sigact[sig - 1].sa_flags & SA_RESTART) == 0) { /* Flag the operation as interrupted: */ pthread->interrupted = 1; @@ -397,11 +451,41 @@ _thread_signal(pthread_t pthread, int sig) } } +/* Send a signal to a specific thread (ala pthread_kill): */ +void +_thread_sig_send(pthread_t pthread, int sig) +{ + /* + * Check that the signal is not being ignored: + */ + 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 (pthread->state != PS_SIGWAIT && + !sigismember(&pthread->sigmask, sig)) { + /* Perform any state changes due to signal arrival: */ + _thread_sig_check_state(pthread, sig); + + /* Call the installed signal handler: */ + _thread_sig_deliver(pthread, sig); + } + else { + /* Increment the pending signal count. */ + sigaddset(&pthread->sigpend,sig); + } + } +} + /* Dispatch pending signals to the running thread: */ void _dispatch_signals() { - sigset_t sigset; + sigset_t sigset, mask; int i; /* @@ -411,9 +495,16 @@ _dispatch_signals() sigset = _thread_run->sigpend; SIGSETOR(sigset, _process_sigpending); SIGSETNAND(sigset, _thread_run->sigmask); - if (SIGNOTEMPTY(sigset)) - /* Look for all possible pending signals: */ - for (i = 1; i < NSIG; i++) + if (SIGNOTEMPTY(sigset)) { + /* + * Enter a loop to calculate deliverable pending signals + * before actually delivering them. The pending signals + * must be removed from the pending signal sets before + * calling the signal handler because the handler may + * call library routines that again check for and deliver + * pending signals. + */ + for (i = 1; i < NSIG; i++) { /* * Check that a custom handler is installed * and if the signal is not blocked: @@ -427,12 +518,67 @@ _dispatch_signals() else /* Clear the process pending signal: */ sigdelset(&_process_sigpending,i); - - /* - * Dispatch the signal via the custom signal - * handler: - */ - (*(_thread_sigact[i - 1].sa_handler))(i); } + else + /* Remove the signal if it can't be handled: */ + sigdelset(&sigset, i); + } + + /* Now deliver the signals: */ + for (i = 1; i < NSIG; i++) { + if (sigismember(&sigset, i)) + /* Deliver the signal to the running thread: */ + _thread_sig_deliver(_thread_run, i); + } + } +} + +/* Deliver a signal to a thread: */ +void +_thread_sig_deliver(pthread_t pthread, int sig) +{ + sigset_t mask; + pthread_t pthread_saved; + + /* + * Check that a custom handler is installed + * and if the signal is not blocked: + */ + if (_thread_sigact[sig - 1].sa_handler != SIG_DFL && + _thread_sigact[sig - 1].sa_handler != SIG_IGN) { + /* Save the current thread: */ + pthread_saved = _thread_run; + + /* Save the threads signal mask: */ + mask = pthread->sigmask; + + /* + * Add the current signal and signal handler + * mask to the threads current signal mask: + */ + SIGSETOR(pthread->sigmask, _thread_sigact[sig - 1].sa_mask); + sigaddset(&pthread->sigmask, sig); + + /* Current thread inside critical region? */ + if (_thread_run->sig_defer_count > 0) + pthread->sig_defer_count++; + + _thread_run = pthread; + + /* + * Dispatch the signal via the custom signal + * handler: + */ + (*(_thread_sigact[sig - 1].sa_handler))(sig); + + _thread_run = pthread_saved; + + /* Current thread inside critical region? */ + if (_thread_run->sig_defer_count > 0) + pthread->sig_defer_count--; + + /* Restore the threads signal mask: */ + pthread->sigmask = mask; + } } #endif diff --git a/lib/libkse/thread/thr_wait4.c b/lib/libkse/thread/thr_wait4.c index baa697c..4a58a70 100644 --- a/lib/libkse/thread/thr_wait4.c +++ b/lib/libkse/thread/thr_wait4.c @@ -40,7 +40,7 @@ pid_t wait4(pid_t pid, int *istat, int options, struct rusage * rusage) { - pid_t ret; + pid_t ret; _thread_enter_cancellation_point(); _thread_kern_sig_defer(); |