summaryrefslogtreecommitdiffstats
path: root/lib/libkse/thread/thr_sig.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libkse/thread/thr_sig.c')
-rw-r--r--lib/libkse/thread/thr_sig.c210
1 files changed, 119 insertions, 91 deletions
diff --git a/lib/libkse/thread/thr_sig.c b/lib/libkse/thread/thr_sig.c
index e51d949..ac475a4 100644
--- a/lib/libkse/thread/thr_sig.c
+++ b/lib/libkse/thread/thr_sig.c
@@ -29,6 +29,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
+ * $Id$
*/
#include <signal.h>
#include <fcntl.h>
@@ -38,107 +39,51 @@
#include <pthread.h>
#include "pthread_private.h"
-/*
- * State change macro for signal handler:
- */
-#define PTHREAD_SIG_NEW_STATE(thrd, newstate) { \
- if ((_thread_run->sched_defer_count == 0) && \
- (_thread_kern_in_sched == 0)) { \
- PTHREAD_NEW_STATE(thrd, newstate); \
- } else { \
- _waitingq_check_reqd = 1; \
- PTHREAD_SET_STATE(thrd, newstate); \
- } \
-}
-
/* Static variables: */
-static int volatile yield_on_unlock_thread = 0;
-static spinlock_t thread_link_list_lock = _SPINLOCK_INITIALIZER;
-
-/* Lock the thread list: */
-void
-_lock_thread_list()
-{
- /* Lock the thread list: */
- _SPINLOCK(&thread_link_list_lock);
-}
+static spinlock_t signal_lock = _SPINLOCK_INITIALIZER;
+unsigned int pending_sigs[NSIG];
+unsigned int handled_sigs[NSIG];
+int volatile check_pending = 0;
-/* Lock the thread list: */
+/* Initialize signal handling facility: */
void
-_unlock_thread_list()
+_thread_sig_init(void)
{
- /* Unlock the thread list: */
- _SPINUNLOCK(&thread_link_list_lock);
-
- /*
- * Check if a scheduler interrupt occurred while the thread
- * list was locked:
- */
- if (yield_on_unlock_thread) {
- /* Reset the interrupt flag: */
- yield_on_unlock_thread = 0;
+ int i;
- /* This thread has overstayed it's welcome: */
- sched_yield();
+ /* Clear pending and handled signal counts: */
+ for (i = 1; i < NSIG; i++) {
+ pending_sigs[i - 1] = 0;
+ handled_sigs[i - 1] = 0;
}
+
+ /* Clear the lock: */
+ signal_lock.access_lock = 0;
}
void
_thread_sig_handler(int sig, int code, struct sigcontext * scp)
{
- char c;
- int i;
- pthread_t pthread;
-
- /*
- * Check if the pthread kernel has unblocked signals (or is about to)
- * and was on its way into a _select when the current
- * signal interrupted it:
- */
- if (_thread_kern_in_select) {
- /* Cast the signal number to a character variable: */
- c = sig;
-
- /*
- * Write the signal number to the kernel pipe so that it will
- * be ready to read when this signal handler returns. This
- * means that the _select call will complete
- * immediately.
- */
- _thread_sys_write(_thread_kern_pipe[1], &c, 1);
- }
- /* Check if the signal requires a dump of thread information: */
- if (sig == SIGINFO)
- /* Dump thread information to file: */
- _thread_dump_info();
+ char c;
+ int i;
/* Check if an interval timer signal: */
- else if (sig == _SCHED_SIGNAL) {
- /* Check if the scheduler interrupt has come at an
- * unfortunate time which one of the threads is
- * modifying the thread list:
- */
- if (thread_link_list_lock.access_lock)
+ if (sig == _SCHED_SIGNAL) {
+ if (_thread_kern_in_sched != 0) {
/*
- * Set a flag so that the thread that has
- * the lock yields when it unlocks the
- * thread list:
+ * The scheduler is already running; ignore this
+ * signal.
*/
- yield_on_unlock_thread = 1;
-
+ }
/*
* Check if the scheduler interrupt has come when
* the currently running thread has deferred thread
- * scheduling.
+ * signals.
*/
- else if (_thread_run->sched_defer_count)
- _thread_run->yield_on_sched_undefer = 1;
+ else if (_thread_run->sig_defer_count > 0)
+ _thread_run->yield_on_sig_undefer = 1;
- /*
- * Check if the kernel has not been interrupted while
- * executing scheduler code:
- */
- else if (!_thread_kern_in_sched) {
+ else {
/*
* Schedule the next thread. This function is not
* expected to return because it will do a longjmp
@@ -152,6 +97,72 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
*/
PANIC("Returned to signal function from scheduler");
}
+ }
+ /*
+ * Check if the kernel has been interrupted while the scheduler
+ * is accessing the scheduling queues or if there is a currently
+ * running thread that has deferred signals.
+ */
+ else if ((_queue_signals != 0) || ((_thread_kern_in_sched == 0) &&
+ (_thread_run->sig_defer_count > 0))) {
+ /* Cast the signal number to a character variable: */
+ c = sig;
+
+ /*
+ * Write the signal number to the kernel pipe so that it will
+ * be ready to read when this signal handler returns.
+ */
+ _thread_sys_write(_thread_kern_pipe[1], &c, 1);
+
+ /* Indicate that there are queued signals in the pipe. */
+ _sigq_check_reqd = 1;
+ }
+ else {
+ if (_atomic_lock(&signal_lock.access_lock)) {
+ /* There is another signal handler running: */
+ pending_sigs[sig - 1]++;
+ check_pending = 1;
+ }
+ else {
+ /* It's safe to handle the signal now. */
+ _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;
+ }
+
+ /* Enter a loop to process pending signals: */
+ while ((check_pending != 0) &&
+ (_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);
+ }
+ signal_lock.access_lock = 0;
+ }
+ }
+}
+
+void
+_thread_sig_handle(int sig, struct sigcontext * scp)
+{
+ int i;
+ pthread_t pthread, pthread_next;
+
+ /* Check if the signal requires a dump of thread information: */
+ if (sig == SIGINFO)
+ /* Dump thread information to file: */
+ _thread_dump_info();
+
+ /* Check if an interval timer signal: */
+ else if (sig == _SCHED_SIGNAL) {
+ /*
+ * This shouldn't ever occur (should this panic?).
+ */
} else {
/* Check if a child has terminated: */
if (sig == SIGCHLD) {
@@ -183,10 +194,9 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
* Enter a loop to discard pending SIGCONT
* signals:
*/
- for (pthread = _thread_link_list;
- pthread != NULL;
- pthread = pthread->nxt)
+ TAILQ_FOREACH(pthread, &_thread_list, tle) {
sigdelset(&pthread->sigpend,SIGCONT);
+ }
}
/*
@@ -196,11 +206,18 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
* if there are multiple waiters, we'll give it to the
* first one we find.
*/
- TAILQ_FOREACH(pthread, &_waitingq, pqe) {
+ 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 ((pthread->state == PS_SIGWAIT) &&
sigismember(pthread->data.sigwait, sig)) {
/* Change the state of the thread to run: */
- PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
+ PTHREAD_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
@@ -219,10 +236,13 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
* Enter a loop to process each thread in the linked
* list:
*/
- for (pthread = _thread_link_list; pthread != NULL;
- pthread = pthread->nxt) {
+ TAILQ_FOREACH(pthread, &_thread_list, tle) {
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;
_thread_signal(pthread,sig);
@@ -232,6 +252,10 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)
*/
_dispatch_signals();
_thread_run = pthread_saved;
+
+ /* Current thread inside critical region? */
+ if (_thread_run->sig_defer_count > 0)
+ pthread->sig_defer_count--;
}
}
@@ -284,7 +308,7 @@ _thread_signal(pthread_t pthread, int sig)
pthread->interrupted = 1;
/* Change the state of the thread to run: */
- PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
+ PTHREAD_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
@@ -296,6 +320,7 @@ _thread_signal(pthread_t pthread, int sig)
*/
case PS_FDR_WAIT:
case PS_FDW_WAIT:
+ case PS_POLL_WAIT:
case PS_SLEEP_WAIT:
case PS_SELECT_WAIT:
if (sig != SIGCHLD ||
@@ -303,8 +328,11 @@ _thread_signal(pthread_t pthread, int sig)
/* 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_SIG_NEW_STATE(pthread,PS_RUNNING);
+ PTHREAD_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
@@ -319,7 +347,7 @@ _thread_signal(pthread_t pthread, int sig)
if (!sigismember(&pthread->sigmask, sig) &&
_thread_sigact[sig - 1].sa_handler != SIG_DFL) {
/* Change the state of the thread to run: */
- PTHREAD_SIG_NEW_STATE(pthread,PS_RUNNING);
+ PTHREAD_NEW_STATE(pthread,PS_RUNNING);
/* Return the signal number: */
pthread->signo = sig;
OpenPOWER on IntegriCloud