summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libc_r/uthread/Makefile.inc1
-rw-r--r--lib/libc_r/uthread/pthread_private.h53
-rw-r--r--lib/libc_r/uthread/uthread_cancel.c22
-rw-r--r--lib/libc_r/uthread/uthread_cond.c32
-rw-r--r--lib/libc_r/uthread/uthread_create.c36
-rw-r--r--lib/libc_r/uthread/uthread_fd.c24
-rw-r--r--lib/libc_r/uthread/uthread_file.c9
-rw-r--r--lib/libc_r/uthread/uthread_init.c12
-rw-r--r--lib/libc_r/uthread/uthread_jmp.c199
-rw-r--r--lib/libc_r/uthread/uthread_join.c8
-rw-r--r--lib/libc_r/uthread/uthread_kern.c48
-rw-r--r--lib/libc_r/uthread/uthread_mutex.c8
-rw-r--r--lib/libc_r/uthread/uthread_priority_queue.c2
-rw-r--r--lib/libc_r/uthread/uthread_setprio.c1
-rw-r--r--lib/libc_r/uthread/uthread_sig.c212
-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
-rw-r--r--lib/libpthread/thread/Makefile.inc1
-rw-r--r--lib/libpthread/thread/thr_cancel.c22
-rw-r--r--lib/libpthread/thread/thr_cond.c32
-rw-r--r--lib/libpthread/thread/thr_create.c36
-rw-r--r--lib/libpthread/thread/thr_init.c12
-rw-r--r--lib/libpthread/thread/thr_join.c8
-rw-r--r--lib/libpthread/thread/thr_kern.c48
-rw-r--r--lib/libpthread/thread/thr_mutex.c8
-rw-r--r--lib/libpthread/thread/thr_priority_queue.c2
-rw-r--r--lib/libpthread/thread/thr_private.h53
-rw-r--r--lib/libpthread/thread/thr_setprio.c1
-rw-r--r--lib/libpthread/thread/thr_sig.c212
39 files changed, 1332 insertions, 205 deletions
diff --git a/lib/libc_r/uthread/Makefile.inc b/lib/libc_r/uthread/Makefile.inc
index 4697305..f46f007 100644
--- a/lib/libc_r/uthread/Makefile.inc
+++ b/lib/libc_r/uthread/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/libc_r/uthread/pthread_private.h b/lib/libc_r/uthread/pthread_private.h
index 8a126f4..8bc2d3f 100644
--- a/lib/libc_r/uthread/pthread_private.h
+++ b/lib/libc_r/uthread/pthread_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/libc_r/uthread/uthread_cancel.c b/lib/libc_r/uthread/uthread_cancel.c
index de7c491..f22bfb5 100644
--- a/lib/libc_r/uthread/uthread_cancel.c
+++ b/lib/libc_r/uthread/uthread_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/libc_r/uthread/uthread_cond.c b/lib/libc_r/uthread/uthread_cond.c
index 3e215af..ced48e3 100644
--- a/lib/libc_r/uthread/uthread_cond.c
+++ b/lib/libc_r/uthread/uthread_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/libc_r/uthread/uthread_create.c b/lib/libc_r/uthread/uthread_create.c
index fd9e746..b8a1c46 100644
--- a/lib/libc_r/uthread/uthread_create.c
+++ b/lib/libc_r/uthread/uthread_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/libc_r/uthread/uthread_fd.c b/lib/libc_r/uthread/uthread_fd.c
index d9a1636..77ccad1 100644
--- a/lib/libc_r/uthread/uthread_fd.c
+++ b/lib/libc_r/uthread/uthread_fd.c
@@ -480,14 +480,10 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout)
_SPINUNLOCK(&_thread_fd_table[fd]->lock);
if (_thread_run->interrupted != 0) {
- if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) == 0) {
- ret = -1;
- errno = EINTR;
- } else {
- _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
- _thread_exit_cleanup();
- pthread_exit(PTHREAD_CANCELED);
- }
+ ret = -1;
+ errno = EINTR;
+ if (_thread_run->continuation != NULL)
+ _thread_run->continuation((void *)_thread_run);
}
}
@@ -816,14 +812,10 @@ _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout,
_SPINUNLOCK(&_thread_fd_table[fd]->lock);
if (_thread_run->interrupted != 0) {
- if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) == 0) {
- ret = -1;
- errno = EINTR;
- } else {
- _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
- _thread_exit_cleanup();
- pthread_exit(PTHREAD_CANCELED);
- }
+ ret = -1;
+ errno = EINTR;
+ if (_thread_run->continuation != NULL)
+ _thread_run->continuation((void *)_thread_run);
}
}
diff --git a/lib/libc_r/uthread/uthread_file.c b/lib/libc_r/uthread/uthread_file.c
index f1bac17..88b6a8f 100644
--- a/lib/libc_r/uthread/uthread_file.c
+++ b/lib/libc_r/uthread/uthread_file.c
@@ -255,12 +255,9 @@ _flockfile_debug(FILE * fp, char *fname, int lineno)
_thread_kern_sig_undefer();
- if (((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) &&
- (_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) != 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;
diff --git a/lib/libc_r/uthread/uthread_init.c b/lib/libc_r/uthread/uthread_init.c
index b6429bb..dd5f53f 100644
--- a/lib/libc_r/uthread/uthread_init.c
+++ b/lib/libc_r/uthread/uthread_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/libc_r/uthread/uthread_jmp.c b/lib/libc_r/uthread/uthread_jmp.c
new file mode 100644
index 0000000..4be77cf
--- /dev/null
+++ b/lib/libc_r/uthread/uthread_jmp.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2000 Jason Evans <jasone@canonware.com>.
+ * All rights reserved.
+ * Copyright (C) 2000 Daniel M. Eischen <eischen@vigrid.com>.
+ * 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(s), this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified other than the possible
+ * addition of one or more copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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$
+ */
+
+#include <unistd.h>
+#include <setjmp.h>
+#include <sys/param.h>
+#include <sys/user.h>
+#ifdef _THREAD_SAFE
+#include <machine/reg.h>
+#include <pthread.h>
+#include "pthread_private.h"
+
+/*
+ * Offset into the jmp_buf. This is highly machine-dependent, but is a
+ * necessary evil in order to compare stack pointers and make decisions based on
+ * where a *longjmp() is jumping to.
+ */
+#if defined(__i386__)
+#define JMP_BUF_SP_OFFSET 2
+#elif defined(__alpha)
+#define JMP_BUF_SP_OFFSET (4 + R_SP)
+#else
+#error "Don't recognize this architecture!"
+#endif
+
+void
+_libc_siglongjmp(sigjmp_buf env, int savemask)
+{
+ void *jmp_stackp;
+ void *stack_begin, *stack_end;
+
+ if (_thread_run->signal_nest_level == 0)
+ __siglongjmp(env, savemask);
+
+ /* Get the stack pointer from the jump buffer. */
+ jmp_stackp = (void *)env->_sjb[JMP_BUF_SP_OFFSET];
+
+ /* Get the bounds of the current threads stack. */
+ if (_thread_run->stack != NULL) {
+ stack_begin = _thread_run->stack;
+ stack_end = stack_begin + _thread_run->attr.stacksize_attr;
+ } else {
+ stack_end = (void *)USRSTACK;
+ stack_begin = stack_end - PTHREAD_STACK_INITIAL;
+ }
+
+ /*
+ * Make sure we aren't jumping to a different stack. Make sure
+ * jmp_stackp is between stack_begin and stack end, to correctly detect
+ * this condition regardless of whether the stack grows up or down.
+ */
+ if (((jmp_stackp < stack_begin) && (jmp_stackp < stack_end)) ||
+ ((jmp_stackp > stack_begin) && (jmp_stackp > stack_end)))
+ PANIC("longjmp()ing between thread contexts is undefined by "
+ "POSIX 1003.1");
+
+ memcpy(_thread_run->nested_jmp.sigjmp, env,
+ sizeof(_thread_run->nested_jmp.sigjmp));
+
+ /*
+ * Only save oldstate once so that dispatching multiple signals will not
+ * lose the thread's original state.
+ */
+ if (_thread_run->jmpflags == JMPFLAGS_NONE)
+ _thread_run->oldstate = _thread_run->state;
+ PTHREAD_SET_STATE(_thread_run, PS_RUNNING);
+ _thread_run->jmpflags = JMPFLAGS_SIGLONGJMP;
+ _thread_run->longjmp_val = savemask;
+ ___longjmp(*_thread_run->sighandler_jmp_buf, 1);
+}
+
+__weak_reference(_libc_siglongjmp, siglongjmp);
+
+void
+_libc_longjmp(jmp_buf env, int val)
+{
+ void *jmp_stackp;
+ void *stack_begin, *stack_end;
+
+ if (_thread_run->signal_nest_level == 0)
+ __longjmp(env, val);
+
+ /* Get the stack pointer from the jump buffer. */
+ jmp_stackp = (void *)env->_jb[JMP_BUF_SP_OFFSET];
+
+ /* Get the bounds of the current threads stack. */
+ if (_thread_run->stack != NULL) {
+ stack_begin = _thread_run->stack;
+ stack_end = stack_begin + _thread_run->attr.stacksize_attr;
+ } else {
+ stack_end = (void *)USRSTACK;
+ stack_begin = stack_end - PTHREAD_STACK_INITIAL;
+ }
+
+ /*
+ * Make sure we aren't jumping to a different stack. Make sure
+ * jmp_stackp is between stack_begin and stack end, to correctly detect
+ * this condition regardless of whether the stack grows up or down.
+ */
+ if (((jmp_stackp < stack_begin) && (jmp_stackp < stack_end)) ||
+ ((jmp_stackp > stack_begin) && (jmp_stackp > stack_end)))
+ PANIC("longjmp()ing between thread contexts is undefined by "
+ "POSIX 1003.1");
+
+ memcpy(_thread_run->nested_jmp.jmp, env,
+ sizeof(_thread_run->nested_jmp.jmp));
+
+ /*
+ * Only save oldstate once so that dispatching multiple signals will not
+ * lose the thread's original state.
+ */
+ if (_thread_run->jmpflags == JMPFLAGS_NONE)
+ _thread_run->oldstate = _thread_run->state;
+ PTHREAD_SET_STATE(_thread_run, PS_RUNNING);
+ _thread_run->jmpflags = JMPFLAGS_LONGJMP;
+ _thread_run->longjmp_val = val;
+ ___longjmp(*_thread_run->sighandler_jmp_buf, 1);
+}
+
+__weak_reference(_libc_longjmp, longjmp);
+
+void
+_libc__longjmp(jmp_buf env, int val)
+{
+ void *jmp_stackp;
+ void *stack_begin, *stack_end;
+
+ if (_thread_run->signal_nest_level == 0)
+ ___longjmp(env, val);
+
+ /* Get the stack pointer from the jump buffer. */
+ jmp_stackp = (void *)env->_jb[JMP_BUF_SP_OFFSET];
+
+ /* Get the bounds of the current threads stack. */
+ if (_thread_run->stack != NULL) {
+ stack_begin = _thread_run->stack;
+ stack_end = stack_begin + _thread_run->attr.stacksize_attr;
+ } else {
+ stack_end = (void *)USRSTACK;
+ stack_begin = stack_end - PTHREAD_STACK_INITIAL;
+ }
+
+ /*
+ * Make sure we aren't jumping to a different stack. Make sure
+ * jmp_stackp is between stack_begin and stack end, to correctly detect
+ * this condition regardless of whether the stack grows up or down.
+ */
+ if (((jmp_stackp < stack_begin) && (jmp_stackp < stack_end)) ||
+ ((jmp_stackp > stack_begin) && (jmp_stackp > stack_end)))
+ PANIC("longjmp()ing between thread contexts is undefined by "
+ "POSIX 1003.1");
+
+ memcpy(_thread_run->nested_jmp.jmp, env,
+ sizeof(_thread_run->nested_jmp.jmp));
+
+ /*
+ * Only save oldstate once so that dispatching multiple signals will not
+ * lose the thread's original state.
+ */
+ if (_thread_run->jmpflags == JMPFLAGS_NONE)
+ _thread_run->oldstate = _thread_run->state;
+ PTHREAD_SET_STATE(_thread_run, PS_RUNNING);
+ _thread_run->jmpflags = JMPFLAGS__LONGJMP;
+ _thread_run->longjmp_val = val;
+ ___longjmp(*_thread_run->sighandler_jmp_buf, 1);
+}
+
+__weak_reference(_libc__longjmp, _longjmp);
+#endif
diff --git a/lib/libc_r/uthread/uthread_join.c b/lib/libc_r/uthread/uthread_join.c
index 155dc64..1cffc96 100644
--- a/lib/libc_r/uthread/uthread_join.c
+++ b/lib/libc_r/uthread/uthread_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/libc_r/uthread/uthread_kern.c b/lib/libc_r/uthread/uthread_kern.c
index b3fbc3a..b833061 100644
--- a/lib/libc_r/uthread/uthread_kern.c
+++ b/lib/libc_r/uthread/uthread_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/libc_r/uthread/uthread_mutex.c b/lib/libc_r/uthread/uthread_mutex.c
index 6526a35..6d75ea5 100644
--- a/lib/libc_r/uthread/uthread_mutex.c
+++ b/lib/libc_r/uthread/uthread_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/libc_r/uthread/uthread_priority_queue.c b/lib/libc_r/uthread/uthread_priority_queue.c
index 6fdf844..1b9fcba 100644
--- a/lib/libc_r/uthread/uthread_priority_queue.c
+++ b/lib/libc_r/uthread/uthread_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/libc_r/uthread/uthread_setprio.c b/lib/libc_r/uthread/uthread_setprio.c
index 5f7b44a..c630db5 100644
--- a/lib/libc_r/uthread/uthread_setprio.c
+++ b/lib/libc_r/uthread/uthread_setprio.c
@@ -31,7 +31,6 @@
*
* $FreeBSD$
*/
-#include <errno.h>
#ifdef _THREAD_SAFE
#include <pthread.h>
#include "pthread_private.h"
diff --git a/lib/libc_r/uthread/uthread_sig.c b/lib/libc_r/uthread/uthread_sig.c
index 160bdab..0f18477 100644
--- a/lib/libc_r/uthread/uthread_sig.c
+++ b/lib/libc_r/uthread/uthread_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--;
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--;
diff --git a/lib/libpthread/thread/Makefile.inc b/lib/libpthread/thread/Makefile.inc
index 4697305..f46f007 100644
--- a/lib/libpthread/thread/Makefile.inc
+++ b/lib/libpthread/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/libpthread/thread/thr_cancel.c b/lib/libpthread/thread/thr_cancel.c
index de7c491..f22bfb5 100644
--- a/lib/libpthread/thread/thr_cancel.c
+++ b/lib/libpthread/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/libpthread/thread/thr_cond.c b/lib/libpthread/thread/thr_cond.c
index 3e215af..ced48e3 100644
--- a/lib/libpthread/thread/thr_cond.c
+++ b/lib/libpthread/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/libpthread/thread/thr_create.c b/lib/libpthread/thread/thr_create.c
index fd9e746..b8a1c46 100644
--- a/lib/libpthread/thread/thr_create.c
+++ b/lib/libpthread/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/libpthread/thread/thr_init.c b/lib/libpthread/thread/thr_init.c
index b6429bb..dd5f53f 100644
--- a/lib/libpthread/thread/thr_init.c
+++ b/lib/libpthread/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/libpthread/thread/thr_join.c b/lib/libpthread/thread/thr_join.c
index 155dc64..1cffc96 100644
--- a/lib/libpthread/thread/thr_join.c
+++ b/lib/libpthread/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/libpthread/thread/thr_kern.c b/lib/libpthread/thread/thr_kern.c
index b3fbc3a..b833061 100644
--- a/lib/libpthread/thread/thr_kern.c
+++ b/lib/libpthread/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/libpthread/thread/thr_mutex.c b/lib/libpthread/thread/thr_mutex.c
index 6526a35..6d75ea5 100644
--- a/lib/libpthread/thread/thr_mutex.c
+++ b/lib/libpthread/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/libpthread/thread/thr_priority_queue.c b/lib/libpthread/thread/thr_priority_queue.c
index 6fdf844..1b9fcba 100644
--- a/lib/libpthread/thread/thr_priority_queue.c
+++ b/lib/libpthread/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/libpthread/thread/thr_private.h b/lib/libpthread/thread/thr_private.h
index 8a126f4..8bc2d3f 100644
--- a/lib/libpthread/thread/thr_private.h
+++ b/lib/libpthread/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/libpthread/thread/thr_setprio.c b/lib/libpthread/thread/thr_setprio.c
index 5f7b44a..c630db5 100644
--- a/lib/libpthread/thread/thr_setprio.c
+++ b/lib/libpthread/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/libpthread/thread/thr_sig.c b/lib/libpthread/thread/thr_sig.c
index 160bdab..0f18477 100644
--- a/lib/libpthread/thread/thr_sig.c
+++ b/lib/libpthread/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