summaryrefslogtreecommitdiffstats
path: root/lib/libkse
diff options
context:
space:
mode:
authorjasone <jasone@FreeBSD.org>2000-01-19 07:04:50 +0000
committerjasone <jasone@FreeBSD.org>2000-01-19 07:04:50 +0000
commit0b9957ff21dc2a9c577ff23b99d36d1787633701 (patch)
treefd1e0fc8602718af3b54f1661587a1462b98ccdd /lib/libkse
parent2c6582da15d1ca764e0434cfacf0ab1cc7fe11f0 (diff)
downloadFreeBSD-src-0b9957ff21dc2a9c577ff23b99d36d1787633701.zip
FreeBSD-src-0b9957ff21dc2a9c577ff23b99d36d1787633701.tar.gz
Implement continuations to correctly handle [sig|_]longjmp() inside of a
signal handler. Explicitly check for jumps to anywhere other than the current stack, since such jumps are undefined according to POSIX. While we're at it, convert thread cancellation to use continuations, since it's cleaner than the original cancellation code. Avoid delivering a signal to a thread twice. This was a pre-existing bug, but was likely unexposed until these other changes were made. Defer signals generated by pthread_kill() so that they can be delivered on the appropriate stack. deischen claims that this is unnecessary, which is likely true, but without this change, pthread_kill() can cause undefined priority queue states and/or PANICs in [sig|_]longjmp(), so I'm leaving this in for now. To compile this code out and exercise the bug, define the _NO_UNDISPATCH cpp macro. Defining _PTHREADS_INVARIANTS as well will cause earlier crashes. PR: kern/14685 Collaboration with: deischen
Diffstat (limited to 'lib/libkse')
-rw-r--r--lib/libkse/thread/Makefile.inc1
-rw-r--r--lib/libkse/thread/thr_cancel.c22
-rw-r--r--lib/libkse/thread/thr_cond.c32
-rw-r--r--lib/libkse/thread/thr_create.c36
-rw-r--r--lib/libkse/thread/thr_init.c12
-rw-r--r--lib/libkse/thread/thr_join.c8
-rw-r--r--lib/libkse/thread/thr_kern.c48
-rw-r--r--lib/libkse/thread/thr_mutex.c8
-rw-r--r--lib/libkse/thread/thr_priority_queue.c2
-rw-r--r--lib/libkse/thread/thr_private.h53
-rw-r--r--lib/libkse/thread/thr_setprio.c1
-rw-r--r--lib/libkse/thread/thr_sig.c212
12 files changed, 374 insertions, 61 deletions
diff --git a/lib/libkse/thread/Makefile.inc b/lib/libkse/thread/Makefile.inc
index 4697305..f46f007 100644
--- a/lib/libkse/thread/Makefile.inc
+++ b/lib/libkse/thread/Makefile.inc
@@ -60,6 +60,7 @@ SRCS+= \
uthread_info.c \
uthread_init.c \
uthread_ioctl.c \
+ uthread_jmp.c \
uthread_join.c \
uthread_kern.c \
uthread_kill.c \
diff --git a/lib/libkse/thread/thr_cancel.c b/lib/libkse/thread/thr_cancel.c
index de7c491..f22bfb5 100644
--- a/lib/libkse/thread/thr_cancel.c
+++ b/lib/libkse/thread/thr_cancel.c
@@ -2,11 +2,12 @@
* David Leonard <d@openbsd.org>, 1999. Public domain.
* $FreeBSD$
*/
-
#include <sys/errno.h>
#include <pthread.h>
#include "pthread_private.h"
+static void finish_cancellation(void *arg);
+
int
pthread_cancel(pthread_t pthread)
{
@@ -71,11 +72,13 @@ pthread_cancel(pthread_t pthread)
* queue. Mark the thread as interrupted and
* needing cancellation, and set the state to
* running. When the thread resumes, it will
- * exit after removing itself from the queue.
+ * remove itself from the queue and call the
+ * cancellation completion routine.
*/
pthread->interrupted = 1;
pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
+ pthread->continuation = finish_cancellation;
break;
case PS_DEAD:
@@ -172,7 +175,6 @@ pthread_testcancel(void)
void
_thread_enter_cancellation_point(void)
{
-
/* Look for a cancellation before we block: */
pthread_testcancel();
_thread_run->cancelflags |= PTHREAD_AT_CANCEL_POINT;
@@ -181,8 +183,20 @@ _thread_enter_cancellation_point(void)
void
_thread_leave_cancellation_point(void)
{
-
_thread_run->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
/* Look for a cancellation after we unblock: */
pthread_testcancel();
}
+
+static void
+finish_cancellation(void *arg)
+{
+ _thread_run->continuation = NULL;
+ _thread_run->interrupted = 0;
+
+ if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
+ _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
+ _thread_exit_cleanup();
+ pthread_exit(PTHREAD_CANCELED);
+ }
+}
diff --git a/lib/libkse/thread/thr_cond.c b/lib/libkse/thread/thr_cond.c
index 3e215af..ced48e3 100644
--- a/lib/libkse/thread/thr_cond.c
+++ b/lib/libkse/thread/thr_cond.c
@@ -157,7 +157,8 @@ pthread_cond_destroy(pthread_cond_t * cond)
int
pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
{
- int rval = 0;
+ int rval = 0;
+ int interrupted = 0;
if (cond == NULL)
rval = EINVAL;
@@ -238,6 +239,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
if (_thread_run->interrupted != 0) {
/*
+ * Remember that this thread
+ * was interrupted:
+ */
+ interrupted = 1;
+
+ /*
* Lock the condition variable
* while removing the thread.
*/
@@ -273,11 +280,8 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
break;
}
- if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
- _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
- _thread_exit_cleanup();
- pthread_exit(PTHREAD_CANCELED);
- }
+ if (interrupted != 0 && _thread_run->continuation != NULL)
+ _thread_run->continuation((void *) _thread_run);
_thread_leave_cancellation_point();
}
@@ -290,7 +294,8 @@ int
pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
const struct timespec * abstime)
{
- int rval = 0;
+ int rval = 0;
+ int interrupted = 0;
if (cond == NULL || abstime == NULL)
rval = EINVAL;
@@ -386,6 +391,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
rval = _mutex_cv_lock(mutex);
} else {
+ /*
+ * Remember if this thread was
+ * interrupted:
+ */
+ interrupted = _thread_run->interrupted;
+
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@@ -431,11 +442,8 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
break;
}
- if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
- _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
- _thread_exit_cleanup();
- pthread_exit(PTHREAD_CANCELED);
- }
+ if (interrupted != 0 && _thread_run->continuation != NULL)
+ _thread_run->continuation((void *) _thread_run);
_thread_leave_cancellation_point();
}
diff --git a/lib/libkse/thread/thr_create.c b/lib/libkse/thread/thr_create.c
index fd9e746..b8a1c46 100644
--- a/lib/libkse/thread/thr_create.c
+++ b/lib/libkse/thread/thr_create.c
@@ -189,19 +189,24 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
*/
#if defined(__FreeBSD__)
#if defined(__alpha__)
- new_thread->saved_jmp_buf[0]._jb[2] = (long) _thread_start;
- new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = 0;
- new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = (long) _thread_start;
+ new_thread->saved_jmp_buf[0]._jb[2] =
+ (long)_thread_start;
+ new_thread->saved_jmp_buf[0]._jb[4 + R_RA] =
+ 0;
+ new_thread->saved_jmp_buf[0]._jb[4 + R_T12] =
+ (long)_thread_start;
#else
- new_thread->saved_jmp_buf[0]._jb[0] = (long) _thread_start;
+ new_thread->saved_jmp_buf[0]._jb[0] =
+ (long)_thread_start;
#endif
#elif defined(__NetBSD__)
#if defined(__alpha__)
- new_thread->saved_jmp_buf[2] = (long) _thread_start;
+ new_thread->saved_jmp_buf[2] = (long)_thread_start;
new_thread->saved_jmp_buf[4 + R_RA] = 0;
- new_thread->saved_jmp_buf[4 + R_T12] = (long) _thread_start;
+ new_thread->saved_jmp_buf[4 + R_T12] =
+ (long)_thread_start;
#else
- new_thread->saved_jmp_buf[0] = (long) _thread_start;
+ new_thread->saved_jmp_buf[0] = (long)_thread_start;
#endif
#else
#error "Don't recognize this operating system!"
@@ -210,15 +215,22 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* The stack starts high and builds down: */
#if defined(__FreeBSD__)
#if defined(__alpha__)
- new_thread->saved_jmp_buf[0]._jb[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double);
+ new_thread->saved_jmp_buf[0]._jb[4 + R_SP] =
+ (long)new_thread->stack + pattr->stacksize_attr
+ - sizeof(double);
#else
- new_thread->saved_jmp_buf[0]._jb[2] = (int) (new_thread->stack + pattr->stacksize_attr - sizeof(double));
+ new_thread->saved_jmp_buf[0]._jb[2] =
+ (int)(new_thread->stack + pattr->stacksize_attr -
+ sizeof(double));
#endif
#elif defined(__NetBSD__)
#if defined(__alpha__)
- new_thread->saved_jmp_buf[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double);
+ new_thread->saved_jmp_buf[4 + R_SP] =
+ (long)new_thread->stack + pattr->stacksize_attr -
+ sizeof(double);
#else
- new_thread->saved_jmp_buf[2] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double);
+ new_thread->saved_jmp_buf[2] = (long)new_thread->stack
+ + pattr->stacksize_attr - sizeof(double);
#endif
#else
#error "Don't recognize this operating system!"
@@ -263,6 +275,8 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
new_thread->flags = 0;
new_thread->poll_data.nfds = 0;
new_thread->poll_data.fds = NULL;
+ new_thread->jmpflags = 0;
+ new_thread->continuation = NULL;
/*
* Defer signals to protect the scheduling queues
diff --git a/lib/libkse/thread/thr_init.c b/lib/libkse/thread/thr_init.c
index b6429bb..dd5f53f 100644
--- a/lib/libkse/thread/thr_init.c
+++ b/lib/libkse/thread/thr_init.c
@@ -184,6 +184,10 @@ _thread_init(void)
/* Initialize the scheduling switch hook routine: */
_sched_switch_hook = NULL;
+ /* Give this thread default attributes: */
+ memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
+ sizeof(struct pthread_attr));
+
/* Initialize the thread stack cache: */
SLIST_INIT(&_stackq);
@@ -199,6 +203,14 @@ _thread_init(void)
-1, 0) == MAP_FAILED)
PANIC("Cannot allocate red zone for initial thread");
+ /* Set the main thread stack pointer. */
+ _thread_initial->stack = (void *) USRSTACK -
+ PTHREAD_STACK_INITIAL;
+
+ /* Set the stack attributes: */
+ _thread_initial->attr.stackaddr_attr = _thread_initial->stack;
+ _thread_initial->attr.stacksize_attr = PTHREAD_STACK_INITIAL;
+
/*
* Write a magic value to the thread structure
* to help identify valid ones:
diff --git a/lib/libkse/thread/thr_join.c b/lib/libkse/thread/thr_join.c
index 155dc64..1cffc96 100644
--- a/lib/libkse/thread/thr_join.c
+++ b/lib/libkse/thread/thr_join.c
@@ -97,11 +97,9 @@ pthread_join(pthread_t pthread, void **thread_return)
_thread_kern_sig_undefer();
- if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
- _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
- _thread_exit_cleanup();
- pthread_exit(PTHREAD_CANCELED);
- }
+ if (_thread_run->interrupted != 0 &&
+ _thread_run->continuation != NULL)
+ _thread_run->continuation(_thread_run);
/* Check if the thread is not detached: */
if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) {
diff --git a/lib/libkse/thread/thr_kern.c b/lib/libkse/thread/thr_kern.c
index b3fbc3a..b833061 100644
--- a/lib/libkse/thread/thr_kern.c
+++ b/lib/libkse/thread/thr_kern.c
@@ -122,6 +122,17 @@ __asm__("fnsave %0": :"m"(*fdata));
pthread_testcancel();
}
+#ifndef _NO_UNDISPATCH
+ /*
+ * Check for undispatched signals due to calls to
+ * pthread_kill().
+ */
+ if (_thread_run->undispatched_signals != 0) {
+ _thread_run->undispatched_signals = 0;
+ _dispatch_signals();
+ }
+#endif
+
if (_sched_switch_hook != NULL) {
/* Run the installed switch hook: */
thread_run_switch_hook(_last_user_thread, _thread_run);
@@ -365,8 +376,7 @@ __asm__("fnsave %0": :"m"(*fdata));
* something happens that changes this condition:
*/
_thread_kern_poll(1);
- }
- else {
+ } else {
/* Remove the thread from the ready queue: */
PTHREAD_PRIOQ_REMOVE(pthread_h);
@@ -537,8 +547,38 @@ __asm__("fnsave %0": :"m"(*fdata));
}
}
+ /*
+ * Check if this thread is being continued from a
+ * longjmp() out of a signal handler:
+ */
+ if ((_thread_run->jmpflags & JMPFLAGS_LONGJMP) != 0) {
+ _thread_run->jmpflags = 0;
+ __longjmp(_thread_run->nested_jmp.jmp,
+ _thread_run->longjmp_val);
+ }
+ /*
+ * Check if this thread is being continued from a
+ * _longjmp() out of a signal handler:
+ */
+ else if ((_thread_run->jmpflags & JMPFLAGS__LONGJMP) !=
+ 0) {
+ _thread_run->jmpflags = 0;
+ ___longjmp(_thread_run->nested_jmp.jmp,
+ _thread_run->longjmp_val);
+ }
+ /*
+ * Check if this thread is being continued from a
+ * siglongjmp() out of a signal handler:
+ */
+ else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP)
+ != 0) {
+ _thread_run->jmpflags = 0;
+ __siglongjmp(
+ _thread_run->nested_jmp.sigjmp,
+ _thread_run->longjmp_val);
+ }
/* Check if a signal context was saved: */
- if (_thread_run->sig_saved == 1) {
+ else if (_thread_run->sig_saved == 1) {
#ifndef __alpha__
/*
* Point to the floating point data in the
@@ -571,7 +611,7 @@ __asm__("fnsave %0": :"m"(*fdata));
* was context switched out (by a longjmp to
* a different thread):
*/
- longjmp(_thread_run->saved_jmp_buf, 1);
+ __longjmp(_thread_run->saved_jmp_buf, 1);
}
/* This point should not be reached. */
diff --git a/lib/libkse/thread/thr_mutex.c b/lib/libkse/thread/thr_mutex.c
index 6526a35..6d75ea5 100644
--- a/lib/libkse/thread/thr_mutex.c
+++ b/lib/libkse/thread/thr_mutex.c
@@ -622,11 +622,9 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
*/
_thread_kern_sig_undefer();
- if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
- _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
- _thread_exit_cleanup();
- pthread_exit(PTHREAD_CANCELED);
- }
+ if (_thread_run->interrupted != 0 &&
+ _thread_run->continuation != NULL)
+ _thread_run->continuation((void *) _thread_run);
}
/* Return the completion status: */
diff --git a/lib/libkse/thread/thr_priority_queue.c b/lib/libkse/thread/thr_priority_queue.c
index 6fdf844..1b9fcba 100644
--- a/lib/libkse/thread/thr_priority_queue.c
+++ b/lib/libkse/thread/thr_priority_queue.c
@@ -287,7 +287,7 @@ _waitq_insert(pthread_t pthread)
TAILQ_INSERT_TAIL(&_waitingq, pthread, pqe);
else {
tid = TAILQ_FIRST(&_waitingq);
- while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) &&
+ while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) &&
((tid->wakeup_time.tv_sec < pthread->wakeup_time.tv_sec) ||
((tid->wakeup_time.tv_sec == pthread->wakeup_time.tv_sec) &&
(tid->wakeup_time.tv_nsec <= pthread->wakeup_time.tv_nsec))))
diff --git a/lib/libkse/thread/thr_private.h b/lib/libkse/thread/thr_private.h
index 8a126f4..8bc2d3f 100644
--- a/lib/libkse/thread/thr_private.h
+++ b/lib/libkse/thread/thr_private.h
@@ -85,7 +85,11 @@
#define PTHREAD_WAITQ_CLEARACTIVE() _waitq_clearactive()
#define PTHREAD_WAITQ_SETACTIVE() _waitq_setactive()
#else
-#define PTHREAD_WAITQ_REMOVE(thrd) TAILQ_REMOVE(&_waitingq,thrd,pqe)
+#define PTHREAD_WAITQ_REMOVE(thrd) do { \
+ TAILQ_REMOVE(&_waitingq,thrd,pqe); \
+ (thrd)->flags &= ~PTHREAD_FLAGS_IN_WAITQ; \
+} while (0)
+
#define PTHREAD_WAITQ_INSERT(thrd) do { \
if ((thrd)->wakeup_time.tv_sec == -1) \
TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \
@@ -101,6 +105,7 @@
else \
TAILQ_INSERT_BEFORE(tid,thrd,pqe); \
} \
+ (thrd)->flags | PTHREAD_FLAGS_IN_WAITQ; \
} while (0)
#define PTHREAD_WAITQ_CLEARACTIVE()
#define PTHREAD_WAITQ_SETACTIVE()
@@ -463,6 +468,12 @@ union pthread_wait_data {
};
/*
+ * Define a continuation routine that can be used to perform a
+ * transfer of control:
+ */
+typedef void (*thread_continuation_t) (void *);
+
+/*
* Thread structure.
*/
struct pthread {
@@ -514,6 +525,24 @@ struct pthread {
* if sig_saved is FALSE.
*/
jmp_buf saved_jmp_buf;
+ jmp_buf *sighandler_jmp_buf;
+
+ /*
+ * Saved jump buffers for use when doing nested [sig|_]longjmp()s, as
+ * when doing signal delivery.
+ */
+ union {
+ jmp_buf jmp;
+ sigjmp_buf sigjmp;
+ } nested_jmp;
+ int longjmp_val;
+
+#define JMPFLAGS_NONE 0x00
+#define JMPFLAGS_LONGJMP 0x01
+#define JMPFLAGS__LONGJMP 0x02
+#define JMPFLAGS_SIGLONGJMP 0x04
+#define JMPFLAGS_DEFERRED 0x08
+ int jmpflags;
/*
* TRUE if the last state saved was a signal context. FALSE if the
@@ -521,6 +550,11 @@ struct pthread {
*/
int sig_saved;
+ /*
+ * Used for tracking delivery of nested signal handlers.
+ */
+ int signal_nest_level;
+
/*
* Cancelability flags - the lower 2 bits are used by cancel
* definitions in pthread.h
@@ -530,14 +564,22 @@ struct pthread {
#define PTHREAD_CANCEL_NEEDED 0x0010
int cancelflags;
+ thread_continuation_t continuation;
+
/*
* Current signal mask and pending signals.
*/
sigset_t sigmask;
sigset_t sigpend;
+#ifndef _NO_UNDISPATCH
+ /* Non-zero if there are undispatched signals for this thread. */
+ int undispatched_signals;
+#endif
+
/* Thread state: */
enum pthread_state state;
+ enum pthread_state oldstate;
/* Time that this thread was last made active. */
struct timeval last_active;
@@ -1188,8 +1230,17 @@ pid_t _thread_sys_wait4(pid_t, int *, int, struct rusage *);
#ifdef _SYS_POLL_H_
int _thread_sys_poll(struct pollfd *, unsigned, int);
#endif
+
/* #include <sys/mman.h> */
+#ifdef _SYS_MMAN_H_
int _thread_sys_msync(void *, size_t, int);
+#endif
+
+/* #include <setjmp.h> */
+#ifdef _SETJMP_H_
+extern void __longjmp(jmp_buf, int);
+extern void __siglongjmp(sigjmp_buf, int);
+#endif
__END_DECLS
#endif /* !_PTHREAD_PRIVATE_H */
diff --git a/lib/libkse/thread/thr_setprio.c b/lib/libkse/thread/thr_setprio.c
index 5f7b44a..c630db5 100644
--- a/lib/libkse/thread/thr_setprio.c
+++ b/lib/libkse/thread/thr_setprio.c
@@ -31,7 +31,6 @@
*
* $FreeBSD$
*/
-#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
diff --git a/lib/libkse/thread/thr_sig.c b/lib/libkse/thread/thr_sig.c
index 160bdab..0f18477 100644
--- a/lib/libkse/thread/thr_sig.c
+++ b/lib/libkse/thread/thr_sig.c
@@ -37,19 +37,24 @@
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
+#include <setjmp.h>
#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
/* Prototypes: */
-static void _thread_sig_check_state(pthread_t pthread, int sig);
+static void thread_sig_check_state(pthread_t pthread, int sig);
+static void thread_sig_finish_longjmp(void *arg);
+static void handle_state_change(pthread_t pthread);
+
/* Static variables: */
static spinlock_t signal_lock = _SPINLOCK_INITIALIZER;
static unsigned int pending_sigs[NSIG];
static unsigned int handled_sigs[NSIG];
static int volatile check_pending = 0;
+static int volatile check_waiting = 0;
/* Initialize signal handling facility: */
void
@@ -73,7 +78,7 @@ _thread_sig_init(void)
void
_thread_sig_handler(int sig, int code, ucontext_t * scp)
{
- pthread_t pthread;
+ pthread_t pthread, pthread_next;
int i;
char c;
@@ -126,8 +131,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp)
/* Indicate that there are queued signals in the pipe. */
_sigq_check_reqd = 1;
- }
- else {
+ } else {
if (_atomic_lock(&signal_lock.access_lock)) {
/* There is another signal handler running: */
pending_sigs[sig - 1]++;
@@ -145,6 +149,18 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp)
signal_lock.access_lock = 0;
else {
sigaddset(&pthread->sigmask, sig);
+
+ /*
+ * Make sure not to deliver the same signal to
+ * the thread twice. sigpend is potentially
+ * modified by the call chain
+ * _thread_sig_handle() -->
+ * thread_sig_check_state(), which can happen
+ * just above.
+ */
+ if (sigismember(&pthread->sigpend, sig))
+ sigdelset(&pthread->sigpend, sig);
+
signal_lock.access_lock = 0;
_thread_sig_deliver(pthread, sig);
sigdelset(&pthread->sigmask, sig);
@@ -161,18 +177,57 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp)
pthread = _thread_sig_handle(i, scp);
if (pthread != NULL) {
sigaddset(&pthread->sigmask, i);
+ /* Save the old state: */
+ pthread->oldstate = pthread->state;
signal_lock.access_lock = 0;
_thread_sig_deliver(pthread, i);
sigdelset(&pthread->sigmask, i);
if (_atomic_lock(&signal_lock.access_lock)) {
check_pending = 1;
+ /*
+ * Have the lock holder take care
+ * of any state changes:
+ */
+ if (pthread->state != pthread->oldstate)
+ check_waiting = 1;
return;
}
+ if (pthread->state != pthread->oldstate)
+ handle_state_change(pthread);
}
}
}
+ while (check_waiting != 0) {
+ check_waiting = 0;
+ /*
+ * Enter a loop to wake up all threads waiting
+ * for a process to complete:
+ */
+ for (pthread = TAILQ_FIRST(&_waitingq);
+ pthread != NULL; pthread = pthread_next) {
+ pthread_next = TAILQ_NEXT(pthread, pqe);
+ if (pthread->state == PS_RUNNING)
+ handle_state_change(pthread);
+ }
+ }
+ /* Release the lock: */
signal_lock.access_lock = 0;
}
+
+ /*
+ * Check to see if the current thread performed a
+ * [sig|_]longjmp() out of a signal handler.
+ */
+ if ((_thread_run->jmpflags & (JMPFLAGS_LONGJMP |
+ JMPFLAGS__LONGJMP)) != 0) {
+ _thread_run->jmpflags = JMPFLAGS_NONE;
+ __longjmp(_thread_run->nested_jmp.jmp,
+ _thread_run->longjmp_val);
+ } else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) != 0) {
+ _thread_run->jmpflags = JMPFLAGS_NONE;
+ __siglongjmp(_thread_run->nested_jmp.sigjmp,
+ _thread_run->longjmp_val);
+ }
}
}
@@ -353,7 +408,7 @@ _thread_sig_handle(int sig, ucontext_t * scp)
* Perform any state changes due to signal
* arrival:
*/
- _thread_sig_check_state(pthread, sig);
+ thread_sig_check_state(pthread, sig);
return (pthread);
}
}
@@ -363,9 +418,99 @@ _thread_sig_handle(int sig, ucontext_t * scp)
return (NULL);
}
+static void
+thread_sig_finish_longjmp(void *arg)
+{
+ /*
+ * Check to see if the current thread performed a [_]longjmp() out of a
+ * signal handler.
+ */
+ if ((_thread_run->jmpflags & (JMPFLAGS_LONGJMP | JMPFLAGS__LONGJMP))
+ != 0) {
+ _thread_run->jmpflags = JMPFLAGS_NONE;
+ _thread_run->continuation = NULL;
+ __longjmp(_thread_run->nested_jmp.jmp,
+ _thread_run->longjmp_val);
+ }
+ /*
+ * Check to see if the current thread performed a siglongjmp
+ * out of a signal handler:
+ */
+ else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) != 0) {
+ _thread_run->jmpflags = JMPFLAGS_NONE;
+ _thread_run->continuation = NULL;
+ __siglongjmp(_thread_run->nested_jmp.sigjmp,
+ _thread_run->longjmp_val);
+ }
+}
+
+static void
+handle_state_change(pthread_t pthread)
+{
+ /*
+ * We should only need to handle threads whose state was
+ * changed to running:
+ */
+ if (pthread->state == PS_RUNNING) {
+ switch (pthread->oldstate) {
+ /*
+ * States which do not change when a signal is trapped:
+ */
+ case PS_DEAD:
+ case PS_DEADLOCK:
+ case PS_RUNNING:
+ case PS_SIGTHREAD:
+ case PS_STATE_MAX:
+ break;
+
+ /*
+ * States which need to return to critical sections
+ * before they can switch contexts:
+ */
+ case PS_COND_WAIT:
+ case PS_FDLR_WAIT:
+ case PS_FDLW_WAIT:
+ case PS_FILE_WAIT:
+ case PS_JOIN:
+ case PS_MUTEX_WAIT:
+ /* Indicate that the thread was interrupted: */
+ pthread->interrupted = 1;
+ /*
+ * Defer the [sig|_]longjmp until leaving the critical
+ * region:
+ */
+ pthread->jmpflags |= JMPFLAGS_DEFERRED;
+
+ /* Set the continuation routine: */
+ pthread->continuation = thread_sig_finish_longjmp;
+ /* FALLTHROUGH */
+ case PS_FDR_WAIT:
+ case PS_FDW_WAIT:
+ case PS_POLL_WAIT:
+ case PS_SELECT_WAIT:
+ case PS_SIGSUSPEND:
+ case PS_SIGWAIT:
+ case PS_SLEEP_WAIT:
+ case PS_SPINBLOCK:
+ case PS_SUSPENDED:
+ case PS_WAIT_WAIT:
+ if ((pthread->flags & PTHREAD_FLAGS_IN_WAITQ) != 0) {
+ PTHREAD_WAITQ_REMOVE(pthread);
+ if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
+ PTHREAD_WORKQ_REMOVE(pthread);
+ }
+ break;
+ }
+
+ if ((pthread->flags & PTHREAD_FLAGS_IN_PRIOQ) == 0)
+ PTHREAD_PRIOQ_INSERT_TAIL(pthread);
+ }
+}
+
+
/* Perform thread specific actions in response to a signal: */
static void
-_thread_sig_check_state(pthread_t pthread, int sig)
+thread_sig_check_state(pthread_t pthread, int sig)
{
/*
* Process according to thread state:
@@ -402,7 +547,6 @@ _thread_sig_check_state(pthread_t pthread, int sig)
sigaddset(&pthread->sigpend,sig);
break;
-
/*
* The wait state is a special case due to the handling of
* SIGCHLD signals.
@@ -483,12 +627,25 @@ _thread_sig_send(pthread_t pthread, int 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);
+ thread_sig_check_state(pthread, sig);
- /* Call the installed signal handler: */
- _thread_sig_deliver(pthread, sig);
- }
- else {
+#ifndef _NO_UNDISPATCH
+ if (_thread_run != pthread) {
+ /*
+ * Make a note to call the signal handler once
+ * the signaled thread is running. This is
+ * necessary in order to make sure that the
+ * signal is delivered on the correct stack.
+ */
+ pthread->undispatched_signals++;
+ } else {
+#endif
+ /* Call the installed signal handler. */
+ _thread_sig_deliver(pthread, sig);
+#ifndef _NO_UNDISPATCH
+ }
+#endif
+ } else {
/* Increment the pending signal count. */
sigaddset(&pthread->sigpend,sig);
}
@@ -553,6 +710,7 @@ _thread_sig_deliver(pthread_t pthread, int sig)
{
sigset_t mask;
pthread_t pthread_saved;
+ jmp_buf jb, *saved_sighandler_jmp_buf;
/*
* Check that a custom handler is installed
@@ -568,7 +726,7 @@ _thread_sig_deliver(pthread_t pthread, int sig)
/*
* Add the current signal and signal handler
- * mask to the threads current signal mask:
+ * mask to the thread's current signal mask:
*/
SIGSETOR(pthread->sigmask, _thread_sigact[sig - 1].sa_mask);
sigaddset(&pthread->sigmask, sig);
@@ -577,16 +735,36 @@ _thread_sig_deliver(pthread_t pthread, int sig)
if (_thread_run->sig_defer_count > 0)
pthread->sig_defer_count++;
- _thread_run = pthread;
+ /* Increment the number of nested signals being handled. */
+ pthread->signal_nest_level++;
/*
- * Dispatch the signal via the custom signal
- * handler:
+ * The jump buffer is allocated off the stack and the current
+ * jump buffer is saved. If the signal handler tries to
+ * [sig|_]longjmp(), our version of [sig|_]longjmp() will copy
+ * the user supplied jump buffer into
+ * _thread_run->nested_jmp.[sig]jmp and _longjmp() back to here.
*/
- (*(_thread_sigact[sig - 1].sa_handler))(sig);
+ saved_sighandler_jmp_buf = pthread->sighandler_jmp_buf;
+ pthread->sighandler_jmp_buf = &jb;
+
+ _thread_run = pthread;
+
+ if (_setjmp(jb) == 0) {
+ /*
+ * Dispatch the signal via the custom signal
+ * handler:
+ */
+ (*(_thread_sigact[sig - 1].sa_handler))(sig);
+ }
_thread_run = pthread_saved;
+ pthread->sighandler_jmp_buf = saved_sighandler_jmp_buf;
+
+ /* Decrement the signal nest level. */
+ pthread->signal_nest_level--;
+
/* Current thread inside critical region? */
if (_thread_run->sig_defer_count > 0)
pthread->sig_defer_count--;
OpenPOWER on IntegriCloud