summaryrefslogtreecommitdiffstats
path: root/lib/libpthread/thread/thr_sig.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libpthread/thread/thr_sig.c')
-rw-r--r--lib/libpthread/thread/thr_sig.c784
1 files changed, 441 insertions, 343 deletions
diff --git a/lib/libpthread/thread/thr_sig.c b/lib/libpthread/thread/thr_sig.c
index 94e5630..d06db9d 100644
--- a/lib/libpthread/thread/thr_sig.c
+++ b/lib/libpthread/thread/thr_sig.c
@@ -45,19 +45,15 @@
/* Prototypes: */
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_add(struct pthread *thread);
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
@@ -137,6 +133,65 @@ static void thr_sig_invoke_handler(struct pthread *, int sig,
* signal unmasked.
*/
+static void *
+sig_daemon(void *arg /* Unused */)
+{
+ int i;
+ kse_critical_t crit;
+ struct timespec ts;
+ sigset_t set;
+ struct kse *curkse;
+ struct pthread *curthread = _get_curthread();
+
+ DBG_MSG("signal daemon started");
+
+ curthread->name = strdup("signal thread");
+ crit = _kse_critical_enter();
+ curkse = _get_curkse();
+ SIGFILLSET(set);
+ __sys_sigprocmask(SIG_SETMASK, &set, NULL);
+ __sys_sigpending(&set);
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ while (1) {
+ KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock);
+ _thr_proc_sigpending = set;
+ KSE_LOCK_RELEASE(curkse, &_thread_signal_lock);
+ for (i = 1; i <= _SIG_MAXSIG; i++) {
+ if (SIGISMEMBER(set, i) != 0)
+ _thr_sig_dispatch(curkse, i,
+ NULL /* no siginfo */);
+ }
+ ts.tv_sec = 30;
+ ts.tv_nsec = 0;
+ curkse->k_mbx.km_flags =
+ KMF_NOUPCALL | KMF_NOCOMPLETED | KMF_WAITSIGEVENT;
+ kse_release(&ts);
+ curkse->k_mbx.km_flags = 0;
+ set = curkse->k_mbx.km_sigscaught;
+ }
+ return (0);
+}
+
+/* Utility function to create signal daemon thread */
+int
+_thr_start_sig_daemon(void)
+{
+ pthread_attr_t attr;
+ sigset_t sigset, oldset;
+
+ SIGFILLSET(sigset);
+ pthread_sigmask(SIG_SETMASK, &sigset, &oldset);
+ pthread_attr_init(&attr);
+ pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
+ /* sigmask will be inherited */
+ if (pthread_create(&_thr_sig_daemon, &attr, sig_daemon, NULL))
+ PANIC("can not create signal daemon thread!\n");
+ pthread_attr_destroy(&attr);
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ return (0);
+}
+
/*
* This signal handler only delivers asynchronous signals.
* This must be called with upcalls disabled and without
@@ -151,7 +206,6 @@ _thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info)
/* Some signals need special handling: */
handle_special_signals(curkse, sig);
- DBG_MSG("dispatch sig:%d\n", sig);
while ((thread = thr_sig_find(curkse, sig, info)) != NULL) {
/*
* Setup the target thread to receive the signal:
@@ -163,18 +217,17 @@ _thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info)
THR_IS_EXITING(thread) || THR_IS_SUSPENDED(thread)) {
KSE_SCHED_UNLOCK(curkse, thread->kseg);
_thr_ref_delete(NULL, thread);
- } else if (sigismember(&thread->tmbx.tm_context.uc_sigmask,
- sig)) {
+ } else if (SIGISMEMBER(thread->sigmask, sig)) {
KSE_SCHED_UNLOCK(curkse, thread->kseg);
_thr_ref_delete(NULL, thread);
- }
- else {
+ } else {
_thr_sig_add(thread, sig, info);
KSE_SCHED_UNLOCK(curkse, thread->kseg);
_thr_ref_delete(NULL, thread);
break;
}
}
+ DBG_MSG("<<< _thr_sig_dispatch\n");
}
void
@@ -187,7 +240,6 @@ _thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
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) &&
(sigfunc != (__siginfohandler_t *)_thr_sig_handler)) {
@@ -202,68 +254,119 @@ _thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
else {
/* Nothing. */
DBG_MSG("Got signal %d\n", sig);
- sigaddset(&curkse->k_mbx.km_sigscaught, sig);
- ucp->uc_sigmask = _thr_proc_sigmask;
+ /* XXX Bound thread will fall into this... */
}
}
+/* Must be called with signal lock and schedule lock held in order */
static void
thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info,
ucontext_t *ucp)
{
void (*sigfunc)(int, siginfo_t *, void *);
- sigset_t saved_mask;
- int saved_seqno;
+ sigset_t sigmask;
+ int sa_flags;
+ struct sigaction act;
+ struct kse *curkse;
- /* Invoke the signal handler without going through the scheduler:
+ /*
+ * Invoke the signal handler without going through the scheduler:
*/
DBG_MSG("Got signal %d, calling handler for current thread %p\n",
sig, curthread);
- /*
- * 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);
-
+ if (!_kse_in_critical())
+ PANIC("thr_sig_invoke_handler without in critical\n");
+ curkse = _get_curkse();
/*
* Check that a custom handler is installed and if
* the signal is not blocked:
*/
sigfunc = _thread_sigact[sig - 1].sa_sigaction;
- ucp->uc_sigmask = _thr_proc_sigmask;
+ sa_flags = _thread_sigact[sig - 1].sa_flags & SA_SIGINFO;
+ sigmask = curthread->sigmask;
+ SIGSETOR(curthread->sigmask, _thread_sigact[sig - 1].sa_mask);
+ if (!(sa_flags & (SA_NODEFER | SA_RESETHAND)))
+ SIGADDSET(curthread->sigmask, sig);
+ if ((sig != SIGILL) && (sa_flags & SA_RESETHAND)) {
+ if (_thread_dfl_count[sig - 1] == 0) {
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = SA_RESTART;
+ SIGEMPTYSET(act.sa_mask);
+ __sys_sigaction(sig, &act, NULL);
+ __sys_sigaction(sig, NULL, &_thread_sigact[sig - 1]);
+ }
+ }
+ KSE_LOCK_RELEASE(curkse, &_thread_signal_lock);
+ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg);
+ _kse_critical_leave(&curthread->tmbx);
+ ucp->uc_sigmask = sigmask;
+
if (((__sighandler_t *)sigfunc != SIG_DFL) &&
((__sighandler_t *)sigfunc != SIG_IGN)) {
- if (((_thread_sigact[sig - 1].sa_flags & SA_SIGINFO) != 0) ||
- (info == NULL))
+ if ((sa_flags & SA_SIGINFO) != 0 || info == NULL)
(*(sigfunc))(sig, info, ucp);
else
(*(sigfunc))(sig, (siginfo_t*)(intptr_t)info->si_code,
ucp);
+ } else {
+ /* XXX
+ * TODO: exit process if signal would kill it.
+ */
+#ifdef NOTYET
+ if (sigprop(sig) & SA_KILL)
+ kse_sigexit(sig);
+#endif
}
-
+ _kse_critical_enter();
+ /* Don't trust after critical leave/enter */
+ curkse = _get_curkse();
+ KSE_SCHED_LOCK(curkse, curkse->k_kseg);
+ KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock);
/*
* 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;
+ curthread->sigmask = ucp->uc_sigmask;
+
+ DBG_MSG("Got signal %d, handler returned %p\n", sig, curthread);
+}
+
+int
+_thr_getprocsig(int sig, siginfo_t *siginfo)
+{
+ kse_critical_t crit;
+ struct kse *curkse;
+ int ret;
+
+ DBG_MSG(">>> _thr_getprocsig\n");
+
+ crit = _kse_critical_enter();
+ curkse = _get_curkse();
+ KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock);
+ ret = _thr_getprocsig_unlocked(sig, siginfo);
+ KSE_LOCK_RELEASE(curkse, &_thread_signal_lock);
+ _kse_critical_leave(crit);
+
+ DBG_MSG("<<< _thr_getprocsig\n");
+ return (ret);
+}
+
+int
+_thr_getprocsig_unlocked(int sig, siginfo_t *siginfo)
+{
+ sigset_t sigset;
+ struct timespec ts;
+
+ /* try to retrieve signal from kernel */
+ SIGEMPTYSET(sigset);
+ SIGADDSET(sigset, sig);
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+ if (__sys_sigtimedwait(&sigset, siginfo, &ts) > 0) {
+ SIGDELSET(_thr_proc_sigpending, sig);
+ return (sig);
+ }
+ return (0);
}
/*
@@ -273,15 +376,12 @@ thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info,
struct pthread *
thr_sig_find(struct kse *curkse, int sig, siginfo_t *info)
{
- int handler_installed;
struct pthread *pthread;
struct pthread *suspended_thread, *signaled_thread;
+ siginfo_t si;
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: */
@@ -302,19 +402,39 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info)
KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock);
TAILQ_FOREACH(pthread, &_thread_list, tle) {
+ if (pthread == _thr_sig_daemon)
+ continue;
+#ifdef NOTYET
+ /* Signal delivering to bound thread is done by kernel */
+ if (pthread->attr.flags & PTHREAD_SCOPE_SYSTEM)
+ continue;
+#endif
+
/* Take the scheduling lock. */
KSE_SCHED_LOCK(curkse, pthread->kseg);
- if ((pthread->state == PS_SIGWAIT) &&
- sigismember(pthread->data.sigwait, sig)) {
+ if ((pthread->state == PS_DEAD) ||
+ (pthread->state == PS_DEADLOCK) ||
+ THR_IS_EXITING(pthread) ||
+ THR_IS_SUSPENDED(pthread) ||
+ SIGISMEMBER(pthread->sigmask, sig)) {
+ ; /* Skip this thread. */
+ } else if (pthread->state == PS_SIGWAIT) {
/*
- * Return the signal number and make the
- * thread runnable.
+ * retrieve signal from kernel, if it is job control
+ * signal, and sigaction is SIG_DFL, then we will
+ * be stopped in kernel, we hold lock here, but that
+ * does not matter, because that's job control, and
+ * whole process should be stopped.
*/
- pthread->signo = sig;
- _thr_setrunnable_unlocked(pthread);
-
+ if (_thr_getprocsig(sig, &si)) {
+ DBG_MSG("Waking thread %p in sigwait"
+ " with signal %d\n", pthread, sig);
+ /* where to put siginfo ? */
+ *(pthread->data.sigwaitinfo) = si;
+ pthread->sigmask = pthread->oldsigmask;
+ _thr_setrunnable_unlocked(pthread);
+ }
KSE_SCHED_UNLOCK(curkse, pthread->kseg);
-
/*
* POSIX doesn't doesn't specify which thread
* will get the signal if there are multiple
@@ -326,16 +446,8 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info)
* 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);
- }
- else if ((pthread->state == PS_DEAD) ||
- (pthread->state == PS_DEADLOCK) ||
- THR_IS_EXITING(pthread) || THR_IS_SUSPENDED(pthread))
- ; /* Skip this thread. */
- else if ((handler_installed != 0) &&
- !sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) {
+ } else {
if (pthread->state == PS_SIGSUSPEND) {
if (suspended_thread == NULL) {
suspended_thread = pthread;
@@ -344,49 +456,22 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info)
} else if (signaled_thread == NULL) {
signaled_thread = pthread;
signaled_thread->refcount++;
- }
+ }
}
KSE_SCHED_UNLOCK(curkse, pthread->kseg);
}
KSE_LOCK_RELEASE(curkse, &_thread_list_lock);
- /*
- * Only perform wakeups and signal delivery if there is a
- * custom handler installed:
- */
- if (handler_installed == 0) {
- /*
- * There is no handler installed; nothing to do here.
- */
- } else if (suspended_thread == NULL &&
- signaled_thread == NULL) {
- /*
- * Add it to the set of signals pending
- * on the process:
- */
- 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:
- */
- if (suspended_thread != NULL) {
- pthread = suspended_thread;
+ if (suspended_thread != NULL) {
+ pthread = suspended_thread;
+ if (signaled_thread)
_thr_ref_delete(NULL, signaled_thread);
- } else
- pthread = signaled_thread;
- return (pthread);
+ } else if (signaled_thread) {
+ pthread = signaled_thread;
+ } else {
+ pthread = NULL;
}
- return (NULL);
+ return (pthread);
}
static void
@@ -405,64 +490,80 @@ void
_thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp,
struct pthread_sigframe *psf)
{
- struct pthread_sigframe psf_save;
- sigset_t sigset;
+ siginfo_t siginfo;
int i;
+ kse_critical_t crit;
+ struct kse *curkse;
- 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);
-
+ DBG_MSG(">>> thr_sig_rundown %p\n", curthread);
/* Check the threads previous state: */
- if ((psf != NULL) && (psf_save.psf_state != PS_RUNNING)) {
+ if ((psf != NULL) && (psf->psf_valid != 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.
*/
- switch (psf_save.psf_state) {
+ switch (psf->psf_state) {
case PS_COND_WAIT:
_cond_wait_backout(curthread);
- psf_save.psf_state = PS_RUNNING;
+ psf->psf_state = PS_RUNNING;
break;
case PS_MUTEX_WAIT:
_mutex_lock_backout(curthread);
- psf_save.psf_state = PS_RUNNING;
+ psf->psf_state = PS_RUNNING;
break;
+ case PS_RUNNING:
+ break;
+
default:
+ psf->psf_state = PS_RUNNING;
break;
}
+ /* XXX see comment in thr_sched_switch_unlocked */
+ curthread->critical_count--;
}
+
/*
* Lower the priority before calling the handler in case
* it never returns (longjmps back):
*/
+ crit = _kse_critical_enter();
+ curkse = _get_curkse();
+ KSE_SCHED_LOCK(curkse, curkse->k_kseg);
+ KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock);
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);
+ while (1) {
+ for (i = 1; i <= _SIG_MAXSIG; i++) {
+ if (SIGISMEMBER(curthread->sigmask, i))
+ continue;
+ if (SIGISMEMBER(curthread->sigpend, i)) {
+ SIGDELSET(curthread->sigpend, i);
+ siginfo = curthread->siginfo[i];
+ break;
+ }
+ if (SIGISMEMBER(_thr_proc_sigpending, i)) {
+ if (_thr_getprocsig_unlocked(i, &siginfo))
+ break;
+ }
}
+ if (i <= _SIG_MAXSIG)
+ thr_sig_invoke_handler(curthread, i, &siginfo, ucp);
+ else
+ break;
}
- 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);
+ if (psf != NULL && psf->psf_valid != 0)
+ thr_sigframe_restore(curthread, psf);
+ curkse = _get_curkse();
+ KSE_LOCK_RELEASE(curkse, &_thread_signal_lock);
+ KSE_SCHED_UNLOCK(curkse, curkse->k_kseg);
+ _kse_critical_leave(&curthread->tmbx);
+
+ DBG_MSG("<<< thr_sig_rundown %p\n", curthread);
}
/*
@@ -477,87 +578,19 @@ _thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp,
void
_thr_sig_check_pending(struct pthread *curthread)
{
- sigset_t sigset;
- sigset_t pending_process;
- sigset_t pending_thread;
- kse_critical_t crit;
- int i;
-
- curthread->check_pending = 0;
-
- /*
- * Check if there are pending signals for the running
- * thread or process that aren't blocked:
- */
- 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);
+ ucontext_t uc;
+ volatile int once;
- 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;
+ if (THR_IN_CRITICAL(curthread))
+ return;
+ once = 0;
+ THR_GETCONTEXT(&uc);
+ if (once == 0) {
+ once = 1;
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);
- }
- }
- }
+ _thr_sig_rundown(curthread, &uc, NULL);
}
- else
- THR_SCHED_UNLOCK(curthread, curthread);
}
/*
@@ -575,10 +608,15 @@ handle_special_signals(struct kse *curkse, int sig)
case SIGTTIN:
case SIGTTOU:
KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock);
- sigdelset(&_thr_proc_sigpending, SIGCONT);
+ SIGDELSET(_thr_proc_sigpending, SIGCONT);
KSE_LOCK_RELEASE(curkse, &_thread_signal_lock);
break;
-
+ case SIGCONT:
+ KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock);
+ SIGDELSET(_thr_proc_sigpending, SIGTSTP);
+ SIGDELSET(_thr_proc_sigpending, SIGTTIN);
+ SIGDELSET(_thr_proc_sigpending, SIGTTOU);
+ KSE_LOCK_RELEASE(curkse, &_thread_signal_lock);
default:
break;
}
@@ -597,72 +635,92 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info)
{
int restart;
int suppress_handler = 0;
+ int fromproc = 0;
+ struct pthread *curthread = _get_curthread();
+ struct kse *curkse;
+ siginfo_t siginfo;
- 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 if (info != &pthread->siginfo[sig])
- memcpy(&pthread->siginfo[sig], info,
- sizeof(*info));
- if ((pthread->blocked != 0) && !THR_IN_CRITICAL(pthread))
- kse_thr_interrupt(&pthread->tmbx /* XXX - restart?!?! */);
- }
- else {
- restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART;
+ DBG_MSG(">>> _thr_sig_add\n");
- /* Make sure this signal isn't still in the pending set: */
- sigdelset(&pthread->sigpend, sig);
+ curkse = _get_curkse();
+ restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART;
+ fromproc = (curthread == _thr_sig_daemon);
+
+ if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK ||
+ pthread->state == PS_STATE_MAX)
+ return; /* return false */
+
+#ifdef NOTYET
+ if ((pthread->attrs.flags & PTHREAD_SCOPE_SYSTEM) != 0) {
+ if (!fromproc)
+ kse_thr_interrupt(&pthread->tmbx, 0, sig);
+ return;
+ }
+#endif
+ if (pthread->curframe == NULL ||
+ SIGISMEMBER(pthread->sigmask, sig) ||
+ THR_IN_CRITICAL(pthread)) {
+ /* thread is running or signal was being masked */
+ if (!fromproc) {
+ SIGADDSET(pthread->sigpend, sig);
+ if (info == NULL)
+ build_siginfo(&pthread->siginfo[sig], sig);
+ else if (info != &pthread->siginfo[sig])
+ memcpy(&pthread->siginfo[sig], info,
+ sizeof(*info));
+ } else {
+ if (!_thr_getprocsig(sig, &pthread->siginfo[sig]))
+ return;
+ SIGADDSET(pthread->sigpend, sig);
+ }
+ if (!SIGISMEMBER(pthread->sigmask, sig)) {
+ pthread->check_pending = 1;
+ if (pthread->blocked != 0 && !THR_IN_CRITICAL(pthread))
+ kse_thr_interrupt(&pthread->tmbx,
+ restart ? -2 : -1);
+ }
+ }
+ else {
+ /* if process signal not exists, just return */
+ if (fromproc) {
+ if (!_thr_getprocsig(sig, &siginfo))
+ return;
+ info = &siginfo;
+ }
/*
* Process according to thread state:
*/
switch (pthread->state) {
- /*
- * States which do not change when a signal is trapped:
- */
case PS_DEAD:
case PS_DEADLOCK:
+ case PS_STATE_MAX:
+ return; /* XXX return false */
case PS_LOCKWAIT:
case PS_SUSPENDED:
- case PS_STATE_MAX:
/*
* You can't call a signal handler for threads in these
* states.
*/
suppress_handler = 1;
break;
-
- /*
- * States which do not need any cleanup handling when signals
- * occur:
- */
case PS_RUNNING:
- /*
- * Remove the thread from the queue before changing its
- * priority:
- */
- if ((pthread->flags & THR_FLAGS_IN_RUNQ) != 0)
+ if ((pthread->flags & THR_FLAGS_IN_RUNQ)) {
THR_RUNQ_REMOVE(pthread);
+ pthread->active_priority |= THR_SIGNAL_PRIORITY;
+ THR_RUNQ_INSERT_TAIL(pthread);
+ } else {
+ /* Possible not in RUNQ and has curframe ? */
+ pthread->active_priority |= THR_SIGNAL_PRIORITY;
+ }
+ suppress_handler = 1;
break;
-
/*
* States which cannot be interrupted but still require the
* signal handler to run:
*/
case PS_COND_WAIT:
case PS_MUTEX_WAIT:
- /*
- * Remove the thread from the wait queue. It will
- * be added back to the wait queue once all signal
- * handlers have been invoked.
- */
- KSE_WAITQ_REMOVE(pthread->kse, pthread);
break;
case PS_SLEEP_WAIT:
@@ -671,60 +729,64 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info)
* early regardless of SA_RESTART:
*/
pthread->interrupted = 1;
- KSE_WAITQ_REMOVE(pthread->kse, pthread);
- THR_SET_STATE(pthread, PS_RUNNING);
break;
case PS_JOIN:
+ break;
+
case PS_SIGSUSPEND:
- KSE_WAITQ_REMOVE(pthread->kse, pthread);
- THR_SET_STATE(pthread, PS_RUNNING);
+ pthread->interrupted = 1;
break;
case PS_SIGWAIT:
+ if (info == NULL)
+ build_siginfo(&pthread->siginfo[sig], sig);
+ else if (info != &pthread->siginfo[sig])
+ memcpy(&pthread->siginfo[sig], info,
+ sizeof(*info));
/*
* 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)) {
+ /* Wake up the thread if the signal is not blocked. */
+ if (!SIGISMEMBER(pthread->sigmask, sig)) {
/* Return the signal number: */
- pthread->signo = sig;
-
+ *(pthread->data.sigwaitinfo) = pthread->siginfo[sig];
+ pthread->sigmask = pthread->oldsigmask;
/* Make the thread runnable: */
_thr_setrunnable_unlocked(pthread);
- } else
+ } else {
/* Increment the pending signal count. */
- sigaddset(&pthread->sigpend, sig);
- break;
+ SIGADDSET(pthread->sigpend, sig);
+ pthread->check_pending = 1;
+ }
+
+ return;
}
+ SIGADDSET(pthread->sigpend, sig);
+ if (info == NULL)
+ build_siginfo(&pthread->siginfo[sig], sig);
+ else if (info != &pthread->siginfo[sig])
+ memcpy(&pthread->siginfo[sig], info, sizeof(*info));
+
if (suppress_handler == 0) {
/*
* Setup a signal frame and save the current threads
* state:
*/
- thr_sigframe_add(pthread, sig, info);
-
- if (pthread->state != 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.
- */
+ thr_sigframe_add(pthread);
+ if (pthread->flags & THR_FLAGS_IN_RUNQ)
+ THR_RUNQ_REMOVE(pthread);
pthread->active_priority |= THR_SIGNAL_PRIORITY;
- if ((pthread->flags & THR_FLAGS_IN_RUNQ) == 0)
- THR_RUNQ_INSERT_TAIL(pthread);
+ _thr_setrunnable_unlocked(pthread);
+ } else {
+ pthread->check_pending = 1;
}
}
+
+ DBG_MSG("<<< _thr_sig_add\n");
}
static void
@@ -749,16 +811,19 @@ thr_sig_check_state(struct pthread *pthread, int sig)
break;
case PS_SIGWAIT:
+ build_siginfo(&pthread->siginfo[sig], sig);
/* Wake up the thread if the signal is blocked. */
- if (sigismember(pthread->data.sigwait, sig)) {
+ if (!SIGISMEMBER(pthread->sigmask, sig)) {
/* Return the signal number: */
- pthread->signo = sig;
-
+ *(pthread->data.sigwaitinfo) = pthread->siginfo[sig];
+ pthread->sigmask = pthread->oldsigmask;
/* Change the state of the thread to run: */
_thr_setrunnable_unlocked(pthread);
- } else
+ } else {
/* Increment the pending signal count. */
- sigaddset(&pthread->sigpend, sig);
+ SIGADDSET(pthread->sigpend, sig);
+ pthread->check_pending = 1;
+ }
break;
case PS_SIGSUSPEND:
@@ -783,6 +848,12 @@ _thr_sig_send(struct pthread *pthread, int sig)
{
struct pthread *curthread = _get_curthread();
+#ifdef NOTYET
+ if ((pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) == 0) {
+ kse_thr_interrupt(&pthread->tmbx, sig);
+ return;
+ }
+#endif
/* Lock the scheduling queue of the target thread. */
THR_SCHED_LOCK(curthread, pthread);
@@ -792,7 +863,7 @@ _thr_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 - 1] == 0) {
/*
* Deliver the signal to the process if a handler
* is not installed:
@@ -812,87 +883,47 @@ _thr_sig_send(struct pthread *pthread, int sig)
* Check that the signal is not being ignored:
*/
else if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) {
- if (pthread->state == PS_SIGWAIT &&
- sigismember(pthread->data.sigwait, sig)) {
- /* Return the signal number: */
- pthread->signo = 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);
- 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:
- */
- _thr_sig_add(pthread, sig, NULL);
- THR_SCHED_UNLOCK(curthread, pthread);
- }
+ _thr_sig_add(pthread, sig, NULL);
+ THR_SCHED_UNLOCK(curthread, pthread);
+ /* XXX
+ * If thread sent signal to itself, check signals now.
+ * It is not really needed, _kse_critical_leave should
+ * have already checked signals.
+ */
+ if (pthread == curthread && curthread->check_pending)
+ _thr_sig_check_pending(curthread);
+ } else {
+ THR_SCHED_UNLOCK(curthread, pthread);
}
}
static void
-thr_sigframe_add(struct pthread *thread, int sig, siginfo_t *info)
+thr_sigframe_add(struct pthread *thread)
{
if (thread->curframe == NULL)
PANIC("Thread doesn't have signal frame ");
- if (thread->have_signals == 0) {
+ if (thread->curframe->psf_valid == 0) {
+ thread->curframe->psf_valid = 1;
/*
* Multiple signals can be added to the same signal
* frame. Only save the thread's state the first time.
*/
thr_sigframe_save(thread, thread->curframe);
- thread->have_signals = 1;
- thread->flags &= THR_FLAGS_PRIVATE;
}
- sigaddset(&thread->curframe->psf_sigset, sig);
- if (info == NULL)
- build_siginfo(&thread->siginfo[sig], sig);
- else if (info != &thread->siginfo[sig])
- memcpy(&thread->siginfo[sig], info, sizeof(*info));
-
- /* Setup the new signal mask. */
- SIGSETOR(thread->tmbx.tm_context.uc_sigmask,
- _thread_sigact[sig - 1].sa_mask);
- sigaddset(&thread->tmbx.tm_context.uc_sigmask, sig);
}
-void
+static void
thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf)
{
+ if (psf->psf_valid == 0)
+ PANIC("invalid pthread_sigframe\n");
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
@@ -906,7 +937,74 @@ thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf)
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);
}
+
+void
+_thr_signal_init(void)
+{
+ sigset_t sigset;
+ struct sigaction act;
+ int i;
+
+ SIGFILLSET(sigset);
+ __sys_sigprocmask(SIG_SETMASK, &sigset, &_thr_initial->sigmask);
+ /* Enter a loop to get the existing signal status: */
+ for (i = 1; i <= _SIG_MAXSIG; i++) {
+ /* Check for signals which cannot be trapped: */
+ if (i == SIGKILL || i == SIGSTOP) {
+ }
+
+ /* Get the signal handler details: */
+ else if (__sys_sigaction(i, NULL,
+ &_thread_sigact[i - 1]) != 0) {
+ /*
+ * Abort this process if signal
+ * initialisation fails:
+ */
+ PANIC("Cannot read signal handler info");
+ }
+ }
+ /*
+ * Install the signal handler for SIGINFO. It isn't
+ * really needed, but it is nice to have for debugging
+ * purposes.
+ */
+ _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART;
+ SIGEMPTYSET(act.sa_mask);
+ act.sa_flags = SA_SIGINFO | SA_RESTART;
+ act.sa_sigaction = (__siginfohandler_t *)&_thr_sig_handler;
+ if (__sys_sigaction(SIGINFO, &act, NULL) != 0) {
+ /*
+ * Abort this process if signal initialisation fails:
+ */
+ PANIC("Cannot initialize signal handler");
+ }
+}
+
+void
+_thr_signal_deinit(void)
+{
+ sigset_t tmpmask, oldmask;
+ int i;
+
+ SIGFILLSET(tmpmask);
+ SIG_CANTMASK(tmpmask);
+ __sys_sigprocmask(SIG_SETMASK, &tmpmask, &oldmask);
+ /* Enter a loop to get the existing signal status: */
+ for (i = 1; i <= _SIG_MAXSIG; i++) {
+ /* Check for signals which cannot be trapped: */
+ if (i == SIGKILL || i == SIGSTOP) {
+ }
+
+ /* Set the signal handler details: */
+ else if (__sys_sigaction(i, &_thread_sigact[i - 1], NULL) != 0) {
+ /*
+ * Abort this process if signal
+ * initialisation fails:
+ */
+ PANIC("Cannot set signal handler info");
+ }
+ }
+ __sys_sigprocmask(SIG_SETMASK, &oldmask, NULL);
+}
+
OpenPOWER on IntegriCloud