diff options
author | tegge <tegge@FreeBSD.org> | 2006-10-13 20:12:07 +0000 |
---|---|---|
committer | tegge <tegge@FreeBSD.org> | 2006-10-13 20:12:07 +0000 |
commit | d21c14576eaf56bfff093b5430e437b27cd11675 (patch) | |
tree | 9abe4b184c29a1facac1785aaa8ccbbdd520ed43 | |
parent | 8f000653f2466e5a55a55aa95a212c4478f15b89 (diff) | |
download | FreeBSD-src-d21c14576eaf56bfff093b5430e437b27cd11675.zip FreeBSD-src-d21c14576eaf56bfff093b5430e437b27cd11675.tar.gz |
Delay unblocking signal and restoring process signal mask until the
thread signal mask has been updated to avoid stack overflow during signal
bursts.
Don't block signal forever if no threads can currently handle signal.
Check for pending signal after direct invocation of signal handler.
-rw-r--r-- | lib/libc_r/uthread/uthread_sig.c | 60 |
1 files changed, 41 insertions, 19 deletions
diff --git a/lib/libc_r/uthread/uthread_sig.c b/lib/libc_r/uthread/uthread_sig.c index 3617b36..b61e21c 100644 --- a/lib/libc_r/uthread/uthread_sig.c +++ b/lib/libc_r/uthread/uthread_sig.c @@ -53,7 +53,7 @@ static void thread_sigframe_add(struct pthread *thread, int sig, static void thread_sigframe_save(struct pthread *thread, struct pthread_signal_frame *psf); static void thread_sig_invoke_handler(int sig, siginfo_t *info, - ucontext_t *ucp); + ucontext_t *ucp, int unblock); /*#define DEBUG_SIGNAL*/ #ifdef DEBUG_SIGNAL @@ -77,6 +77,7 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) struct pthread *pthread, *pthread_h; int in_sched = _thread_kern_in_sched; char c; + sigset_t sigset; if (ucp == NULL) PANIC("Thread signal handler received null context"); @@ -126,7 +127,14 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) * be ready to read when this signal handler returns. */ if (_queue_signals != 0) { - __sys_write(_thread_kern_pipe[1], &c, 1); + ssize_t wgot; + do { + wgot = __sys_write(_thread_kern_pipe[1], &c, + 1); + } while (wgot < 0 && errno == EINTR); + if (wgot < 0 && errno != EAGAIN) { + PANIC("Failed to queue signal"); + } DBG_MSG("Got signal %d, queueing to kernel pipe\n", sig); } if (_thread_sigq[sig - 1].blocked == 0) { @@ -199,15 +207,8 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) if ((pthread = thread_sig_find(sig)) == NULL) DBG_MSG("No thread to handle signal %d\n", sig); else if (pthread == curthread) { - /* - * Unblock the signal and restore the process signal - * mask in case we don't return from the handler: - */ - _thread_sigq[sig - 1].blocked = 0; - __sys_sigprocmask(SIG_SETMASK, &_process_sigmask, NULL); - /* Call the signal handler for the current thread: */ - thread_sig_invoke_handler(sig, info, ucp); + thread_sig_invoke_handler(sig, info, ucp, 2); /* * Set the process signal mask in the context; it @@ -215,6 +216,16 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) */ ucp->uc_sigmask = _process_sigmask; + /* + * The signal mask was restored; check for any + * pending signals: + */ + sigset = curthread->sigpend; + SIGSETOR(sigset, _process_sigpending); + SIGSETNAND(sigset, curthread->sigmask); + if (SIGNOTEMPTY(sigset)) + curthread->check_pending = 1; + /* Resume the interrupted thread: */ __sys_sigreturn(ucp); } else { @@ -251,7 +262,8 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) } static void -thread_sig_invoke_handler(int sig, siginfo_t *info, ucontext_t *ucp) +thread_sig_invoke_handler(int sig, siginfo_t *info, ucontext_t *ucp, + int unblock) { struct pthread *curthread = _get_curthread(); void (*sigfunc)(int, siginfo_t *, void *); @@ -271,6 +283,17 @@ thread_sig_invoke_handler(int sig, siginfo_t *info, ucontext_t *ucp) SIGSETOR(curthread->sigmask, _thread_sigact[sig - 1].sa_mask); sigaddset(&curthread->sigmask, sig); + if (unblock > 0) { + /* + * Unblock the signal and restore the process signal + * mask in case we don't return from the handler: + */ + _thread_sigq[sig - 1].blocked = 0; + if (unblock > 1) + __sys_sigprocmask(SIG_SETMASK, &_process_sigmask, + NULL); + } + /* * Check that a custom handler is installed and if * the signal is not blocked: @@ -418,13 +441,14 @@ thread_sig_find(int sig) } if (suspended_thread == NULL && - signaled_thread == NULL) + signaled_thread == NULL) { /* * Add it to the set of signals pending * on the process: */ + _thread_sigq[sig - 1].blocked = 0; sigaddset(&_process_sigpending, sig); - else { + } else { /* * We only deliver the signal to one thread; * give preference to the suspended thread: @@ -937,7 +961,7 @@ _thread_sig_send(struct pthread *pthread, int sig) sigaddset(&pthread->sigpend, sig); else if (pthread == curthread) /* Call the signal handler for the current thread: */ - thread_sig_invoke_handler(sig, NULL, NULL); + thread_sig_invoke_handler(sig, NULL, NULL, 0); else { /* Protect the scheduling queues: */ _thread_kern_sig_defer(); @@ -1004,9 +1028,6 @@ _thread_sig_wrapper(void) } } - /* Unblock the signal in case we don't return from the handler: */ - _thread_sigq[psf->signo - 1].blocked = 0; - /* * Lower the priority before calling the handler in case * it never returns (longjmps back): @@ -1023,9 +1044,10 @@ _thread_sig_wrapper(void) * Dispatch the signal via the custom signal handler: */ if (psf->sig_has_args == 0) - thread_sig_invoke_handler(psf->signo, NULL, NULL); + thread_sig_invoke_handler(psf->signo, NULL, NULL, 1); else - thread_sig_invoke_handler(psf->signo, &psf->siginfo, &psf->uc); + thread_sig_invoke_handler(psf->signo, &psf->siginfo, &psf->uc, + 1); /* * Call the kernel scheduler to safely restore the frame and |