diff options
author | Marco S Hyman <marc@cvs.openbsd.org> | 2003-01-27 22:22:31 +0000 |
---|---|---|
committer | Marco S Hyman <marc@cvs.openbsd.org> | 2003-01-27 22:22:31 +0000 |
commit | c356b59c59c09b663e2373cbf77a6ce3962c1694 (patch) | |
tree | df10bf685045545eb60831c30a1d770dbdc53070 /lib | |
parent | 220bcc2a4a0a756afc843744df1b212ed811f4ce (diff) |
pthreads signal handling improvements. With these changes all
of the thread regressions tests pass on i386, sparc, sparc64
(save the siginfo test on sparc64 due to a kernel issue) and alpha.
The tests should also pass on ppc.
In addition, it fixes the problems with the "mysql torture test"
provided by one of our users. The python port also appears to
work correctly with these changes.
Summary of changes:
* check_pending removed from thread structure, no longer used.
* unused elements of sigstatus structure removed. The sigstatus
structure is now used to keep track of siginfo data instead of
defining a local array in uthread_sig.c.
* _thread_kern_sched_sig removed
* _thread_sig_process goes away -- can't have a lock active when
signal handlers are called. Functions now call _thread_sig_handle
directly.
* _thread_clear_pending now used lib wide to clear pending flags.
It was named _clean_pending_flag and only used in uthread_sig.c.
The function clears both per thread signals, and per process signals.
* _thread_sig_handle now returns a value.
* unused debugging macros removed from the thread kernel
* dispatch pending signals after switching to a thread providing
that signal handling hasn't been deferred by the thread.
* process thread switchhooks before dispatching pending sigs
* check for thread cancellation before dispatching pending sigs
* re-wrote pthread-kill to do the correct thing. It now does
minimal thread-kill-specific processing and then calls the
existing code in uthread_sig to process the generated signal.
* shut the compiler up when compiling uthread_mutex.c
* no more "signal_lock". It does more harm than good.
* keep track of "per-process" signals.
* don't bother saving siginfo_t data for the scheduling signal.
* per posix: SIGSTOP cleared when SIGCONT received and vice versa.
* add _dispatch_signal to properly dispatch a signal to a thread.
It sets the appropriate signal mask, something that was missing
in the previous implementation. This fixes several bugs.
The previous method held a lock. If the signal handler longjmp-ed
the lock was never cleared and no more signals were processed.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libpthread/uthread/pthread_private.h | 15 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_kern.c | 78 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_kill.c | 170 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_mutex.c | 10 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_sig.c | 266 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_sigmask.c | 4 | ||||
-rw-r--r-- | lib/libpthread/uthread/uthread_sigwait.c | 7 |
7 files changed, 167 insertions, 383 deletions
diff --git a/lib/libpthread/uthread/pthread_private.h b/lib/libpthread/uthread/pthread_private.h index 78d34b82804..cae2335ef0d 100644 --- a/lib/libpthread/uthread/pthread_private.h +++ b/lib/libpthread/uthread/pthread_private.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pthread_private.h,v 1.41 2003/01/24 21:03:15 marc Exp $ */ +/* $OpenBSD: pthread_private.h,v 1.42 2003/01/27 22:22:30 marc Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -624,7 +624,6 @@ struct pthread { sigset_t sigmask; sigset_t sigpend; int sigmask_seqno; - int check_pending; /* Thread state: */ enum pthread_state state; @@ -1016,14 +1015,7 @@ SCLASS volatile int _spinblock_count /* Used to maintain pending and active signals: */ struct sigstatus { int pending; /* Is this a pending signal? */ - int blocked; /* - * A handler is currently active for - * this signal; ignore subsequent - * signals until the handler is done. - */ - int signo; /* arg 1 to signal handler */ siginfo_t siginfo; /* arg 2 to signal handler */ - struct sigcontext uc; /* arg 3 to signal handler */ }; SCLASS struct sigstatus _thread_sigq[NSIG]; @@ -1095,11 +1087,11 @@ void _waitq_clearactive(void); __dead void _thread_exit(const char *, int, const char *) __attribute__((__noreturn__)); void *_thread_cleanup(pthread_t); void _thread_cleanupspecific(void); +void _thread_clear_pending(int, pthread_t); void _thread_dump_data(const void *, int); void _thread_dump_info(void); void _thread_init(void); void _thread_kern_sched(struct sigcontext *); -void _thread_kern_sched_sig(void); void _thread_kern_sched_state(enum pthread_state,char *fname,int lineno); void _thread_kern_sched_state_unlock(enum pthread_state state, spinlock_t *lock, char *fname, int lineno); @@ -1107,9 +1099,8 @@ void _thread_kern_set_timeout(const struct timespec *); void _thread_kern_sig_defer(void); void _thread_kern_sig_undefer(void); void _thread_sig_handler(int, siginfo_t *, struct sigcontext *); -void _thread_sig_handle(int, struct sigcontext *); +int _thread_sig_handle(int, struct sigcontext *); void _thread_sig_init(void); -void _thread_sig_process(int, struct sigcontext *); void _thread_start(void); void _thread_start_sig_handler(void); void _thread_seterrno(pthread_t,int); diff --git a/lib/libpthread/uthread/uthread_kern.c b/lib/libpthread/uthread/uthread_kern.c index 9930098a974..d72e7b5ed9e 100644 --- a/lib/libpthread/uthread/uthread_kern.c +++ b/lib/libpthread/uthread/uthread_kern.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_kern.c,v 1.24 2003/01/24 21:03:15 marc Exp $ */ +/* $OpenBSD: uthread_kern.c,v 1.25 2003/01/27 22:22:30 marc Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -50,12 +50,6 @@ #include <pthread.h> #include "pthread_private.h" -#if defined(PTHREAD_TRACE_KERN) -#define PTHREAD_TRACE(x) _thread_sys_write(-1, (void*) x, 0) -#else -#define PTHREAD_TRACE(x) -#endif - /* * local functions. Do NOT make these static... we want so see them in * crash dumps. @@ -68,16 +62,6 @@ inline void _thread_run_switch_hook(pthread_t, pthread_t); static int last_tick = 0; void -_thread_kern_sched_sig(void) -{ - struct pthread *curthread = _get_curthread(); - - PTHREAD_TRACE(1); - curthread->check_pending = 1; - _thread_kern_sched(NULL); -} - -void _thread_kern_sched(struct sigcontext * scp) { struct timespec ts; @@ -88,8 +72,6 @@ _thread_kern_sched(struct sigcontext * scp) int add_to_prioq; pthread_t old_thread_run; - PTHREAD_TRACE(2); - /* * Flag the pthread kernel as executing scheduler code * to avoid a scheduler signal from interrupting this @@ -257,23 +239,6 @@ _thread_kern_sched(struct sigcontext * scp) PTHREAD_WORKQ_INSERT(curthread); break; } - - /* - * Are there pending signals for this thread? - * - * This check has to be performed after the thread - * has been placed in the queue(s) appropriate for - * its state. The process of adding pending signals - * can change a threads state, which in turn will - * attempt to add or remove the thread from any - * scheduling queue to which it belongs. - */ -#ifdef notyet - if (curthread->check_pending != 0) { - curthread->check_pending = 0; - _thread_sig_check_pending(curthread); - } -#endif } /* @@ -502,18 +467,10 @@ _thread_kern_sched(struct sigcontext * scp) /* Restore floating point state. */ _thread_machdep_restore_float_state(&curthread->_machdep); - /* - * Restore the new thread, saving current. - */ + /* Restore the new thread, saving current. */ _thread_machdep_switch(&curthread->_machdep, &old_thread_run->_machdep); - - /* - * Process any pending signals for the thread we - * just switched to. - */ _thread_kern_in_sched = 0; - _dispatch_signals(scp); /* run any installed switch-hooks */ if ((_sched_switch_hook != NULL) && @@ -529,6 +486,10 @@ _thread_kern_sched(struct sigcontext * scp) PTHREAD_CANCEL_ASYNCHRONOUS) != 0)) pthread_testcancel(); + /* dispatch any pending signals if possible */ + if (curthread->sig_defer_count == 0) + _dispatch_signals(scp); + /* Check if a signal context was saved: */ if (curthread->sig_saved == 1) { /* return to signal handler. This code @@ -556,7 +517,6 @@ _thread_kern_sched_state(enum pthread_state state, char *fname, int lineno) { struct pthread *curthread = _get_curthread(); - PTHREAD_TRACE(3); /* * Flag the pthread kernel as executing scheduler code * to avoid a scheduler signal from interrupting this @@ -585,8 +545,6 @@ _thread_kern_sched_state_unlock(enum pthread_state state, { struct pthread *curthread = _get_curthread(); - PTHREAD_TRACE(4); - /* * Flag the pthread kernel as executing scheduler code * to avoid a scheduler signal from interrupting this @@ -624,8 +582,6 @@ _thread_kern_poll(int wait_reqd) struct timespec ts; struct timeval tv; - PTHREAD_TRACE(5); - /* Check if the caller wants to wait: */ if (wait_reqd == 0) { timeout_ms = 0; @@ -948,8 +904,6 @@ _thread_kern_set_timeout(const struct timespec * timeout) struct timespec current_time; struct timeval tv; - PTHREAD_TRACE(6); - /* Reset the timeout flag for the running thread: */ curthread->timeout = 0; @@ -990,8 +944,6 @@ _thread_kern_sig_defer(void) { struct pthread *curthread = _get_curthread(); - PTHREAD_TRACE(7); - /* Allow signal deferral to be recursive. */ curthread->sig_defer_count++; } @@ -1001,8 +953,6 @@ _thread_kern_sig_undefer(void) { struct pthread *curthread = _get_curthread(); - PTHREAD_TRACE(8); - /* * Perform checks to yield only if we are about to undefer * signals. @@ -1049,8 +999,6 @@ _dequeue_signals(void) char bufr[128]; int i, num; - PTHREAD_TRACE(9); - /* * Enter a loop to read and handle queued signals from the * pthread kernel pipe: @@ -1062,16 +1010,8 @@ _dequeue_signals(void) * each byte is the signal number. */ for (i = 0; i < num; i++) { - if ((int) bufr[i] == _SCHED_SIGNAL) { - /* - * Scheduling signals shouldn't ever be - * queued; just ignore it for now. - */ - } - else { - /* Handle this signal: */ - _thread_sig_process((int) bufr[i], NULL); - } + if ((int) bufr[i] != _SCHED_SIGNAL) + _thread_sig_handle((int) bufr[i], NULL); } } if ((num < 0) && (errno != EAGAIN)) { @@ -1089,8 +1029,6 @@ _thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in) pthread_t tid_out = thread_out; pthread_t tid_in = thread_in; - PTHREAD_TRACE(10); - if ((tid_out != NULL) && (tid_out->flags & PTHREAD_FLAGS_PRIVATE) != 0) tid_out = NULL; diff --git a/lib/libpthread/uthread/uthread_kill.c b/lib/libpthread/uthread/uthread_kill.c index 59d78b80ebf..9e94a7f0ee4 100644 --- a/lib/libpthread/uthread/uthread_kill.c +++ b/lib/libpthread/uthread/uthread_kill.c @@ -1,37 +1,6 @@ -/* $OpenBSD: uthread_kill.c,v 1.8 2002/10/30 19:11:56 marc Exp $ */ -/* - * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by John Birrell. - * 4. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: uthread_kill.c,v 1.9 1999/08/28 00:03:38 peter Exp $ - */ +/* $OpenBSD: uthread_kill.c,v 1.9 2003/01/27 22:22:30 marc Exp $ */ +/* PUBLIC_DOMAIN <marc@snafu.org> */ + #include <errno.h> #include <signal.h> #ifdef _THREAD_SAFE @@ -39,132 +8,29 @@ #include "pthread_private.h" /* - * XXX THIS IS WRONG! The signal has to either come through the OS to - * get the proper siginfo, context, etc., or we need to gen up a - * siginfo (assuming needed). Signal reset and other semantics - * also need to be obeyed. + * Validate the signal number and thread. If valid process the signal. + * ;;; need to fake up a siginfo_t and put it in the queue for this signal. */ int pthread_kill(pthread_t pthread, int sig) { int ret; - /* Check for invalid signal numbers: */ - 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 - * not being ignored: - */ - else if (((ret = _find_thread(pthread)) == 0) && (sig > 0) && - (_thread_sigact[sig - 1].sa_handler != SIG_IGN)) { - /* - * Defer signals to protect the scheduling queues from - * access by the signal handler: - */ - _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; - } - 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 - 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 { - sigaddset(&pthread->sigpend,sig); + if (sig >= 0 && sig < NSIG) { + ret = _find_thread(pthread); + if (sig != 0) { + if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) { + if (pthread->state == PS_SIGWAIT && + sigismember(pthread->data.sigwait, sig)) { + PTHREAD_NEW_STATE(pthread,PS_RUNNING); + pthread->signo = sig; + } else + _thread_signal(pthread,sig); } - break; - - default: - /* Increment the pending signal count: */ - sigaddset(&pthread->sigpend,sig); - break; } + } else + ret = EINVAL; - - /* - * 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: ;;; what about SA_SIGINFO??? - */ - (*(_thread_sigact[sig - 1].sa_handler))(sig); - - _thread_run = pthread_saved; - - if (_thread_run->sig_defer_count > 0) - pthread->sig_defer_count--; - } - - /* - * Undefer and handle pending signals, yielding if - * necessary: - */ - _thread_kern_sig_undefer(); - } - - /* Return the completion status: */ - return (ret); + return ret; } #endif diff --git a/lib/libpthread/uthread/uthread_mutex.c b/lib/libpthread/uthread/uthread_mutex.c index 79bd3573379..5845cf01a6b 100644 --- a/lib/libpthread/uthread/uthread_mutex.c +++ b/lib/libpthread/uthread/uthread_mutex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_mutex.c,v 1.15 2002/10/30 19:11:56 marc Exp $ */ +/* $OpenBSD: uthread_mutex.c,v 1.16 2003/01/27 22:22:30 marc Exp $ */ /* * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -114,11 +114,11 @@ int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * mutex_attr) { - enum pthread_mutextype type; - int protocol; - int ceiling; - int flags; pthread_mutex_t pmutex; + enum pthread_mutextype type = 0; + int protocol = 0; + int ceiling = 0; + int flags = 0; int ret = 0; if (mutex == NULL) diff --git a/lib/libpthread/uthread/uthread_sig.c b/lib/libpthread/uthread/uthread_sig.c index 17eef3fa38e..d833465bf3a 100644 --- a/lib/libpthread/uthread/uthread_sig.c +++ b/lib/libpthread/uthread/uthread_sig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_sig.c,v 1.17 2003/01/24 21:03:15 marc Exp $ */ +/* $OpenBSD: uthread_sig.c,v 1.18 2003/01/27 22:22:30 marc Exp $ */ /* * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. @@ -41,11 +41,6 @@ #include <pthread.h> #include "pthread_private.h" -/* Static variables: */ -static _spinlock_lock_t signal_lock = _SPINLOCK_UNLOCKED; -siginfo_t info_queue[NSIG]; -volatile sig_atomic_t pending_sigs[NSIG]; -volatile sig_atomic_t check_pending = 0; /* Initialize signal handling facility: */ void @@ -55,32 +50,8 @@ _thread_sig_init(void) /* Clear local state */ for (i = 1; i < NSIG; i++) { - pending_sigs[i - 1] = 0; + _thread_sigq[i - 1].pending = 0; } - - /* Clear the lock: */ - signal_lock = _SPINLOCK_UNLOCKED; -} - -/* - * Process a pending signal unless it is already in progress. If the - * SA_NODEFER flag is on, process it any way. - */ -void -_thread_sig_process(int sig, struct sigcontext * scp) -{ - int locked = 0; - - if (_atomic_lock(&signal_lock) == 0) - locked = 1; - - if (locked || _thread_sigact[sig - 1].sa_flags & SA_NODEFER) { - pending_sigs[sig - 1] = 0; - _thread_sig_handle(sig, scp); - } else - check_pending = 1; - if (locked) - signal_lock = _SPINLOCK_UNLOCKED; } /* @@ -93,17 +64,6 @@ _thread_sig_handler(int sig, siginfo_t *info, struct sigcontext * scp) { struct pthread *curthread = _get_curthread(); char c; - int i; - - /* - * save the info for this signal in a per signal queue of depth - * one. Per a POSIX suggestion, only the info for the first - * of multiple activations of the same signal is kept. - */ - if (pending_sigs[sig - 1] == 0) { - pending_sigs[sig - 1]++; - memcpy(&info_queue[sig - 1], info, sizeof *info); - } if (sig == _SCHED_SIGNAL) { /* Update the scheduling clock: */ @@ -138,59 +98,68 @@ _thread_sig_handler(int sig, siginfo_t *info, struct sigcontext * scp) return; } } - } else if ((_queue_signals != 0) || - ((_thread_kern_in_sched == 0) && - (curthread->sig_defer_count > 0))) { - /* - * The kernel has been interrupted while the scheduler - * is accessing the scheduling queues or there is a currently - * running thread that has deferred signals. - * - * Cast the signal number to a character variable and Write - * the signal number to the kernel pipe so that it will be - * ready to read when this signal handler returns. - */ - c = sig; - _thread_sys_write(_thread_kern_pipe[1], &c, 1); - _sigq_check_reqd = 1; } else { - /* process the signal */ - _thread_sig_process(sig, scp); /* - * process pending signals unless a current signal handler - * is running (signal_lock is locked). When locked - * the pending signals will be processed when the running - * handler returns. + * save the info for this signal in a per signal queue of depth + * one. Per a POSIX suggestion, only the info for the first + * of multiple activations of the same signal is kept. */ - while (check_pending != 0 && _atomic_lock(&signal_lock) == 0) { - check_pending = 0; - signal_lock = _SPINLOCK_UNLOCKED; - for (i = 1; i < NSIG; i++) - if (pending_sigs[i - 1]) - _thread_sig_process(i, scp); + if (_thread_sigq[sig - 1].pending == 0) { + sigaddset(&_process_sigpending, sig); + _thread_sigq[sig - 1].pending++; + memcpy(&_thread_sigq[sig - 1].siginfo, info, + sizeof *info); } - } + if ((_queue_signals != 0) || + ((_thread_kern_in_sched == 0) && + (curthread->sig_defer_count > 0))) { + /* + * The kernel has been interrupted while the scheduler + * is accessing the scheduling queues or there is a + * currently running thread that has deferred signals. + * + * Cast the signal number to a character variable + * and Write the signal number to the kernel pipe so + * that it will be ready to read when this signal + * handler returns. + */ + c = sig; + _thread_sys_write(_thread_kern_pipe[1], &c, 1); + _sigq_check_reqd = 1; + } else { + if (_thread_sig_handle(sig, scp)) + _dispatch_signals(scp); + } + } } /* * Clear the pending flag for the given signal on all threads + * if per process, or only for the given thread if non null + * and the signal doesn't exist in _process_sigpending. */ void -_clear_pending_flag(int sig) +_thread_clear_pending(int sig, pthread_t thread) { pthread_t pthread; - TAILQ_FOREACH(pthread, &_thread_list, tle) { - sigdelset(&pthread->sigpend, sig); - } + if (sigismember(&_process_sigpending, sig)) { + _thread_sigq[sig - 1].pending = 0; + sigdelset(&_process_sigpending, sig); + TAILQ_FOREACH(pthread, &_thread_list, tle) { + sigdelset(&pthread->sigpend, sig); + } + } else if (thread) + sigdelset(&thread->sigpend, sig); } /* - * Process the given signal. + * Process the given signal. Returns 1 if the signal may be dispatched, + * otherwise 0. */ -void +int _thread_sig_handle(int sig, struct sigcontext * scp) { struct pthread *curthread = _get_curthread(); @@ -216,11 +185,27 @@ _thread_sig_handle(int sig, struct sigcontext * scp) } /* - * POSIX says that pending SIGCONT signals are - * discarded when one of these signals occurs. + * If the handler is SIG_IGN the signal never happened. + * remove it from the pending list and return. + */ + if (_thread_sigact[sig - 1].sa_handler == SIG_IGN) { + _thread_clear_pending(sig, curthread); + return 0; + } + + /* + * POSIX says that pending SIGCONT signals are discarded + * when one of these signals occurs and vice versa. */ - if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) - _clear_pending_flag(SIGCONT); + if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || + sig == SIGTTOU) + _thread_clear_pending(SIGCONT, curthread); + if (sig == SIGCONT) { + _thread_clear_pending(SIGSTOP, curthread); + _thread_clear_pending(SIGTSTP, curthread); + _thread_clear_pending(SIGTTIN, curthread); + _thread_clear_pending(SIGTTOU, curthread); + } /* * Enter a loop to process each thread in the waiting @@ -239,61 +224,49 @@ _thread_sig_handle(int sig, struct sigcontext * scp) 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; - /* - * Do not attempt to deliver this signal - * to other threads. + * found a sigwaiter. Mark its state as + * running, save the signal that will be + * returned, and mark the signal as no + * longer pending. */ - return; + PTHREAD_NEW_STATE(pthread,PS_RUNNING); + pthread->signo = sig; + _thread_clear_pending(sig, pthread); + return 0; } } - if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) { - /* - * mark the signal as pending for each thread - * and give the thread a chance to update - * its state. - */ - TAILQ_FOREACH(pthread, &_thread_list, tle) { - /* Current thread inside critical region? */ - if (curthread->sig_defer_count > 0) - pthread->sig_defer_count++; - _thread_signal(pthread,sig); - if (curthread->sig_defer_count > 0) - pthread->sig_defer_count--; - } - /* - * Give the current thread a chance to dispatch - * the signals. Other threads will get thier - * chance (if the signal is still pending) later. - */ - _dispatch_signals(scp); - + /* + * mark the signal as pending for each thread + * and give the thread a chance to update + * its state. + */ + TAILQ_FOREACH(pthread, &_thread_list, tle) { + /* Current thread inside critical region? */ + if (curthread->sig_defer_count > 0) + pthread->sig_defer_count++; + _thread_signal(pthread,sig); + if (curthread->sig_defer_count > 0) + pthread->sig_defer_count--; } + return 1; } + return 0; } /* Perform thread specific actions in response to a signal: */ void _thread_signal(pthread_t pthread, int sig) { - /* - * Flag the signal as pending. It may be dispatched later. - */ + /* Flag the signal as pending. It may be dispatched later. */ sigaddset(&pthread->sigpend,sig); /* skip this thread if signal is masked */ if (sigismember(&pthread->sigmask, sig)) return; - /* - * Process according to thread state: - */ + /* Process according to thread state: */ switch (pthread->state) { /* * States which do not change when a signal is trapped: @@ -377,14 +350,49 @@ _thread_signal(pthread_t pthread, int sig) } /* - * possibly dispatch a signal to the current thread. + * Dispatch a signal to the current thread after setting up the + * appropriate signal mask. */ void -_dispatch_signals(struct sigcontext * scp) +_dispatch_signal(int sig, struct sigcontext * scp) { struct pthread *curthread = _get_curthread(); + + sigset_t set; + sigset_t oset; struct sigaction act; void (*action)(int, siginfo_t *, void *); + + _thread_clear_pending(sig, curthread); + + /* save off the action and set the signal mask */ + action = _thread_sigact[sig - 1].sa_sigaction; + set = _thread_sigact[sig - 1].sa_mask; + if ((_thread_sigact[sig-1].sa_flags & SA_NODEFER) == 0) + sigaddset(&set, sig); + oset = curthread->sigmask; + curthread->sigmask |= set; + + /* clear custom handler if SA_RESETHAND set. */ + if (_thread_sigact[sig - 1].sa_flags & SA_RESETHAND) { + act.sa_handler = SIG_DFL; + act.sa_flags = 0; + sigemptyset(&act.sa_mask); + sigaction(sig, &act, NULL); + } + + /* call the action and reset the signal mask */ + (*action)(sig, &_thread_sigq[sig - 1].siginfo, scp); + curthread->sigmask = oset; +} + +/* + * possibly dispatch a signal to the current thread. + */ +void +_dispatch_signals(struct sigcontext * scp) +{ + struct pthread *curthread = _get_curthread(); int i; /* @@ -401,24 +409,8 @@ _dispatch_signals(struct sigcontext * scp) if (_thread_sigact[i - 1].sa_handler != SIG_DFL && _thread_sigact[i - 1].sa_handler != SIG_IGN && sigismember(&curthread->sigpend,i) && - !sigismember(&curthread->sigmask,i)) { - action = _thread_sigact[i - 1].sa_sigaction; - _clear_pending_flag(i); - - /* clear custom handler if SA_RESETHAND set. */ - if (_thread_sigact[i - 1].sa_flags & - SA_RESETHAND) { - act.sa_handler = SIG_DFL; - act.sa_flags = 0; - sigemptyset(&act.sa_mask); - sigaction(i, &act, NULL); - } - - /* - * Dispatch the signal via the custom signal - * handler. - */ - (*action)(i, &info_queue[i - 1], scp); - } + !sigismember(&curthread->sigmask,i)) + /* dispatch */ + _dispatch_signal(i, scp); } #endif diff --git a/lib/libpthread/uthread/uthread_sigmask.c b/lib/libpthread/uthread/uthread_sigmask.c index 12dd6ec3c69..0b2e54e0652 100644 --- a/lib/libpthread/uthread/uthread_sigmask.c +++ b/lib/libpthread/uthread/uthread_sigmask.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_sigmask.c,v 1.5 2002/02/21 20:57:41 fgsch Exp $ */ +/* $OpenBSD: uthread_sigmask.c,v 1.6 2003/01/27 22:22:30 marc Exp $ */ /* * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -95,7 +95,7 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) * Call the kernel scheduler which will safely * install a signal frame for the running thread: */ - _thread_kern_sched_sig(); + _thread_kern_sched(NULL); } /* Return the completion status: */ diff --git a/lib/libpthread/uthread/uthread_sigwait.c b/lib/libpthread/uthread/uthread_sigwait.c index b7b72c7ea09..6e4bc5fc8a9 100644 --- a/lib/libpthread/uthread/uthread_sigwait.c +++ b/lib/libpthread/uthread/uthread_sigwait.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uthread_sigwait.c,v 1.11 2002/10/30 19:11:56 marc Exp $ */ +/* $OpenBSD: uthread_sigwait.c,v 1.12 2003/01/27 22:22:30 marc Exp $ */ /* * Copyright (c) 1997 John Birrell <jb@cimlogic.com.au>. * All rights reserved. @@ -82,10 +82,7 @@ sigwait(const sigset_t * set, int *sig) } /* Clear the pending signal: */ - if (sigismember(&curthread->sigpend, i)) - sigdelset(&curthread->sigpend, i); - else - sigdelset(&_process_sigpending, i); + _thread_clear_pending(i, curthread); /* Return the signal number to the caller: */ *sig = i; |