summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libc_r/uthread/pthread_private.h69
-rw-r--r--lib/libc_r/uthread/uthread_cond.c145
-rw-r--r--lib/libc_r/uthread/uthread_create.c45
-rw-r--r--lib/libc_r/uthread/uthread_detach.c7
-rw-r--r--lib/libc_r/uthread/uthread_exit.c35
-rw-r--r--lib/libc_r/uthread/uthread_info.c225
-rw-r--r--lib/libc_r/uthread/uthread_init.c54
-rw-r--r--lib/libc_r/uthread/uthread_jmp.c157
-rw-r--r--lib/libc_r/uthread/uthread_join.c104
-rw-r--r--lib/libc_r/uthread/uthread_kern.c87
-rw-r--r--lib/libc_r/uthread/uthread_mutex.c35
-rw-r--r--lib/libc_r/uthread/uthread_sig.c443
-rw-r--r--lib/libc_r/uthread/uthread_sigaction.c2
-rw-r--r--lib/libc_r/uthread/uthread_sigmask.c3
-rw-r--r--lib/libkse/thread/thr_cond.c145
-rw-r--r--lib/libkse/thread/thr_create.c45
-rw-r--r--lib/libkse/thread/thr_detach.c7
-rw-r--r--lib/libkse/thread/thr_exit.c35
-rw-r--r--lib/libkse/thread/thr_info.c225
-rw-r--r--lib/libkse/thread/thr_init.c54
-rw-r--r--lib/libkse/thread/thr_join.c104
-rw-r--r--lib/libkse/thread/thr_kern.c87
-rw-r--r--lib/libkse/thread/thr_mutex.c35
-rw-r--r--lib/libkse/thread/thr_private.h69
-rw-r--r--lib/libkse/thread/thr_sig.c443
-rw-r--r--lib/libkse/thread/thr_sigaction.c2
-rw-r--r--lib/libkse/thread/thr_sigmask.c3
-rw-r--r--lib/libpthread/thread/thr_cond.c145
-rw-r--r--lib/libpthread/thread/thr_create.c45
-rw-r--r--lib/libpthread/thread/thr_detach.c7
-rw-r--r--lib/libpthread/thread/thr_exit.c35
-rw-r--r--lib/libpthread/thread/thr_info.c225
-rw-r--r--lib/libpthread/thread/thr_init.c54
-rw-r--r--lib/libpthread/thread/thr_join.c104
-rw-r--r--lib/libpthread/thread/thr_kern.c87
-rw-r--r--lib/libpthread/thread/thr_mutex.c35
-rw-r--r--lib/libpthread/thread/thr_private.h69
-rw-r--r--lib/libpthread/thread/thr_sig.c443
-rw-r--r--lib/libpthread/thread/thr_sigaction.c2
-rw-r--r--lib/libpthread/thread/thr_sigmask.c3
40 files changed, 1815 insertions, 2104 deletions
diff --git a/lib/libc_r/uthread/pthread_private.h b/lib/libc_r/uthread/pthread_private.h
index 5076510..e8fff12 100644
--- a/lib/libc_r/uthread/pthread_private.h
+++ b/lib/libc_r/uthread/pthread_private.h
@@ -327,6 +327,7 @@ struct pthread_cond {
pthread_mutex_t c_mutex;
void *c_data;
long c_flags;
+ int c_seqno;
/*
* Lock for accesses to this structure.
@@ -351,7 +352,7 @@ struct pthread_cond_attr {
*/
#define PTHREAD_COND_STATIC_INITIALIZER \
{ COND_TYPE_FAST, TAILQ_INITIALIZER, NULL, NULL, \
- 0, _SPINLOCK_INITIALIZER }
+ 0, 0, _SPINLOCK_INITIALIZER }
/*
* Semaphore definitions.
@@ -424,6 +425,9 @@ enum pthread_susp {
*/
#define PTHREAD_STACK_INITIAL 0x100000
+/* Size of the scheduler stack: */
+#define SCHED_STACK_SIZE PAGE_SIZE
+
/*
* Define the different priority ranges. All applications have thread
* priorities constrained within 0-31. The threads library raises the
@@ -574,13 +578,20 @@ union pthread_wait_data {
*/
typedef void (*thread_continuation_t) (void *);
+struct pthread_signal_frame;
+
struct pthread_state_data {
- int psd_interrupted;
+ struct pthread_signal_frame *psd_curframe;
sigset_t psd_sigmask;
- enum pthread_state psd_state;
- int psd_flags;
struct timespec psd_wakeup_time;
union pthread_wait_data psd_wait_data;
+ enum pthread_state psd_state;
+ int psd_flags;
+ int psd_interrupted;
+ int psd_longjmp_val;
+ int psd_sigmask_seqno;
+ int psd_signo;
+ int psd_sig_defer_count;
/* XXX - What about thread->timeout and/or thread->error? */
};
@@ -620,9 +631,6 @@ struct pthread_signal_frame {
*/
struct pthread_state_data saved_state;
- /* Beginning (bottom) of threads stack frame for this signal. */
- unsigned long stackp;
-
/*
* Threads return context; ctxtype identifies the type of context.
* For signal frame 0, these point to the context storage area
@@ -637,18 +645,10 @@ struct pthread_signal_frame {
} ctx;
thread_context_t ctxtype;
int longjmp_val;
-
- /* Threads "jump out of signal handler" destination frame. */
- int dst_frame;
-
- /*
- * Used to return back to the signal handling frame in case
- * the application tries to change contexts from the handler.
- */
- jmp_buf *sig_jb;
-
int signo; /* signal, arg 1 to sighandler */
int sig_has_args; /* use signal args if true */
+ ucontext_t uc;
+ siginfo_t siginfo;
};
/*
@@ -685,18 +685,20 @@ struct pthread {
struct pthread_attr attr;
/*
- * Used for tracking delivery of nested signal handlers.
- * Signal frame 0 is used for normal context (when no
- * signal handlers are active for the thread). Frame
- * 1 is used as the context for the first signal, and
- * frames 2 .. NSIG-1 are used when additional signals
- * arrive interrupting already active signal handlers.
+ * Threads return context; ctxtype identifies the type of context.
+ */
+ union {
+ jmp_buf jb;
+ sigjmp_buf sigjb;
+ ucontext_t uc;
+ } ctx;
+ thread_context_t ctxtype;
+ int longjmp_val;
+
+ /*
+ * Used for tracking delivery of signal handlers.
*/
- struct pthread_signal_frame *sigframes[NSIG];
- struct pthread_signal_frame sigframe0;
struct pthread_signal_frame *curframe;
- int sigframe_count;
- int sigframe_done;
/*
* Cancelability flags - the lower 2 bits are used by cancel
@@ -716,6 +718,7 @@ struct pthread {
*/
sigset_t sigmask;
sigset_t sigpend;
+ int sigmask_seqno;
int check_pending;
/* Thread state: */
@@ -1078,7 +1081,11 @@ SCLASS int _thread_dfl_count[NSIG];
* Pending signals and mask for this process:
*/
SCLASS sigset_t _process_sigpending;
-SCLASS sigset_t _process_sigmask;
+SCLASS sigset_t _process_sigmask
+#ifdef GLOBAL_PTHREAD_PRIVATE
+= { {0, 0, 0, 0} }
+#endif
+;
/*
* Scheduling queues:
@@ -1222,7 +1229,6 @@ void _waitq_clearactive(void);
#endif
void _thread_exit(char *, int, char *);
void _thread_exit_cleanup(void);
-void _thread_exit_finish(void);
void _thread_fd_unlock(int, int);
void _thread_fd_unlock_debug(int, int, char *, int);
void _thread_fd_unlock_owned(pthread_t);
@@ -1232,7 +1238,7 @@ void _thread_dump_info(void);
void _thread_init(void);
void _thread_kern_sched(ucontext_t *);
void _thread_kern_scheduler(void);
-void _thread_kern_sched_frame(int frame);
+void _thread_kern_sched_frame(struct pthread_signal_frame *psf);
void _thread_kern_sched_sig(void);
void _thread_kern_sched_state(enum pthread_state, char *fname, int lineno);
void _thread_kern_sched_state_unlock(enum pthread_state state,
@@ -1245,7 +1251,7 @@ void _thread_sig_check_pending(pthread_t pthread);
void _thread_sig_handle_pending(void);
void _thread_sig_send(pthread_t pthread, int sig);
void _thread_sig_wrapper(void);
-int _thread_sigframe_find(pthread_t pthread, void *stackp);
+void _thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf);
void _thread_start(void);
void _thread_seterrno(pthread_t, int);
int _thread_fd_table_init(int fd);
@@ -1262,6 +1268,7 @@ int _thread_sys_sigsuspend(const sigset_t *);
int _thread_sys_siginterrupt(int, int);
int _thread_sys_sigpause(int);
int _thread_sys_sigreturn(ucontext_t *);
+int _thread_sys_sigaltstack(const struct sigaltstack *, struct sigstack *);
int _thread_sys_sigstack(const struct sigstack *, struct sigstack *);
int _thread_sys_sigvec(int, struct sigvec *, struct sigvec *);
void _thread_sys_psignal(unsigned int, const char *);
diff --git a/lib/libc_r/uthread/uthread_cond.c b/lib/libc_r/uthread/uthread_cond.c
index 50cf927..5ff0967 100644
--- a/lib/libc_r/uthread/uthread_cond.c
+++ b/lib/libc_r/uthread/uthread_cond.c
@@ -47,7 +47,7 @@ static inline void cond_queue_enq(pthread_cond_t, pthread_t);
/* Reinitialize a condition variable to defaults. */
int
-_cond_reinit(pthread_cond_t * cond)
+_cond_reinit(pthread_cond_t *cond)
{
int ret = 0;
@@ -63,13 +63,14 @@ _cond_reinit(pthread_cond_t * cond)
(*cond)->c_flags = COND_FLAGS_INITED;
(*cond)->c_type = COND_TYPE_FAST;
(*cond)->c_mutex = NULL;
+ (*cond)->c_seqno = 0;
memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
}
return (ret);
}
int
-pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
+pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
{
enum pthread_cond_type type;
pthread_cond_t pcond;
@@ -118,6 +119,7 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
pcond->c_flags |= COND_FLAGS_INITED;
pcond->c_type = type;
pcond->c_mutex = NULL;
+ pcond->c_seqno = 0;
memset(&pcond->lock,0,sizeof(pcond->lock));
*cond = pcond;
}
@@ -128,7 +130,7 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
}
int
-pthread_cond_destroy(pthread_cond_t * cond)
+pthread_cond_destroy(pthread_cond_t *cond)
{
int rval = 0;
@@ -155,22 +157,37 @@ pthread_cond_destroy(pthread_cond_t * cond)
}
int
-pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
+pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
int rval = 0;
+ int done = 0;
int interrupted = 0;
+ int unlock_mutex = 1;
+ int seqno;
_thread_enter_cancellation_point();
if (cond == NULL)
- rval = EINVAL;
+ return (EINVAL);
/*
* If the condition variable is statically initialized,
* perform the dynamic initialization:
*/
- else if (*cond != NULL ||
- (rval = pthread_cond_init(cond, NULL)) == 0) {
+ if (*cond == NULL &&
+ (rval = pthread_cond_init(cond, NULL)) != 0)
+ return (rval);
+
+ /*
+ * Enter a loop waiting for a condition signal or broadcast
+ * to wake up this thread. A loop is needed in case the waiting
+ * thread is interrupted by a signal to execute a signal handler.
+ * It is not (currently) possible to remain in the waiting queue
+ * while running a handler. Instead, the thread is interrupted
+ * and backed out of the waiting queue prior to executing the
+ * signal handler.
+ */
+ do {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@@ -205,14 +222,16 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
*/
cond_queue_enq(*cond, _thread_run);
- /* Remember the mutex that is being used: */
+ /* Remember the mutex and sequence number: */
(*cond)->c_mutex = *mutex;
+ seqno = (*cond)->c_seqno;
/* Wait forever: */
_thread_run->wakeup_time.tv_sec = -1;
/* Unlock the mutex: */
- if ((rval = _mutex_cv_unlock(mutex)) != 0) {
+ if ((unlock_mutex != 0) &&
+ ((rval = _mutex_cv_unlock(mutex)) != 0)) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
@@ -230,19 +249,23 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
}
else {
/*
+ * Don't unlock the mutex in the event
+ * this thread has to be requeued in
+ * condition variable queue:
+ */
+ unlock_mutex = 0;
+
+ /*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
- if (_thread_run->interrupted != 0) {
- /*
- * Remember that this thread
- * was interrupted:
- */
- interrupted = 1;
+ done = (seqno != (*cond)->c_seqno);
+ if ((_thread_run->flags &
+ PTHREAD_FLAGS_IN_CONDQ) != 0) {
/*
* Lock the condition variable
* while removing the thread.
@@ -260,6 +283,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
}
/*
+ * Save the interrupted flag; locking
+ * the mutex will destroy it.
+ */
+ interrupted = _thread_run->interrupted;
+
+ /*
* Note that even though this thread may have
* been canceled, POSIX requires that the mutex
* be reaquired prior to cancellation.
@@ -279,11 +308,9 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
break;
}
- if (interrupted != 0) {
- if (_thread_run->continuation != NULL)
- _thread_run->continuation((void *) _thread_run);
- }
- }
+ if ((interrupted != 0) && (_thread_run->continuation != NULL))
+ _thread_run->continuation((void *) _thread_run);
+ } while ((done == 0) && (rval == 0));
_thread_leave_cancellation_point();
@@ -296,18 +323,33 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
const struct timespec * abstime)
{
int rval = 0;
+ int done = 0;
int interrupted = 0;
+ int unlock_mutex = 1;
+ int seqno;
_thread_enter_cancellation_point();
if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
abstime->tv_nsec >= 1000000000)
- rval = EINVAL;
+ return (EINVAL);
/*
* If the condition variable is statically initialized, perform dynamic
* initialization.
*/
- else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
+ if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
+ return (rval);
+
+ /*
+ * Enter a loop waiting for a condition signal or broadcast
+ * to wake up this thread. A loop is needed in case the waiting
+ * thread is interrupted by a signal to execute a signal handler.
+ * It is not (currently) possible to remain in the waiting queue
+ * while running a handler. Instead, the thread is interrupted
+ * and backed out of the waiting queue prior to executing the
+ * signal handler.
+ */
+ do {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@@ -348,11 +390,13 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
*/
cond_queue_enq(*cond, _thread_run);
- /* Remember the mutex that is being used: */
+ /* Remember the mutex and sequence number: */
(*cond)->c_mutex = *mutex;
+ seqno = (*cond)->c_seqno;
/* Unlock the mutex: */
- if ((rval = _mutex_cv_unlock(mutex)) != 0) {
+ if ((unlock_mutex != 0) &&
+ ((rval = _mutex_cv_unlock(mutex)) != 0)) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
@@ -368,35 +412,39 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
_SPINUNLOCK(&(*cond)->lock);
} else {
/*
+ * Don't unlock the mutex in the event
+ * this thread has to be requeued in
+ * condition variable queue:
+ */
+ unlock_mutex = 0;
+
+ /*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
+ done = (seqno != (*cond)->c_seqno);
+
/*
- * Check if the wait timedout or was
- * interrupted (canceled):
+ * Check if the wait timedout, was
+ * interrupted (canceled), or needs to
+ * be resumed after handling a signal.
*/
if ((_thread_run->timeout == 0) &&
- (_thread_run->interrupted == 0)) {
+ (_thread_run->interrupted == 0) &&
+ (done != 0)) {
/* Lock the mutex: */
rval = _mutex_cv_lock(mutex);
-
} else {
- /*
- * Remember if this thread was
- * interrupted:
- */
- interrupted = _thread_run->interrupted;
-
- /* Lock the condition variable structure: */
+ /* Lock the CV structure: */
_SPINLOCK(&(*cond)->lock);
/*
* The wait timed out; remove
* the thread from the condition
- * variable queue:
+ * variable queue:
*/
cond_queue_remove(*cond,
_thread_run);
@@ -405,11 +453,18 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
- /* Unock the condition variable structure: */
+ /* Unock the CV structure: */
_SPINUNLOCK(&(*cond)->lock);
/* Return a timeout error: */
- rval = ETIMEDOUT;
+ if (_thread_run->timeout != 0)
+ rval = ETIMEDOUT;
+ /*
+ * Save the interrupted flag;
+ * locking the mutex will
+ * destroy it.
+ */
+ interrupted = _thread_run->interrupted;
/*
* Lock the mutex and ignore any
@@ -435,11 +490,9 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
break;
}
- if (interrupted != 0) {
- if (_thread_run->continuation != NULL)
- _thread_run->continuation((void *) _thread_run);
- }
- }
+ if ((interrupted != 0) && (_thread_run->continuation != NULL))
+ _thread_run->continuation((void *) _thread_run);
+ } while ((done == 0) && (rval == 0));
_thread_leave_cancellation_point();
@@ -473,6 +526,9 @@ pthread_cond_signal(pthread_cond_t * cond)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
+ /* Increment the sequence number: */
+ (*cond)->c_seqno++;
+
if ((pthread = cond_queue_deq(*cond)) != NULL) {
/*
* Unless the thread is currently suspended,
@@ -538,6 +594,9 @@ pthread_cond_broadcast(pthread_cond_t * cond)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
+ /* Increment the sequence number: */
+ (*cond)->c_seqno++;
+
/*
* Enter a loop to bring all threads off the
* condition queue:
diff --git a/lib/libc_r/uthread/uthread_create.c b/lib/libc_r/uthread/uthread_create.c
index 0390f1b..430869e 100644
--- a/lib/libc_r/uthread/uthread_create.c
+++ b/lib/libc_r/uthread/uthread_create.c
@@ -49,16 +49,13 @@
static u_int64_t next_uniqueid = 1;
#define OFF(f) offsetof(struct pthread, f)
-#define SIGFRAME_OFF(f) offsetof(struct pthread_signal_frame, f)
int _thread_next_offset = OFF(tle.tqe_next);
int _thread_uniqueid_offset = OFF(uniqueid);
int _thread_state_offset = OFF(state);
int _thread_name_offset = OFF(name);
-int _thread_curframe_offset = OFF(curframe);
-int _thread_sigframe_ctx_offset = SIGFRAME_OFF(ctx);
-int _thread_sigframe_ctxtype_offset = SIGFRAME_OFF(ctxtype);
+int _thread_ctxtype_offset = OFF(ctxtype);
+int _thread_ctx_offset = OFF(ctx);
#undef OFF
-#undef SIGFRAME_OFF
int _thread_PS_RUNNING_value = PS_RUNNING;
int _thread_PS_DEAD_value = PS_DEAD;
@@ -66,12 +63,12 @@ int _thread_CTX_JB_NOSIG_value = CTX_JB_NOSIG;
int _thread_CTX_JB_value = CTX_JB;
int _thread_CTX_SJB_value = CTX_SJB;
int _thread_CTX_UC_value = CTX_UC;
-int _thread_sigframe_size_value = sizeof(struct pthread_signal_frame);
int
pthread_create(pthread_t * thread, const pthread_attr_t * attr,
void *(*start_routine) (void *), void *arg)
{
+ struct itimerval itimer;
int f_gc = 0;
int ret = 0;
pthread_t gc_thread;
@@ -127,7 +124,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
} else {
/* Allocate a new stack. */
stack = _next_stack + PTHREAD_STACK_GUARD;
-
+
/*
* Even if stack allocation fails, we don't want
* to try to use this location again, so
@@ -184,40 +181,35 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Initialise the thread for signals: */
new_thread->sigmask = _thread_run->sigmask;
+ new_thread->sigmask_seqno = 0;
- /* Initialize the first signal frame: */
- new_thread->sigframes[0] = &new_thread->sigframe0;
- new_thread->curframe = &new_thread->sigframe0;
+ /* Initialize the signal frame: */
+ new_thread->curframe = NULL;
/* Initialise the jump buffer: */
- _setjmp(new_thread->curframe->ctx.jb);
+ _setjmp(new_thread->ctx.jb);
/*
* Set up new stack frame so that it looks like it
* returned from a longjmp() to the beginning of
* _thread_start().
*/
- SET_RETURN_ADDR_JB(new_thread->curframe->ctx.jb,
- _thread_start);
+ SET_RETURN_ADDR_JB(new_thread->ctx.jb, _thread_start);
/* The stack starts high and builds down: */
- SET_STACK_JB(new_thread->curframe->ctx.jb,
+ SET_STACK_JB(new_thread->ctx.jb,
(long)new_thread->stack + pattr->stacksize_attr
- sizeof(double));
/* Initialize the rest of the frame: */
- new_thread->curframe->ctxtype = CTX_JB_NOSIG;
- /* Set the base of the stack: */
- new_thread->curframe->stackp =
- GET_STACK_JB(new_thread->curframe->ctx.jb);
- new_thread->sigframe_count = 0;
+ new_thread->ctxtype = CTX_JB_NOSIG;
/* Copy the thread attributes: */
memcpy(&new_thread->attr, pattr, sizeof(struct pthread_attr));
/*
* Check if this thread is to inherit the scheduling
- * attributes from its parent:
+ * attributes from its parent:
*/
if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
/* Copy the scheduling attributes: */
@@ -233,7 +225,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/*
* Use just the thread priority, leaving the
* other scheduling attributes as their
- * default values:
+ * default values:
*/
new_thread->base_priority =
new_thread->attr.prio;
@@ -292,8 +284,19 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Return a pointer to the thread structure: */
(*thread) = new_thread;
+ if (f_gc != 0) {
+ /* Install the scheduling timer: */
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = _clock_res_usec;
+ itimer.it_value = itimer.it_interval;
+ if (setitimer(_ITIMER_SCHED_TIMER, &itimer,
+ NULL) != 0)
+ PANIC("Cannot set interval timer");
+ }
+
/* Schedule the new user thread: */
_thread_kern_sched(NULL);
+
/*
* Start a garbage collector thread
* if necessary.
diff --git a/lib/libc_r/uthread/uthread_detach.c b/lib/libc_r/uthread/uthread_detach.c
index 3bade9d..6dd762a 100644
--- a/lib/libc_r/uthread/uthread_detach.c
+++ b/lib/libc_r/uthread/uthread_detach.c
@@ -65,7 +65,12 @@ pthread_detach(pthread_t pthread)
pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
/* Make the thread runnable: */
- PTHREAD_NEW_STATE(next_thread,PS_RUNNING);
+ PTHREAD_NEW_STATE(next_thread, PS_RUNNING);
+
+ /*
+ * Set the return value for the woken thread:
+ */
+ next_thread->error = ESRCH;
}
/*
diff --git a/lib/libc_r/uthread/uthread_exit.c b/lib/libc_r/uthread/uthread_exit.c
index 7fbeb65..aef12fe 100644
--- a/lib/libc_r/uthread/uthread_exit.c
+++ b/lib/libc_r/uthread/uthread_exit.c
@@ -141,7 +141,7 @@ _thread_exit_cleanup(void)
void
pthread_exit(void *status)
{
- int frame;
+ pthread_t pthread;
/* Check if this thread is already in the process of exiting: */
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
@@ -159,7 +159,6 @@ pthread_exit(void *status)
while (_thread_run->cleanup != NULL) {
pthread_cleanup_pop(1);
}
-
if (_thread_run->attr.cleanup_attr != NULL) {
_thread_run->attr.cleanup_attr(_thread_run->attr.arg_attr);
}
@@ -175,25 +174,6 @@ pthread_exit(void *status)
_thread_run->poll_data.fds = NULL;
}
- if ((frame = _thread_run->sigframe_count) == 0)
- _thread_exit_finish();
- else {
- /*
- * Jump back and unwind the signal frames to gracefully
- * cleanup.
- */
- ___longjmp(*_thread_run->sigframes[frame]->sig_jb, 1);
- }
-
- /* This point should not be reached. */
- PANIC("Dead thread has resumed");
-}
-
-void
-_thread_exit_finish(void)
-{
- pthread_t pthread;
-
/*
* Lock the garbage collector mutex to ensure that the garbage
* collector is not using the dead thread list.
@@ -233,6 +213,16 @@ _thread_exit_finish(void)
* detach this thread:
*/
PTHREAD_NEW_STATE(pthread, PS_RUNNING);
+
+ /*
+ * Set the return value for the woken thread:
+ */
+ if ((_thread_run->attr.flags & PTHREAD_DETACHED) != 0)
+ pthread->error = ESRCH;
+ else {
+ pthread->ret = _thread_run->ret;
+ pthread->error = 0;
+ }
}
/* Remove this thread from the thread list: */
@@ -240,5 +230,8 @@ _thread_exit_finish(void)
/* This thread will never be re-scheduled. */
_thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__);
+
+ /* This point should not be reached. */
+ PANIC("Dead thread has resumed");
}
#endif
diff --git a/lib/libc_r/uthread/uthread_info.c b/lib/libc_r/uthread/uthread_info.c
index ca91512..956ae7c 100644
--- a/lib/libc_r/uthread/uthread_info.c
+++ b/lib/libc_r/uthread/uthread_info.c
@@ -41,6 +41,13 @@
#include <errno.h>
#include "pthread_private.h"
+#ifndef NELEMENTS
+#define NELEMENTS(arr) (sizeof(arr) / sizeof(arr[0]))
+#endif
+
+static void dump_thread(int fd, pthread_t pthread, int long_version);
+
+
struct s_thread_info {
enum pthread_state state;
char *name;
@@ -77,12 +84,11 @@ _thread_dump_info(void)
char s[512];
int fd;
int i;
- int j;
pthread_t pthread;
char tmpfile[128];
pq_list_t *pq_list;
- for (i = 0; i < 100000; i++) {
+ for (i = 0; i < 100000; i++) {
snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i",
getpid(), i);
/* Open the dump file for append and create it if necessary: */
@@ -112,70 +118,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the global list: */
TAILQ_FOREACH(pthread, &_thread_list, tle) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
-
- /* Check if this is the running thread: */
- if (pthread == _thread_run) {
- /* Output a record for the running thread: */
- strcpy(s, "This is the running thread\n");
- _thread_sys_write(fd, s, strlen(s));
- }
- /* Check if this is the initial thread: */
- if (pthread == _thread_initial) {
- /* Output a record for the initial thread: */
- strcpy(s, "This is the initial thread\n");
- _thread_sys_write(fd, s, strlen(s));
- }
- /* Process according to thread state: */
- switch (pthread->state) {
- /* File descriptor read lock wait: */
- case PS_FDLR_WAIT:
- case PS_FDLW_WAIT:
- case PS_FDR_WAIT:
- case PS_FDW_WAIT:
- /* Write the lock details: */
- snprintf(s, sizeof(s), "fd %d[%s:%d]",
- pthread->data.fd.fd,
- pthread->data.fd.fname,
- pthread->data.fd.branch);
- _thread_sys_write(fd, s, strlen(s));
- snprintf(s, sizeof(s), "owner %pr/%pw\n",
- _thread_fd_table[pthread->data.fd.fd]->r_owner,
- _thread_fd_table[pthread->data.fd.fd]->w_owner);
- _thread_sys_write(fd, s, strlen(s));
- break;
- case PS_SIGWAIT:
- snprintf(s, sizeof(s), "sigmask (hi)");
- _thread_sys_write(fd, s, strlen(s));
- for (i = _SIG_WORDS - 1; i >= 0; i--) {
- snprintf(s, sizeof(s), "%08x\n",
- pthread->sigmask.__bits[i]);
- _thread_sys_write(fd, s, strlen(s));
- }
- snprintf(s, sizeof(s), "(lo)\n");
- _thread_sys_write(fd, s, strlen(s));
- break;
-
- /*
- * Trap other states that are not explicitly
- * coded to dump information:
- */
- default:
- /* Nothing to do here. */
- break;
- }
+ dump_thread(fd, pthread, /*long_verson*/ 1);
}
/* Output a header for ready threads: */
@@ -185,19 +128,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the ready queue: */
TAILQ_FOREACH (pq_list, &_readyq.pq_queue, pl_link) {
TAILQ_FOREACH(pthread, &pq_list->pl_head, pqe) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
}
@@ -207,19 +138,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the waiting queue: */
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
/* Output a header for threads in the work queue: */
@@ -228,19 +147,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the waiting queue: */
TAILQ_FOREACH (pthread, &_workq, qe) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
/* Check if there are no dead threads: */
@@ -255,42 +162,38 @@ _thread_dump_info(void)
/*
* Enter a loop to report each thread in the global
- * dead thread list:
+ * dead thread list:
*/
TAILQ_FOREACH(pthread, &_dead_list, dle) {
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "Thread %p prio %3d [%s:%d]\n",
- pthread, pthread->base_priority,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
}
/* Output a header for file descriptors: */
- snprintf(s, sizeof(s), "\n\n=============\nFILE DESCRIPTOR TABLE (table size %d)\n\n",_thread_dtablesize);
+ snprintf(s, sizeof(s), "\n\n=============\nFILE DESCRIPTOR "
+ "TABLE (table size %d)\n\n", _thread_dtablesize);
_thread_sys_write(fd, s, strlen(s));
/* Enter a loop to report file descriptor lock usage: */
for (i = 0; i < _thread_dtablesize; i++) {
/*
* Check if memory is allocated for this file
- * descriptor:
+ * descriptor:
*/
if (_thread_fd_table[i] != NULL) {
/* Report the file descriptor lock status: */
snprintf(s, sizeof(s),
- "fd[%3d] read owner %p count %d [%s:%d]\n write owner %p count %d [%s:%d]\n",
- i,
- _thread_fd_table[i]->r_owner,
- _thread_fd_table[i]->r_lockcount,
- _thread_fd_table[i]->r_fname,
- _thread_fd_table[i]->r_lineno,
- _thread_fd_table[i]->w_owner,
- _thread_fd_table[i]->w_lockcount,
- _thread_fd_table[i]->w_fname,
- _thread_fd_table[i]->w_lineno);
- _thread_sys_write(fd, s, strlen(s));
+ "fd[%3d] read owner %p count %d [%s:%d]\n"
+ " write owner %p count %d [%s:%d]\n",
+ i, _thread_fd_table[i]->r_owner,
+ _thread_fd_table[i]->r_lockcount,
+ _thread_fd_table[i]->r_fname,
+ _thread_fd_table[i]->r_lineno,
+ _thread_fd_table[i]->w_owner,
+ _thread_fd_table[i]->w_lockcount,
+ _thread_fd_table[i]->w_fname,
+ _thread_fd_table[i]->w_lineno);
+ _thread_sys_write(fd, s, strlen(s));
}
}
@@ -299,6 +202,78 @@ _thread_dump_info(void)
}
}
+static void
+dump_thread(int fd, pthread_t pthread, int long_version)
+{
+ char s[512];
+ int i;
+
+ /* Find the state: */
+ for (i = 0; i < NELEMENTS(thread_info) - 1; i++)
+ if (thread_info[i].state == pthread->state)
+ break;
+
+ /* Output a record for the thread: */
+ snprintf(s, sizeof(s),
+ "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
+ pthread, (pthread->name == NULL) ? "" : pthread->name,
+ pthread->active_priority, thread_info[i].name, pthread->fname,
+ pthread->lineno);
+ _thread_sys_write(fd, s, strlen(s));
+
+ if (long_version != 0) {
+ /* Check if this is the running thread: */
+ if (pthread == _thread_run) {
+ /* Output a record for the running thread: */
+ strcpy(s, "This is the running thread\n");
+ _thread_sys_write(fd, s, strlen(s));
+ }
+ /* Check if this is the initial thread: */
+ if (pthread == _thread_initial) {
+ /* Output a record for the initial thread: */
+ strcpy(s, "This is the initial thread\n");
+ _thread_sys_write(fd, s, strlen(s));
+ }
+ /* Process according to thread state: */
+ switch (pthread->state) {
+ /* File descriptor read lock wait: */
+ case PS_FDLR_WAIT:
+ case PS_FDLW_WAIT:
+ case PS_FDR_WAIT:
+ case PS_FDW_WAIT:
+ /* Write the lock details: */
+ snprintf(s, sizeof(s), "fd %d[%s:%d]",
+ pthread->data.fd.fd,
+ pthread->data.fd.fname,
+ pthread->data.fd.branch);
+ _thread_sys_write(fd, s, strlen(s));
+ snprintf(s, sizeof(s), "owner %pr/%pw\n",
+ _thread_fd_table[pthread->data.fd.fd]->r_owner,
+ _thread_fd_table[pthread->data.fd.fd]->w_owner);
+ _thread_sys_write(fd, s, strlen(s));
+ break;
+ case PS_SIGWAIT:
+ snprintf(s, sizeof(s), "sigmask (hi)");
+ _thread_sys_write(fd, s, strlen(s));
+ for (i = _SIG_WORDS - 1; i >= 0; i--) {
+ snprintf(s, sizeof(s), "%08x\n",
+ pthread->sigmask.__bits[i]);
+ _thread_sys_write(fd, s, strlen(s));
+ }
+ snprintf(s, sizeof(s), "(lo)\n");
+ _thread_sys_write(fd, s, strlen(s));
+ break;
+ /*
+ * Trap other states that are not explicitly
+ * coded to dump information:
+ */
+ default:
+ /* Nothing to do here. */
+ break;
+ }
+ }
+}
+
/* Set the thread name for debug: */
void
pthread_set_name_np(pthread_t thread, char *name)
diff --git a/lib/libc_r/uthread/uthread_init.c b/lib/libc_r/uthread/uthread_init.c
index 3cbd453..35731c4 100644
--- a/lib/libc_r/uthread/uthread_init.c
+++ b/lib/libc_r/uthread/uthread_init.c
@@ -92,7 +92,7 @@ _thread_init(void)
int mib[2];
struct clockinfo clockinfo;
struct sigaction act;
- struct itimerval itimer;
+ struct sigaltstack alt;
/* Check if this function has already been called: */
if (_thread_initial)
@@ -133,7 +133,7 @@ _thread_init(void)
/*
* Create a pipe that is written to by the signal handler to prevent
- * signals being missed in calls to _select:
+ * signals being missed in calls to _select:
*/
if (_thread_sys_pipe(_thread_kern_pipe) != 0) {
/* Cannot create pipe, so abort: */
@@ -168,12 +168,12 @@ _thread_init(void)
else if ((_thread_initial = (pthread_t) malloc(sizeof(struct pthread))) == NULL) {
/*
* Insufficient memory to initialise this application, so
- * abort:
+ * abort:
*/
PANIC("Cannot allocate memory for initial thread");
}
/* Allocate memory for the scheduler stack: */
- else if ((_thread_kern_sched_stack = malloc(PAGE_SIZE * 10)) == NULL)
+ else if ((_thread_kern_sched_stack = malloc(SCHED_STACK_SIZE)) == NULL)
PANIC("Failed to allocate stack for scheduler");
else {
/* Zero the global kernel thread structure: */
@@ -217,8 +217,8 @@ _thread_init(void)
/* Setup the context for the scheduler: */
_setjmp(_thread_kern_sched_jb);
- SET_STACK_JB(_thread_kern_sched_jb,
- _thread_kern_sched_stack + PAGE_SIZE*10 - sizeof(double));
+ SET_STACK_JB(_thread_kern_sched_jb, _thread_kern_sched_stack +
+ SCHED_STACK_SIZE - sizeof(double));
SET_RETURN_ADDR_JB(_thread_kern_sched_jb, _thread_kern_scheduler);
/*
@@ -253,12 +253,9 @@ _thread_init(void)
/* Initialize last active: */
_thread_initial->last_active = (long) _sched_ticks;
- /* Initialize the initial signal frame: */
- _thread_initial->sigframes[0] = &_thread_initial->sigframe0;
- _thread_initial->curframe = &_thread_initial->sigframe0;
- _thread_initial->curframe->ctxtype = CTX_JB_NOSIG;
- /* Set the base of the stack: */
- _thread_initial->curframe->stackp = (unsigned long) USRSTACK;
+ /* Initialize the initial context: */
+ _thread_initial->curframe = NULL;
+ _thread_initial->ctxtype = CTX_JB_NOSIG;
/* Initialise the rest of the fields: */
_thread_initial->poll_data.nfds = 0;
@@ -276,7 +273,7 @@ _thread_init(void)
/* Initialise the global signal action structure: */
sigfillset(&act.sa_mask);
act.sa_handler = (void (*) ()) _thread_sig_handler;
- act.sa_flags = SA_SIGINFO;
+ act.sa_flags = SA_SIGINFO | SA_ONSTACK;
/* Clear pending signals for the process: */
sigemptyset(&_process_sigpending);
@@ -284,6 +281,13 @@ _thread_init(void)
/* Clear the signal queue: */
memset(_thread_sigq, 0, sizeof(_thread_sigq));
+ /* Create and install an alternate signal stack: */
+ alt.ss_sp = malloc(SIGSTKSZ); /* recommended stack size */
+ alt.ss_size = SIGSTKSZ;
+ alt.ss_flags = 0;
+ if (_thread_sys_sigaltstack(&alt, NULL) != 0)
+ PANIC("Unable to install alternate signal stack");
+
/* Enter a loop to get the existing signal status: */
for (i = 1; i < NSIG; i++) {
/* Check for signals which cannot be trapped: */
@@ -295,7 +299,7 @@ _thread_init(void)
&_thread_sigact[i - 1]) != 0) {
/*
* Abort this process if signal
- * initialisation fails:
+ * initialisation fails:
*/
PANIC("Cannot read signal handler info");
}
@@ -313,7 +317,7 @@ _thread_init(void)
_thread_sys_sigaction(SIGINFO, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGCHLD, &act, NULL) != 0) {
/*
- * Abort this process if signal initialisation fails:
+ * Abort this process if signal initialisation fails:
*/
PANIC("Cannot initialise signal handler");
}
@@ -335,7 +339,7 @@ _thread_init(void)
if ((_thread_dtablesize = getdtablesize()) < 0) {
/*
* Cannot get the system defined table size, so abort
- * this process.
+ * this process.
*/
PANIC("Cannot get dtablesize");
}
@@ -346,7 +350,7 @@ _thread_init(void)
/*
* Cannot allocate memory for the file descriptor
- * table, so abort this process.
+ * table, so abort this process.
*/
PANIC("Cannot allocate memory for file descriptor table");
}
@@ -354,13 +358,13 @@ _thread_init(void)
if ((_thread_pfd_table = (struct pollfd *) malloc(sizeof(struct pollfd) * _thread_dtablesize)) == NULL) {
/*
* Cannot allocate memory for the file descriptor
- * table, so abort this process.
+ * table, so abort this process.
*/
PANIC("Cannot allocate memory for pollfd table");
} else {
/*
* Enter a loop to initialise the file descriptor
- * table:
+ * table:
*/
for (i = 0; i < _thread_dtablesize; i++) {
/* Initialise the file descriptor table: */
@@ -374,14 +378,6 @@ _thread_init(void)
PANIC("Cannot initialize stdio file "
"descriptor table entry");
}
-
- /* Install the scheduling timer: */
- itimer.it_interval.tv_sec = 0;
- itimer.it_interval.tv_usec = _clock_res_usec;
- itimer.it_value = itimer.it_interval;
- if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0)
- PANIC("Cannot set interval timer");
-
}
}
@@ -401,10 +397,10 @@ _thread_init(void)
}
/*
- * Special start up code for NetBSD/Alpha
+ * Special start up code for NetBSD/Alpha
*/
#if defined(__NetBSD__) && defined(__alpha__)
-int
+int
main(int argc, char *argv[], char *env);
int
diff --git a/lib/libc_r/uthread/uthread_jmp.c b/lib/libc_r/uthread/uthread_jmp.c
index f2c38c2..db19ebd 100644
--- a/lib/libc_r/uthread/uthread_jmp.c
+++ b/lib/libc_r/uthread/uthread_jmp.c
@@ -40,163 +40,72 @@
#include <pthread.h>
#include "pthread_private.h"
+/* Prototypes: */
+static inline int check_stack(pthread_t thread, void *stackp);
+
void
siglongjmp(sigjmp_buf env, int savemask)
{
- void *jmp_stackp;
- void *stack_begin, *stack_end;
- int frame, dst_frame;
-
- if ((frame = _thread_run->sigframe_count) == 0)
- __siglongjmp(env, savemask);
-
- /* Get the stack pointer from the jump buffer. */
- jmp_stackp = (void *) GET_STACK_SJB(env);
-
- /* Get the bounds of the current threads stack. */
- PTHREAD_ASSERT(_thread_run->stack != NULL,
- "Thread stack pointer is null");
- stack_begin = _thread_run->stack;
- stack_end = stack_begin + _thread_run->attr.stacksize_attr;
-
- /*
- * 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)))
+ if (check_stack(_thread_run, (void *) GET_STACK_SJB(env)))
PANIC("siglongjmp()ing between thread contexts is undefined by "
"POSIX 1003.1");
- if ((dst_frame = _thread_sigframe_find(_thread_run, jmp_stackp)) < 0)
- /*
- * The stack pointer was verified above, so this
- * shouldn't happen. Let's be anal anyways.
- */
- PANIC("Error locating signal frame");
- else if (dst_frame == frame) {
- /*
- * The stack pointer is somewhere within the current
- * frame. Jump to the users context.
- */
- __siglongjmp(env, savemask);
- }
/*
- * Copy the users context to the return context of the
- * destination frame.
+ * The stack pointer is somewhere within the threads stack.
+ * Jump to the users context.
*/
- memcpy(&_thread_run->sigframes[dst_frame]->ctx.sigjb, env, sizeof(*env));
- _thread_run->sigframes[dst_frame]->ctxtype = CTX_SJB;
- _thread_run->sigframes[dst_frame]->longjmp_val = savemask;
- _thread_run->curframe->dst_frame = dst_frame;
- ___longjmp(*_thread_run->curframe->sig_jb, 1);
+ __siglongjmp(env, savemask);
}
void
longjmp(jmp_buf env, int val)
{
- void *jmp_stackp;
- void *stack_begin, *stack_end;
- int frame, dst_frame;
-
- if ((frame = _thread_run->sigframe_count) == 0)
- __longjmp(env, val);
-
- /* Get the stack pointer from the jump buffer. */
- jmp_stackp = (void *) GET_STACK_JB(env);
-
- /* Get the bounds of the current threads stack. */
- PTHREAD_ASSERT(_thread_run->stack != NULL,
- "Thread stack pointer is null");
- stack_begin = _thread_run->stack;
- stack_end = stack_begin + _thread_run->attr.stacksize_attr;
-
- /*
- * 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)))
+ if (check_stack(_thread_run, (void *) GET_STACK_JB(env)))
PANIC("longjmp()ing between thread contexts is undefined by "
"POSIX 1003.1");
- if ((dst_frame = _thread_sigframe_find(_thread_run, jmp_stackp)) < 0)
- /*
- * The stack pointer was verified above, so this
- * shouldn't happen. Let's be anal anyways.
- */
- PANIC("Error locating signal frame");
- else if (dst_frame == frame) {
- /*
- * The stack pointer is somewhere within the current
- * frame. Jump to the users context.
- */
- __longjmp(env, val);
- }
-
/*
- * Copy the users context to the return context of the
- * destination frame.
+ * The stack pointer is somewhere within the threads stack.
+ * Jump to the users context.
*/
- memcpy(&_thread_run->sigframes[dst_frame]->ctx.jb, env, sizeof(*env));
- _thread_run->sigframes[dst_frame]->ctxtype = CTX_JB;
- _thread_run->sigframes[dst_frame]->longjmp_val = val;
- _thread_run->curframe->dst_frame = dst_frame;
- ___longjmp(*_thread_run->curframe->sig_jb, 1);
+ __longjmp(env, val);
}
void
_longjmp(jmp_buf env, int val)
{
- void *jmp_stackp;
- void *stack_begin, *stack_end;
- int frame, dst_frame;
+ if (check_stack(_thread_run, (void *) GET_STACK_JB(env)))
+ PANIC("_longjmp()ing between thread contexts is undefined by "
+ "POSIX 1003.1");
- if ((frame = _thread_run->sigframe_count) == 0)
- ___longjmp(env, val);
+ /*
+ * The stack pointer is somewhere within the threads stack.
+ * Jump to the users context.
+ */
+ ___longjmp(env, val);
+}
- /* Get the stack pointer from the jump buffer. */
- jmp_stackp = (void *) GET_STACK_JB(env);
+/* Returns 0 if stack check is OK, non-zero otherwise. */
+static inline int
+check_stack(pthread_t thread, void *stackp)
+{
+ void *stack_begin, *stack_end;
/* Get the bounds of the current threads stack. */
- PTHREAD_ASSERT(_thread_run->stack != NULL,
+ PTHREAD_ASSERT(thread->stack != NULL,
"Thread stack pointer is null");
- stack_begin = _thread_run->stack;
- stack_end = stack_begin + _thread_run->attr.stacksize_attr;
+ stack_begin = thread->stack;
+ stack_end = stack_begin + thread->attr.stacksize_attr;
/*
* 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");
-
- if ((dst_frame = _thread_sigframe_find(_thread_run, jmp_stackp)) < 0)
- /*
- * The stack pointer was verified above, so this
- * shouldn't happen. Let's be anal anyways.
- */
- PANIC("Error locating signal frame");
- else if (dst_frame == frame) {
- /*
- * The stack pointer is somewhere within the current
- * frame. Jump to the users context.
- */
- ___longjmp(env, val);
- }
- /*
- * Copy the users context to the return context of the
- * destination frame.
- */
- memcpy(&_thread_run->sigframes[dst_frame]->ctx.jb, env, sizeof(*env));
- _thread_run->sigframes[dst_frame]->ctxtype = CTX_JB_NOSIG;
- _thread_run->sigframes[dst_frame]->longjmp_val = val;
- _thread_run->curframe->dst_frame = dst_frame;
- ___longjmp(*_thread_run->curframe->sig_jb, 1);
+ if (((stackp < stack_begin) && (stackp < stack_end)) ||
+ ((stackp > stack_begin) && (stackp > stack_end)))
+ return (1);
+ else
+ return (0);
}
#endif
diff --git a/lib/libc_r/uthread/uthread_join.c b/lib/libc_r/uthread/uthread_join.c
index cda31bd..b4a7c61 100644
--- a/lib/libc_r/uthread/uthread_join.c
+++ b/lib/libc_r/uthread/uthread_join.c
@@ -74,46 +74,74 @@ pthread_join(pthread_t pthread, void **thread_return)
else if (pthread->state != PS_DEAD) {
PTHREAD_ASSERT_NOT_IN_SYNCQ(_thread_run);
- /* Clear the interrupted flag: */
- _thread_run->interrupted = 0;
-
/*
- * Protect against being context switched out while
- * adding this thread to the join queue.
+ * Enter a loop in case this thread is woken prematurely
+ * in order to invoke a signal handler:
*/
- _thread_kern_sig_defer();
-
- /* Add the running thread to the join queue: */
- TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, sqe);
- _thread_run->flags |= PTHREAD_FLAGS_IN_JOINQ;
- _thread_run->data.thread = pthread;
-
- /* Schedule the next thread: */
- _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
-
- if (_thread_run->interrupted != 0) {
- TAILQ_REMOVE(&(pthread->join_queue), _thread_run, sqe);
- _thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
+ for (;;) {
+ /* Clear the interrupted flag: */
+ _thread_run->interrupted = 0;
+
+ /*
+ * Protect against being context switched out while
+ * adding this thread to the join queue.
+ */
+ _thread_kern_sig_defer();
+
+ /* Add the running thread to the join queue: */
+ TAILQ_INSERT_TAIL(&(pthread->join_queue),
+ _thread_run, sqe);
+ _thread_run->flags |= PTHREAD_FLAGS_IN_JOINQ;
+ _thread_run->data.thread = pthread;
+
+ /* Schedule the next thread: */
+ _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
+
+ if ((_thread_run->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
+ TAILQ_REMOVE(&(pthread->join_queue),
+ _thread_run, sqe);
+ _thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
+ }
+ _thread_run->data.thread = NULL;
+
+ _thread_kern_sig_undefer();
+
+ if (_thread_run->interrupted != 0) {
+ if (_thread_run->continuation != NULL)
+ _thread_run->continuation(_thread_run);
+ /*
+ * This thread was interrupted, probably to
+ * invoke a signal handler. Make sure the
+ * target thread is still joinable.
+ */
+ if (((_find_thread(pthread) != 0) &&
+ (_find_dead_thread(pthread) != 0)) ||
+ ((pthread->attr.flags &
+ PTHREAD_DETACHED) != 0)) {
+ /* Return an error: */
+ ret = ESRCH;
+
+ /* We're done; break out of the loop. */
+ break;
+ }
+ else if (pthread->state == PS_DEAD) {
+ /* We're done; break out of the loop. */
+ break;
+ }
+ } else {
+ /*
+ * The thread return value and error are set
+ * by the thread we're joining to when it
+ * exits or detaches:
+ */
+ ret = _thread_run->error;
+ if ((ret == 0) && (thread_return != NULL))
+ *thread_return = _thread_run->ret;
+
+ /* We're done; break out of the loop. */
+ break;
+ }
}
- _thread_run->data.thread = NULL;
-
- _thread_kern_sig_undefer();
-
- 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) {
- /* Check if the return value is required: */
- if (thread_return)
- /* Return the thread's return value: */
- *thread_return = pthread->ret;
- }
- else
- /* Return an error: */
- ret = ESRCH;
-
/* Check if the return value is required: */
} else if (thread_return != NULL)
/* Return the thread's return value: */
@@ -129,7 +157,7 @@ void
_join_backout(pthread_t pthread)
{
_thread_kern_sig_defer();
- if (pthread->state == PS_JOIN) {
+ if ((pthread->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
TAILQ_REMOVE(&pthread->data.thread->join_queue, pthread, sqe);
_thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
}
diff --git a/lib/libc_r/uthread/uthread_kern.c b/lib/libc_r/uthread/uthread_kern.c
index 23f16bc..67844e1 100644
--- a/lib/libc_r/uthread/uthread_kern.c
+++ b/lib/libc_r/uthread/uthread_kern.c
@@ -60,7 +60,7 @@
#endif
/* Static function prototype definitions: */
-static void
+static void
thread_kern_poll(int wait_reqd);
static void
@@ -77,7 +77,7 @@ static int last_tick = 0;
* return to a previous frame.
*/
void
-_thread_kern_sched_frame(int frame)
+_thread_kern_sched_frame(struct pthread_signal_frame *psf)
{
/*
* Flag the pthread kernel as executing scheduler code
@@ -86,13 +86,8 @@ _thread_kern_sched_frame(int frame)
*/
_thread_kern_in_sched = 1;
- /* Return to the specified frame: */
- _thread_run->curframe = _thread_run->sigframes[frame];
- _thread_run->sigframe_count = frame;
-
- if (_thread_run->sigframe_count == 0)
- /* Restore the threads priority: */
- _thread_run->active_priority &= ~PTHREAD_SIGNAL_PRIORITY;
+ /* Restore the signal frame: */
+ _thread_sigframe_restore(_thread_run, psf);
/* Switch to the thread scheduler: */
___longjmp(_thread_kern_sched_jb, 1);
@@ -127,16 +122,16 @@ _thread_kern_sched(ucontext_t *scp)
_thread_kern_scheduler();
} else {
/* Save the state of the current thread: */
- if (_setjmp(_thread_run->curframe->ctx.jb) == 0) {
+ if (_setjmp(_thread_run->ctx.jb) == 0) {
/* Flag the jump buffer was the last state saved: */
- _thread_run->curframe->ctxtype = CTX_JB_NOSIG;
- _thread_run->curframe->longjmp_val = 1;
+ _thread_run->ctxtype = CTX_JB_NOSIG;
+ _thread_run->longjmp_val = 1;
} else {
DBG_MSG("Returned from ___longjmp, thread %p\n",
_thread_run);
/*
* This point is reached when a longjmp() is called
- * to restore the state of a thread.
+ * to restore the state of a thread.
*
* This is the normal way out of the scheduler.
*/
@@ -147,7 +142,7 @@ _thread_kern_sched(ucontext_t *scp)
PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags &
PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
- /*
+ /*
* Cancellations override signals.
*
* Stick a cancellation point at the
@@ -183,7 +178,6 @@ _thread_kern_sched_sig(void)
void
_thread_kern_scheduler(void)
{
- struct pthread_signal_frame *psf;
struct timespec ts;
struct timeval tv;
pthread_t pthread, pthread_h;
@@ -205,7 +199,7 @@ _thread_kern_scheduler(void)
* ready to run. This loop completes when there are no more threads
* in the global list or when a thread has its state restored by
* either a sigreturn (if the state was saved as a sigcontext) or a
- * longjmp (if the state was saved by a setjmp).
+ * longjmp (if the state was saved by a setjmp).
*/
while (!(TAILQ_EMPTY(&_thread_list))) {
/* Get the current time of day: */
@@ -229,7 +223,7 @@ _thread_kern_scheduler(void)
if (_thread_run->state != PS_RUNNING) {
/*
* Save the current time as the time that the
- * thread became inactive:
+ * thread became inactive:
*/
_thread_run->last_inactive = (long)current_tick;
if (_thread_run->last_inactive <
@@ -266,7 +260,7 @@ _thread_kern_scheduler(void)
/*
* States which do not depend on file descriptor I/O
- * operations or timeouts:
+ * operations or timeouts:
*/
case PS_DEADLOCK:
case PS_FDLR_WAIT:
@@ -326,10 +320,16 @@ _thread_kern_scheduler(void)
}
/*
+ * Avoid polling file descriptors if there are none
+ * waiting:
+ */
+ if (TAILQ_EMPTY(&_workq) == 0) {
+ }
+ /*
* Poll file descriptors only if a new scheduling signal
* has occurred or if we have no more runnable threads.
*/
- if (((current_tick = _sched_ticks) != last_tick) ||
+ else if (((current_tick = _sched_ticks) != last_tick) ||
((_thread_run->state != PS_RUNNING) &&
(PTHREAD_PRIOQ_FIRST() == NULL))) {
/* Unprotect the scheduling queues: */
@@ -337,7 +337,7 @@ _thread_kern_scheduler(void)
/*
* Poll file descriptors to update the state of threads
- * waiting on file I/O where data may be available:
+ * waiting on file I/O where data may be available:
*/
thread_kern_poll(0);
@@ -392,7 +392,7 @@ _thread_kern_scheduler(void)
if (add_to_prioq != 0) {
/*
* Save the current time as the time that the
- * thread became inactive:
+ * thread became inactive:
*/
current_tick = _sched_ticks;
_thread_run->last_inactive = (long)current_tick;
@@ -445,7 +445,7 @@ _thread_kern_scheduler(void)
/*
* Lock the pthread kernel by changing the pointer to
* the running thread to point to the global kernel
- * thread structure:
+ * thread structure:
*/
_thread_run = &_thread_kern_thread;
DBG_MSG("No runnable threads, using kernel thread %p\n",
@@ -456,7 +456,7 @@ _thread_kern_scheduler(void)
/*
* There are no threads ready to run, so wait until
- * something happens that changes this condition:
+ * something happens that changes this condition:
*/
thread_kern_poll(1);
@@ -524,7 +524,7 @@ _thread_kern_scheduler(void)
/*
* Save the current time as the time that the thread
- * became active:
+ * became active:
*/
current_tick = _sched_ticks;
_thread_run->last_active = (long) current_tick;
@@ -532,7 +532,7 @@ _thread_kern_scheduler(void)
/*
* Check if this thread is running for the first time
* or running again after using its full time slice
- * allocation:
+ * allocation:
*/
if (_thread_run->slice_usec == -1) {
/* Reset the accumulated time slice period: */
@@ -551,36 +551,39 @@ _thread_kern_scheduler(void)
/*
* Continue the thread at its current frame:
*/
- psf = _thread_run->curframe;
- switch(psf->ctxtype) {
+ switch(_thread_run->ctxtype) {
case CTX_JB_NOSIG:
- ___longjmp(psf->ctx.jb, psf->longjmp_val);
+ ___longjmp(_thread_run->ctx.jb,
+ _thread_run->longjmp_val);
break;
case CTX_JB:
- __longjmp(psf->ctx.jb, psf->longjmp_val);
+ __longjmp(_thread_run->ctx.jb,
+ _thread_run->longjmp_val);
break;
case CTX_SJB:
- __siglongjmp(psf->ctx.sigjb, psf->longjmp_val);
+ __siglongjmp(_thread_run->ctx.sigjb,
+ _thread_run->longjmp_val);
break;
case CTX_UC:
/* XXX - Restore FP regsisters? */
- FP_RESTORE_UC(&psf->ctx.uc);
+ FP_RESTORE_UC(&_thread_run->ctx.uc);
/*
* Do a sigreturn to restart the thread that
- * was interrupted by a signal:
+ * was interrupted by a signal:
*/
_thread_kern_in_sched = 0;
#if NOT_YET
- _setcontext(&psf->ctx.uc);
+ _setcontext(&_thread_run->ctx.uc);
#else
/*
* Ensure the process signal mask is set
* correctly:
*/
- psf->ctx.uc.uc_sigmask = _process_sigmask;
- _thread_sys_sigreturn(&psf->ctx.uc);
+ _thread_run->ctx.uc.uc_sigmask =
+ _process_sigmask;
+ _thread_sys_sigreturn(&_thread_run->ctx.uc);
#endif
break;
}
@@ -800,14 +803,14 @@ thread_kern_poll(int wait_reqd)
/*
* Wait for a file descriptor to be ready for read, write, or
- * an exception, or a timeout to occur:
+ * an exception, or a timeout to occur:
*/
count = _thread_sys_poll(_thread_pfd_table, nfds, timeout_ms);
if (kern_pipe_added != 0)
/*
* Remove the pthread kernel pipe file descriptor
- * from the pollfd table:
+ * from the pollfd table:
*/
nfds = 1;
else
@@ -821,7 +824,7 @@ thread_kern_poll(int wait_reqd)
(_thread_pfd_table[0].revents & POLLRDNORM))) {
/*
* If the kernel read pipe was included in the
- * count:
+ * count:
*/
if (count > 0) {
/* Decrement the count of file descriptors: */
@@ -843,7 +846,7 @@ thread_kern_poll(int wait_reqd)
/*
* Enter a loop to look for threads waiting on file
* descriptors that are flagged as available by the
- * _poll syscall:
+ * _poll syscall:
*/
PTHREAD_WAITQ_SETACTIVE();
TAILQ_FOREACH(pthread, &_workq, qe) {
@@ -986,7 +989,7 @@ _thread_kern_set_timeout(const struct timespec * timeout)
if (timeout == NULL) {
/*
* Set the wakeup time to something that can be recognised as
- * different to an actual time of day:
+ * different to an actual time of day:
*/
_thread_run->wakeup_time.tv_sec = -1;
_thread_run->wakeup_time.tv_nsec = -1;
@@ -1042,7 +1045,7 @@ _thread_kern_sig_undefer(void)
if (_sigq_check_reqd != 0)
_thread_kern_sched(NULL);
- /*
+ /*
* Check for asynchronous cancellation before delivering any
* pending signals:
*/
@@ -1071,7 +1074,7 @@ dequeue_signals(void)
int num;
/*
- * Enter a loop to clear the pthread kernel pipe:
+ * Enter a loop to clear the pthread kernel pipe:
*/
while (((num = _thread_sys_read(_thread_kern_pipe[0], bufr,
sizeof(bufr))) > 0) || (num == -1 && errno == EINTR)) {
diff --git a/lib/libc_r/uthread/uthread_mutex.c b/lib/libc_r/uthread/uthread_mutex.c
index ec6c0f6..f3649db 100644
--- a/lib/libc_r/uthread/uthread_mutex.c
+++ b/lib/libc_r/uthread/uthread_mutex.c
@@ -406,13 +406,29 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
_thread_init();
if (mutex == NULL)
- ret = EINVAL;
+ return (EINVAL);
/*
* If the mutex is statically initialized, perform the dynamic
* initialization:
*/
- else if (*mutex != NULL || (ret = init_static(mutex)) == 0) {
+ if ((*mutex == NULL) &&
+ ((ret = init_static(mutex)) != 0))
+ return (ret);
+
+ /* Reset the interrupted flag: */
+ _thread_run->interrupted = 0;
+
+ /*
+ * Enter a loop waiting to become the mutex owner. We need a
+ * loop in case the waiting thread is interrupted by a signal
+ * to execute a signal handler. It is not (currently) possible
+ * to remain in the waiting queue while running a handler.
+ * Instead, the thread is interrupted and backed out of the
+ * waiting queue prior to executing the signal handler.
+ */
+ while (((*mutex)->m_owner != _thread_run) && (ret == 0) &&
+ (_thread_run->interrupted == 0)) {
/*
* Defer signals to protect the scheduling queues from
* access by the signal handler:
@@ -432,9 +448,6 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
_MUTEX_INIT_LINK(*mutex);
}
- /* Reset the interrupted flag: */
- _thread_run->interrupted = 0;
-
/* Process according to mutex type: */
switch ((*mutex)->m_protocol) {
/* Default POSIX mutex: */
@@ -624,12 +637,12 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
* necessary:
*/
_thread_kern_sig_undefer();
-
- if (_thread_run->interrupted != 0 &&
- _thread_run->continuation != NULL)
- _thread_run->continuation((void *) _thread_run);
}
+ if (_thread_run->interrupted != 0 &&
+ _thread_run->continuation != NULL)
+ _thread_run->continuation((void *) _thread_run);
+
/* Return the completion status: */
return (ret);
}
@@ -1381,7 +1394,7 @@ _mutex_lock_backout(pthread_t pthread)
* access by the signal handler:
*/
_thread_kern_sig_defer();
- if (pthread->state == PS_MUTEX_WAIT) {
+ if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
mutex = pthread->data.mutex;
/* Lock the mutex structure: */
@@ -1390,7 +1403,7 @@ _mutex_lock_backout(pthread_t pthread)
mutex_queue_remove(mutex, pthread);
/* This thread is no longer waiting for the mutex: */
- mutex->m_owner->data.mutex = NULL;
+ pthread->data.mutex = NULL;
/* Unlock the mutex structure: */
_SPINUNLOCK(&mutex->lock);
diff --git a/lib/libc_r/uthread/uthread_sig.c b/lib/libc_r/uthread/uthread_sig.c
index f19582d..3bcd9c1 100644
--- a/lib/libc_r/uthread/uthread_sig.c
+++ b/lib/libc_r/uthread/uthread_sig.c
@@ -49,9 +49,7 @@ static void thread_sig_check_state(pthread_t pthread, int sig);
static pthread_t thread_sig_find(int sig);
static void thread_sig_handle_special(int sig);
static void thread_sig_savecontext(pthread_t pthread, ucontext_t *ucp);
-static void thread_sigframe_add(pthread_t thread, int sig);
-static void thread_sigframe_leave(pthread_t thread, int frame);
-static void thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf);
+static void thread_sigframe_add(pthread_t thread, int sig, int has_args);
static void thread_sigframe_save(pthread_t thread, struct pthread_signal_frame *psf);
/* #define DEBUG_SIGNAL */
@@ -72,21 +70,30 @@ static void thread_sigframe_save(pthread_t thread, struct pthread_signal_frame *
void
_thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
{
- pthread_t pthread;
- int current_frame;
+ pthread_t pthread, pthread_h;
+ void *stackp;
+ int in_sched = 0;
char c;
if (ucp == NULL)
PANIC("Thread signal handler received null context");
DBG_MSG("Got signal %d, current thread %p\n", sig, _thread_run);
+ if (_thread_kern_in_sched != 0)
+ in_sched = 1;
+ else {
+ stackp = (void *)GET_STACK_UC(ucp);
+ if ((stackp >= _thread_kern_sched_stack) &&
+ (stackp <= _thread_kern_sched_stack + SCHED_STACK_SIZE))
+ in_sched = 1;
+ }
/* Check if an interval timer signal: */
if (sig == _SCHED_SIGNAL) {
/* Update the scheduling clock: */
gettimeofday((struct timeval *)&_sched_tod, NULL);
_sched_ticks++;
- if (_thread_kern_in_sched != 0) {
+ if (in_sched != 0) {
/*
* The scheduler is already running; ignore this
* signal.
@@ -108,13 +115,13 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
/*
* Schedule the next thread. This function is not
* expected to return because it will do a longjmp
- * instead.
+ * instead.
*/
_thread_kern_sched(ucp);
/*
* This point should not be reached, so abort the
- * process:
+ * process:
*/
PANIC("Returned to signal function from scheduler");
}
@@ -124,8 +131,7 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
* is accessing the scheduling queues or if there is a currently
* running thread that has deferred signals.
*/
- else if ((_thread_kern_in_sched != 0) ||
- (_thread_run->sig_defer_count > 0)) {
+ else if ((in_sched != 0) || (_thread_run->sig_defer_count > 0)) {
/* Cast the signal number to a character variable: */
c = sig;
@@ -176,10 +182,9 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
* cannot be interrupted by other signals.
*/
else if (_thread_sigq[sig - 1].blocked == 0) {
- /* The signal is not blocked; handle the signal: */
- current_frame = _thread_run->sigframe_count;
-
/*
+ * The signal is not blocked; handle the signal.
+ *
* Ignore subsequent occurrences of this signal
* until the current signal is handled:
*/
@@ -204,6 +209,7 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
/* Handle special signals: */
thread_sig_handle_special(sig);
+ pthread_h = NULL;
if ((pthread = thread_sig_find(sig)) != NULL) {
DBG_MSG("Got signal %d, adding frame to thread %p\n",
sig, pthread);
@@ -221,9 +227,9 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
thread_sig_add(pthread, sig, /*has_args*/ 1);
/* Take a peek at the next ready to run thread: */
- pthread = PTHREAD_PRIOQ_FIRST();
+ pthread_h = PTHREAD_PRIOQ_FIRST();
DBG_MSG("Finished adding frame, head of prio list %p\n",
- pthread);
+ pthread_h);
}
else
DBG_MSG("No thread to handle signal %d\n", sig);
@@ -235,11 +241,9 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
* signal and the currently running thread is not in a
* signal handler.
*/
- if ((_thread_run->sigframe_count > current_frame) ||
- ((pthread != NULL) &&
- (pthread->active_priority > _thread_run->active_priority))) {
+ if ((pthread == _thread_run) || ((pthread_h != NULL) &&
+ (pthread_h->active_priority > _thread_run->active_priority))) {
/* Enter the kernel scheduler: */
- DBG_MSG("Entering scheduler from signal handler\n");
_thread_kern_sched(ucp);
}
}
@@ -253,17 +257,13 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
static void
thread_sig_savecontext(pthread_t pthread, ucontext_t *ucp)
{
- struct pthread_signal_frame *psf;
-
- psf = _thread_run->curframe;
-
- memcpy(&psf->ctx.uc, ucp, sizeof(*ucp));
+ memcpy(&pthread->ctx.uc, ucp, sizeof(*ucp));
/* XXX - Save FP registers too? */
- FP_SAVE_UC(&psf->ctx.uc);
+ FP_SAVE_UC(&pthread->ctx.uc);
/* Mark the context saved as a ucontext: */
- psf->ctxtype = CTX_UC;
+ pthread->ctxtype = CTX_UC;
}
/*
@@ -278,10 +278,13 @@ thread_sig_find(int sig)
DBG_MSG("Looking for thread to handle signal %d\n", sig);
/* Check if the signal requires a dump of thread information: */
- if (sig == SIGINFO)
+ if (sig == SIGINFO) {
/* Dump thread information to file: */
_thread_dump_info();
+ /* Unblock this signal to allow further dumps: */
+ _thread_sigq[sig - 1].blocked = 0;
+ }
/* Check if an interval timer signal: */
else if (sig == _SCHED_SIGNAL) {
/*
@@ -326,7 +329,7 @@ thread_sig_find(int sig)
* A signal handler is not invoked for threads
* in sigwait. Clear the blocked and pending
* flags.
- */
+ */
_thread_sigq[sig - 1].blocked = 0;
_thread_sigq[sig - 1].pending = 0;
@@ -375,7 +378,7 @@ thread_sig_find(int sig)
signaled_thread == NULL) {
/*
* Enter a loop to look for other threads
- * capable of receiving the signal:
+ * capable of receiving the signal:
*/
TAILQ_FOREACH(pthread, &_thread_list, tle) {
if (!sigismember(&pthread->sigmask,
@@ -565,8 +568,7 @@ thread_sig_handle_special(int sig)
static void
thread_sig_add(pthread_t pthread, int sig, int has_args)
{
- int restart, frame;
- int block_signals = 0;
+ int restart;
int suppress_handler = 0;
restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART;
@@ -657,8 +659,11 @@ thread_sig_add(pthread_t pthread, int sig, int has_args)
* States which cannot be interrupted but still require the
* signal handler to run:
*/
- case PS_COND_WAIT:
case PS_JOIN:
+ /* Only set the interrupted flag for PS_JOIN: */
+ pthread->interrupted = 1;
+ /* FALLTHROUGH */
+ case PS_COND_WAIT:
case PS_MUTEX_WAIT:
/*
* Remove the thread from the wait queue. It will
@@ -687,14 +692,6 @@ thread_sig_add(pthread_t pthread, int sig, int has_args)
* the actual handler.
*/
PTHREAD_WAITQ_REMOVE(pthread);
- /*
- * To ensure the thread is removed from the fd and file
- * queues before any other signal interrupts it, set the
- * signal mask to block all signals. As soon as the thread
- * is removed from the queue the signal mask will be
- * restored.
- */
- block_signals = 1;
break;
/*
@@ -736,22 +733,15 @@ thread_sig_add(pthread_t pthread, int sig, int has_args)
}
if (suppress_handler == 0) {
+ /* Setup a signal frame and save the current threads state: */
+ thread_sigframe_add(pthread, sig, has_args);
+
/*
- * Save the current state of the thread and add a
- * new signal frame.
+ * Signals are deferred until just before the threads
+ * signal handler is invoked:
*/
- frame = pthread->sigframe_count;
- thread_sigframe_save(pthread, pthread->curframe);
- thread_sigframe_add(pthread, sig);
- pthread->sigframes[frame + 1]->sig_has_args = has_args;
- SIGSETOR(pthread->sigmask, _thread_sigact[sig - 1].sa_mask);
- if (block_signals != 0) {
- /* Save the signal mask and block all signals: */
- pthread->sigframes[frame + 1]->saved_state.psd_sigmask =
- pthread->sigmask;
- sigfillset(&pthread->sigmask);
- }
-
+ pthread->sig_defer_count = 1;
+
/* Make sure the thread is runnable: */
if (pthread->state != PS_RUNNING)
PTHREAD_SET_STATE(pthread, PS_RUNNING);
@@ -925,19 +915,16 @@ _thread_sig_wrapper(void)
{
void (*sigfunc)(int, siginfo_t *, void *);
struct pthread_signal_frame *psf;
- pthread_t thread;
- int dead = 0;
- int i, sig, has_args;
- int frame, dst_frame;
+ pthread_t thread;
thread = _thread_run;
/* Get the current frame and state: */
- frame = thread->sigframe_count;
- PTHREAD_ASSERT(frame > 0, "Invalid signal frame in signal handler");
psf = thread->curframe;
+ thread->curframe = NULL;
+ PTHREAD_ASSERT(psf != NULL, "Invalid signal frame in signal handler");
- /* Check the threads previous state: */
+ /* Check the threads previous state: */
if (psf->saved_state.psd_state != PS_RUNNING) {
/*
* Do a little cleanup handling for those threads in
@@ -950,15 +937,26 @@ _thread_sig_wrapper(void)
case PS_FDLW_WAIT:
_fd_lock_backout(thread);
psf->saved_state.psd_state = PS_RUNNING;
- /* Reenable signals: */
- thread->sigmask = psf->saved_state.psd_sigmask;
break;
case PS_FILE_WAIT:
_flockfile_backout(thread);
psf->saved_state.psd_state = PS_RUNNING;
- /* Reenable signals: */
- thread->sigmask = psf->saved_state.psd_sigmask;
+ break;
+
+ case PS_COND_WAIT:
+ _cond_wait_backout(thread);
+ psf->saved_state.psd_state = PS_RUNNING;
+ break;
+
+ case PS_JOIN:
+ _join_backout(thread);
+ psf->saved_state.psd_state = PS_RUNNING;
+ break;
+
+ case PS_MUTEX_WAIT:
+ _mutex_lock_backout(thread);
+ psf->saved_state.psd_state = PS_RUNNING;
break;
default:
@@ -966,11 +964,20 @@ _thread_sig_wrapper(void)
}
}
+ /* Unblock the signal in case we don't return from the handler: */
+ _thread_sigq[psf->signo - 1].blocked = 0;
+
+ /*
+ * Lower the priority before calling the handler in case
+ * it never returns (longjmps back):
+ */
+ thread->active_priority &= ~PTHREAD_SIGNAL_PRIORITY;
+
/*
- * Unless the thread exits or longjmps out of the signal handler,
- * return to the previous frame:
+ * Reenable interruptions without checking for the need to
+ * context switch:
*/
- dst_frame = frame - 1;
+ thread->sig_defer_count = 0;
/*
* Check that a custom handler is installed and if the signal
@@ -979,141 +986,45 @@ _thread_sig_wrapper(void)
sigfunc = _thread_sigact[psf->signo - 1].sa_sigaction;
if (((__sighandler_t *)sigfunc != SIG_DFL) &&
((__sighandler_t *)sigfunc != SIG_IGN)) {
+ DBG_MSG("_thread_sig_wrapper: Calling signal handler for "
+ "thread 0x%p\n", thread);
/*
- * The signal jump buffer is allocated off the stack.
- * If the signal handler tries to [_][sig]longjmp() or
- * setcontext(), our wrapped versions of these routines
- * will copy the user supplied jump buffer or context
- * to the destination signal frame, set the destination
- * signal frame in psf->dst_frame, and _longjmp() back
- * to here.
- */
- jmp_buf jb;
-
- /*
- * Set up the context for abnormal returns out of signal
- * handlers.
+ * Dispatch the signal via the custom signal
+ * handler:
*/
- psf->sig_jb = &jb;
- if (_setjmp(jb) == 0) {
- DBG_MSG("_thread_sig_wrapper: Entering frame %d, "
- "stack 0x%lx\n", frame, GET_STACK_JB(jb));
- /*
- * Invalidate the destination frame before calling
- * the signal handler.
- */
- psf->dst_frame = -1;
-
- /*
- * Dispatch the signal via the custom signal
- * handler:
- */
- if (psf->sig_has_args == 0)
- (*(sigfunc))(psf->signo, NULL, NULL);
- else if ((_thread_sigact[psf->signo - 1].sa_flags &
- SA_SIGINFO) != 0)
- (*(sigfunc))(psf->signo,
- &_thread_sigq[psf->signo - 1].siginfo,
- &_thread_sigq[psf->signo - 1].uc);
- else
- (*(sigfunc))(psf->signo,
- (siginfo_t *)_thread_sigq[psf->signo - 1].siginfo.si_code,
- &_thread_sigq[psf->signo - 1].uc);
- }
- else {
- /*
- * The return from _setjmp() should only be non-zero
- * when the signal handler wants to xxxlongjmp() or
- * setcontext() to a different context, or if the
- * thread has exited (via pthread_exit).
- */
- /*
- * Grab a copy of the destination frame before it
- * gets clobbered after unwinding.
- */
- dst_frame = psf->dst_frame;
- DBG_MSG("Abnormal exit from handler for signal %d, "
- "frame %d\n", psf->signo, frame);
-
- /* Has the thread exited? */
- if ((dead = thread->flags & PTHREAD_EXITING) != 0)
- /* When exiting, unwind to frame 0. */
- dst_frame = 0;
- else if ((dst_frame < 0) || (dst_frame > frame))
- PANIC("Attempt to unwind to invalid "
- "signal frame");
-
- /* Unwind to the target frame: */
- for (i = frame; i > dst_frame; i--) {
- DBG_MSG("Leaving frame %d, signal %d\n", i,
- thread->sigframes[i]->signo);
- /* Leave the current signal frame: */
- thread_sigframe_leave(thread, i);
-
- /*
- * Save whatever is needed out of the state
- * data; as soon as the frame count is
- * is decremented, another signal can arrive
- * and corrupt this view of the state data.
- */
- sig = thread->sigframes[i]->signo;
- has_args = thread->sigframes[i]->sig_has_args;
-
- /*
- * We're done with this signal frame:
- */
- thread->curframe = thread->sigframes[i - 1];
- thread->sigframe_count = i - 1;
-
- /*
- * Only unblock the signal if it was a
- * process signal as opposed to a signal
- * generated by pthread_kill().
- */
- if (has_args != 0)
- _thread_sigq[sig - 1].blocked = 0;
- }
- }
+ if (psf->sig_has_args == 0)
+ (*(sigfunc))(psf->signo, NULL, NULL);
+ else if ((_thread_sigact[psf->signo - 1].sa_flags &
+ SA_SIGINFO) != 0)
+ (*(sigfunc))(psf->signo, &psf->siginfo, &psf->uc);
+ else
+ (*(sigfunc))(psf->signo,
+ (siginfo_t *)psf->siginfo.si_code, &psf->uc);
}
-
/*
- * Call the kernel scheduler to schedule the next
- * thread.
+ * Call the kernel scheduler to safely restore the frame and
+ * schedule the next thread:
*/
- if (dead == 0) {
- /* Restore the threads state: */
- thread_sigframe_restore(thread, thread->sigframes[dst_frame]);
- _thread_kern_sched_frame(dst_frame);
- }
- else {
- PTHREAD_ASSERT(dst_frame == 0,
- "Invalid signal frame for dead thread");
-
- /* Perform any necessary cleanup before exiting. */
- thread_sigframe_leave(thread, 0);
-
- /* This should never return: */
- _thread_exit_finish();
- PANIC("Return from _thread_exit_finish in signal wrapper");
- }
+ _thread_kern_sched_frame(psf);
}
static void
-thread_sigframe_add(pthread_t thread, int sig)
+thread_sigframe_add(pthread_t thread, int sig, int has_args)
{
+ struct pthread_signal_frame *psf = NULL;
unsigned long stackp = 0;
/* Get the top of the threads stack: */
- switch (thread->curframe->ctxtype) {
+ switch (thread->ctxtype) {
case CTX_JB:
case CTX_JB_NOSIG:
- stackp = GET_STACK_JB(thread->curframe->ctx.jb);
+ stackp = GET_STACK_JB(thread->ctx.jb);
break;
case CTX_SJB:
- stackp = GET_STACK_SJB(thread->curframe->ctx.sigjb);
+ stackp = GET_STACK_SJB(thread->ctx.sigjb);
break;
case CTX_UC:
- stackp = GET_STACK_UC(&thread->curframe->ctx.uc);
+ stackp = GET_STACK_UC(&thread->ctx.uc);
break;
default:
PANIC("Invalid thread context type");
@@ -1130,138 +1041,76 @@ thread_sigframe_add(pthread_t thread, int sig)
/* Allocate room on top of the stack for a new signal frame: */
stackp -= sizeof(struct pthread_signal_frame);
- /* Set up the new frame: */
- thread->sigframe_count++;
- thread->sigframes[thread->sigframe_count] =
- (struct pthread_signal_frame *) stackp;
- thread->curframe = thread->sigframes[thread->sigframe_count];
- thread->curframe->stackp = stackp;
- thread->curframe->ctxtype = CTX_JB_NOSIG;
- thread->curframe->longjmp_val = 1;
- thread->curframe->signo = sig;
+ psf = (struct pthread_signal_frame *) stackp;
- /*
- * Set up the context:
- */
- _setjmp(thread->curframe->ctx.jb);
- SET_STACK_JB(thread->curframe->ctx.jb, stackp);
- SET_RETURN_ADDR_JB(thread->curframe->ctx.jb, _thread_sig_wrapper);
-}
+ /* Save the current context in the signal frame: */
+ thread_sigframe_save(thread, psf);
-/*
- * Locate the signal frame from the specified stack pointer.
- */
-int
-_thread_sigframe_find(pthread_t pthread, void *stackp)
-{
- int frame;
+ /* Set handler specific information: */
+ psf->sig_has_args = has_args;
+ psf->signo = sig;
+ if (has_args) {
+ /* Copy the signal handler arguments to the signal frame: */
+ memcpy(&psf->uc, &_thread_sigq[psf->signo - 1].uc,
+ sizeof(psf->uc));
+ memcpy(&psf->siginfo, &_thread_sigq[psf->signo - 1].siginfo,
+ sizeof(psf->siginfo));
+ }
+ /* Set up the new frame: */
+ thread->curframe = psf;
+ thread->ctxtype = CTX_JB_NOSIG;
+ thread->longjmp_val = 1;
+ thread->flags &= PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE |
+ PTHREAD_FLAGS_IN_SYNCQ;
/*
- * Find the destination of the target frame based on the
- * given stack pointer.
+ * Set up the context:
*/
- for (frame = pthread->sigframe_count; frame >= 0; frame--) {
- if (stackp < (void *)pthread->sigframes[frame]->stackp)
- break;
- }
- return (frame);
+ stackp += sizeof(double);
+ _setjmp(thread->ctx.jb);
+ SET_STACK_JB(thread->ctx.jb, stackp);
+ SET_RETURN_ADDR_JB(thread->ctx.jb, _thread_sig_wrapper);
}
-
+
void
-thread_sigframe_leave(pthread_t thread, int frame)
+_thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf)
{
- struct pthread_state_data *psd;
-
- psd = &thread->sigframes[frame]->saved_state;
-
+ thread->ctxtype = psf->ctxtype;
+ memcpy(&thread->ctx.uc, &psf->ctx.uc, sizeof(thread->ctx.uc));
/*
- * Perform any necessary cleanup for this signal frame:
+ * Only restore the signal mask if it hasn't been changed
+ * by the application during invocation of the signal handler:
*/
- switch (psd->psd_state) {
- case PS_DEAD:
- case PS_DEADLOCK:
- case PS_RUNNING:
- case PS_SIGTHREAD:
- case PS_STATE_MAX:
- case PS_SUSPENDED:
- break;
-
- /*
- * Threads in the following states need to be removed
- * from queues.
- */
- case PS_COND_WAIT:
- _cond_wait_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_FDLR_WAIT:
- case PS_FDLW_WAIT:
- _fd_lock_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_FILE_WAIT:
- _flockfile_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_JOIN:
- _join_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_MUTEX_WAIT:
- _mutex_lock_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- 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_WAIT_WAIT:
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0) {
- PTHREAD_WAITQ_REMOVE(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WORKQ) != 0)
- PTHREAD_WORKQ_REMOVE(thread);
- }
- break;
- }
-}
-
-static void
-thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf)
-{
- thread->interrupted = psf->saved_state.psd_interrupted;
- thread->sigmask = psf->saved_state.psd_sigmask;
- thread->state = psf->saved_state.psd_state;
- thread->flags = psf->saved_state.psd_flags;
+ if (thread->sigmask_seqno == psf->saved_state.psd_sigmask_seqno)
+ thread->sigmask = psf->saved_state.psd_sigmask;
+ thread->curframe = psf->saved_state.psd_curframe;
thread->wakeup_time = psf->saved_state.psd_wakeup_time;
thread->data = psf->saved_state.psd_wait_data;
+ thread->state = psf->saved_state.psd_state;
+ thread->flags = psf->saved_state.psd_flags;
+ thread->interrupted = psf->saved_state.psd_interrupted;
+ thread->longjmp_val = psf->saved_state.psd_longjmp_val;
+ thread->signo = psf->saved_state.psd_signo;
+ thread->sig_defer_count = psf->saved_state.psd_sig_defer_count;
}
static void
thread_sigframe_save(pthread_t thread, struct pthread_signal_frame *psf)
{
- psf->saved_state.psd_interrupted = thread->interrupted;
+ psf->ctxtype = thread->ctxtype;
+ memcpy(&psf->ctx.uc, &thread->ctx.uc, sizeof(thread->ctx.uc));
psf->saved_state.psd_sigmask = thread->sigmask;
- psf->saved_state.psd_state = thread->state;
- psf->saved_state.psd_flags = thread->flags;
- thread->flags &= PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE |
- PTHREAD_FLAGS_IN_CONDQ | PTHREAD_FLAGS_IN_MUTEXQ |
- PTHREAD_FLAGS_IN_JOINQ;
+ psf->saved_state.psd_curframe = thread->curframe;
psf->saved_state.psd_wakeup_time = thread->wakeup_time;
psf->saved_state.psd_wait_data = thread->data;
+ psf->saved_state.psd_state = thread->state;
+ psf->saved_state.psd_flags = thread->flags &
+ (PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE);
+ psf->saved_state.psd_interrupted = thread->interrupted;
+ psf->saved_state.psd_longjmp_val = thread->longjmp_val;
+ psf->saved_state.psd_sigmask_seqno = thread->sigmask_seqno;
+ psf->saved_state.psd_signo = thread->signo;
+ psf->saved_state.psd_sig_defer_count = thread->sig_defer_count;
}
#endif
diff --git a/lib/libc_r/uthread/uthread_sigaction.c b/lib/libc_r/uthread/uthread_sigaction.c
index e78f329..4d13819 100644
--- a/lib/libc_r/uthread/uthread_sigaction.c
+++ b/lib/libc_r/uthread/uthread_sigaction.c
@@ -80,7 +80,7 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
* handler arguments.
*/
sigfillset(&gact.sa_mask);
- gact.sa_flags = SA_SIGINFO;
+ gact.sa_flags = SA_SIGINFO | SA_ONSTACK;
/*
* Check if the signal handler is being set to
diff --git a/lib/libc_r/uthread/uthread_sigmask.c b/lib/libc_r/uthread/uthread_sigmask.c
index bdb0b43..53d0774 100644
--- a/lib/libc_r/uthread/uthread_sigmask.c
+++ b/lib/libc_r/uthread/uthread_sigmask.c
@@ -81,6 +81,9 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
break;
}
+ /* Increment the sequence number: */
+ _thread_run->sigmask_seqno++;
+
/*
* Check if there are pending signals for the running
* thread or process that aren't blocked:
diff --git a/lib/libkse/thread/thr_cond.c b/lib/libkse/thread/thr_cond.c
index 50cf927..5ff0967 100644
--- a/lib/libkse/thread/thr_cond.c
+++ b/lib/libkse/thread/thr_cond.c
@@ -47,7 +47,7 @@ static inline void cond_queue_enq(pthread_cond_t, pthread_t);
/* Reinitialize a condition variable to defaults. */
int
-_cond_reinit(pthread_cond_t * cond)
+_cond_reinit(pthread_cond_t *cond)
{
int ret = 0;
@@ -63,13 +63,14 @@ _cond_reinit(pthread_cond_t * cond)
(*cond)->c_flags = COND_FLAGS_INITED;
(*cond)->c_type = COND_TYPE_FAST;
(*cond)->c_mutex = NULL;
+ (*cond)->c_seqno = 0;
memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
}
return (ret);
}
int
-pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
+pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
{
enum pthread_cond_type type;
pthread_cond_t pcond;
@@ -118,6 +119,7 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
pcond->c_flags |= COND_FLAGS_INITED;
pcond->c_type = type;
pcond->c_mutex = NULL;
+ pcond->c_seqno = 0;
memset(&pcond->lock,0,sizeof(pcond->lock));
*cond = pcond;
}
@@ -128,7 +130,7 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
}
int
-pthread_cond_destroy(pthread_cond_t * cond)
+pthread_cond_destroy(pthread_cond_t *cond)
{
int rval = 0;
@@ -155,22 +157,37 @@ pthread_cond_destroy(pthread_cond_t * cond)
}
int
-pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
+pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
int rval = 0;
+ int done = 0;
int interrupted = 0;
+ int unlock_mutex = 1;
+ int seqno;
_thread_enter_cancellation_point();
if (cond == NULL)
- rval = EINVAL;
+ return (EINVAL);
/*
* If the condition variable is statically initialized,
* perform the dynamic initialization:
*/
- else if (*cond != NULL ||
- (rval = pthread_cond_init(cond, NULL)) == 0) {
+ if (*cond == NULL &&
+ (rval = pthread_cond_init(cond, NULL)) != 0)
+ return (rval);
+
+ /*
+ * Enter a loop waiting for a condition signal or broadcast
+ * to wake up this thread. A loop is needed in case the waiting
+ * thread is interrupted by a signal to execute a signal handler.
+ * It is not (currently) possible to remain in the waiting queue
+ * while running a handler. Instead, the thread is interrupted
+ * and backed out of the waiting queue prior to executing the
+ * signal handler.
+ */
+ do {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@@ -205,14 +222,16 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
*/
cond_queue_enq(*cond, _thread_run);
- /* Remember the mutex that is being used: */
+ /* Remember the mutex and sequence number: */
(*cond)->c_mutex = *mutex;
+ seqno = (*cond)->c_seqno;
/* Wait forever: */
_thread_run->wakeup_time.tv_sec = -1;
/* Unlock the mutex: */
- if ((rval = _mutex_cv_unlock(mutex)) != 0) {
+ if ((unlock_mutex != 0) &&
+ ((rval = _mutex_cv_unlock(mutex)) != 0)) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
@@ -230,19 +249,23 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
}
else {
/*
+ * Don't unlock the mutex in the event
+ * this thread has to be requeued in
+ * condition variable queue:
+ */
+ unlock_mutex = 0;
+
+ /*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
- if (_thread_run->interrupted != 0) {
- /*
- * Remember that this thread
- * was interrupted:
- */
- interrupted = 1;
+ done = (seqno != (*cond)->c_seqno);
+ if ((_thread_run->flags &
+ PTHREAD_FLAGS_IN_CONDQ) != 0) {
/*
* Lock the condition variable
* while removing the thread.
@@ -260,6 +283,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
}
/*
+ * Save the interrupted flag; locking
+ * the mutex will destroy it.
+ */
+ interrupted = _thread_run->interrupted;
+
+ /*
* Note that even though this thread may have
* been canceled, POSIX requires that the mutex
* be reaquired prior to cancellation.
@@ -279,11 +308,9 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
break;
}
- if (interrupted != 0) {
- if (_thread_run->continuation != NULL)
- _thread_run->continuation((void *) _thread_run);
- }
- }
+ if ((interrupted != 0) && (_thread_run->continuation != NULL))
+ _thread_run->continuation((void *) _thread_run);
+ } while ((done == 0) && (rval == 0));
_thread_leave_cancellation_point();
@@ -296,18 +323,33 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
const struct timespec * abstime)
{
int rval = 0;
+ int done = 0;
int interrupted = 0;
+ int unlock_mutex = 1;
+ int seqno;
_thread_enter_cancellation_point();
if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
abstime->tv_nsec >= 1000000000)
- rval = EINVAL;
+ return (EINVAL);
/*
* If the condition variable is statically initialized, perform dynamic
* initialization.
*/
- else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
+ if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
+ return (rval);
+
+ /*
+ * Enter a loop waiting for a condition signal or broadcast
+ * to wake up this thread. A loop is needed in case the waiting
+ * thread is interrupted by a signal to execute a signal handler.
+ * It is not (currently) possible to remain in the waiting queue
+ * while running a handler. Instead, the thread is interrupted
+ * and backed out of the waiting queue prior to executing the
+ * signal handler.
+ */
+ do {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@@ -348,11 +390,13 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
*/
cond_queue_enq(*cond, _thread_run);
- /* Remember the mutex that is being used: */
+ /* Remember the mutex and sequence number: */
(*cond)->c_mutex = *mutex;
+ seqno = (*cond)->c_seqno;
/* Unlock the mutex: */
- if ((rval = _mutex_cv_unlock(mutex)) != 0) {
+ if ((unlock_mutex != 0) &&
+ ((rval = _mutex_cv_unlock(mutex)) != 0)) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
@@ -368,35 +412,39 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
_SPINUNLOCK(&(*cond)->lock);
} else {
/*
+ * Don't unlock the mutex in the event
+ * this thread has to be requeued in
+ * condition variable queue:
+ */
+ unlock_mutex = 0;
+
+ /*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
+ done = (seqno != (*cond)->c_seqno);
+
/*
- * Check if the wait timedout or was
- * interrupted (canceled):
+ * Check if the wait timedout, was
+ * interrupted (canceled), or needs to
+ * be resumed after handling a signal.
*/
if ((_thread_run->timeout == 0) &&
- (_thread_run->interrupted == 0)) {
+ (_thread_run->interrupted == 0) &&
+ (done != 0)) {
/* Lock the mutex: */
rval = _mutex_cv_lock(mutex);
-
} else {
- /*
- * Remember if this thread was
- * interrupted:
- */
- interrupted = _thread_run->interrupted;
-
- /* Lock the condition variable structure: */
+ /* Lock the CV structure: */
_SPINLOCK(&(*cond)->lock);
/*
* The wait timed out; remove
* the thread from the condition
- * variable queue:
+ * variable queue:
*/
cond_queue_remove(*cond,
_thread_run);
@@ -405,11 +453,18 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
- /* Unock the condition variable structure: */
+ /* Unock the CV structure: */
_SPINUNLOCK(&(*cond)->lock);
/* Return a timeout error: */
- rval = ETIMEDOUT;
+ if (_thread_run->timeout != 0)
+ rval = ETIMEDOUT;
+ /*
+ * Save the interrupted flag;
+ * locking the mutex will
+ * destroy it.
+ */
+ interrupted = _thread_run->interrupted;
/*
* Lock the mutex and ignore any
@@ -435,11 +490,9 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
break;
}
- if (interrupted != 0) {
- if (_thread_run->continuation != NULL)
- _thread_run->continuation((void *) _thread_run);
- }
- }
+ if ((interrupted != 0) && (_thread_run->continuation != NULL))
+ _thread_run->continuation((void *) _thread_run);
+ } while ((done == 0) && (rval == 0));
_thread_leave_cancellation_point();
@@ -473,6 +526,9 @@ pthread_cond_signal(pthread_cond_t * cond)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
+ /* Increment the sequence number: */
+ (*cond)->c_seqno++;
+
if ((pthread = cond_queue_deq(*cond)) != NULL) {
/*
* Unless the thread is currently suspended,
@@ -538,6 +594,9 @@ pthread_cond_broadcast(pthread_cond_t * cond)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
+ /* Increment the sequence number: */
+ (*cond)->c_seqno++;
+
/*
* Enter a loop to bring all threads off the
* condition queue:
diff --git a/lib/libkse/thread/thr_create.c b/lib/libkse/thread/thr_create.c
index 0390f1b..430869e 100644
--- a/lib/libkse/thread/thr_create.c
+++ b/lib/libkse/thread/thr_create.c
@@ -49,16 +49,13 @@
static u_int64_t next_uniqueid = 1;
#define OFF(f) offsetof(struct pthread, f)
-#define SIGFRAME_OFF(f) offsetof(struct pthread_signal_frame, f)
int _thread_next_offset = OFF(tle.tqe_next);
int _thread_uniqueid_offset = OFF(uniqueid);
int _thread_state_offset = OFF(state);
int _thread_name_offset = OFF(name);
-int _thread_curframe_offset = OFF(curframe);
-int _thread_sigframe_ctx_offset = SIGFRAME_OFF(ctx);
-int _thread_sigframe_ctxtype_offset = SIGFRAME_OFF(ctxtype);
+int _thread_ctxtype_offset = OFF(ctxtype);
+int _thread_ctx_offset = OFF(ctx);
#undef OFF
-#undef SIGFRAME_OFF
int _thread_PS_RUNNING_value = PS_RUNNING;
int _thread_PS_DEAD_value = PS_DEAD;
@@ -66,12 +63,12 @@ int _thread_CTX_JB_NOSIG_value = CTX_JB_NOSIG;
int _thread_CTX_JB_value = CTX_JB;
int _thread_CTX_SJB_value = CTX_SJB;
int _thread_CTX_UC_value = CTX_UC;
-int _thread_sigframe_size_value = sizeof(struct pthread_signal_frame);
int
pthread_create(pthread_t * thread, const pthread_attr_t * attr,
void *(*start_routine) (void *), void *arg)
{
+ struct itimerval itimer;
int f_gc = 0;
int ret = 0;
pthread_t gc_thread;
@@ -127,7 +124,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
} else {
/* Allocate a new stack. */
stack = _next_stack + PTHREAD_STACK_GUARD;
-
+
/*
* Even if stack allocation fails, we don't want
* to try to use this location again, so
@@ -184,40 +181,35 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Initialise the thread for signals: */
new_thread->sigmask = _thread_run->sigmask;
+ new_thread->sigmask_seqno = 0;
- /* Initialize the first signal frame: */
- new_thread->sigframes[0] = &new_thread->sigframe0;
- new_thread->curframe = &new_thread->sigframe0;
+ /* Initialize the signal frame: */
+ new_thread->curframe = NULL;
/* Initialise the jump buffer: */
- _setjmp(new_thread->curframe->ctx.jb);
+ _setjmp(new_thread->ctx.jb);
/*
* Set up new stack frame so that it looks like it
* returned from a longjmp() to the beginning of
* _thread_start().
*/
- SET_RETURN_ADDR_JB(new_thread->curframe->ctx.jb,
- _thread_start);
+ SET_RETURN_ADDR_JB(new_thread->ctx.jb, _thread_start);
/* The stack starts high and builds down: */
- SET_STACK_JB(new_thread->curframe->ctx.jb,
+ SET_STACK_JB(new_thread->ctx.jb,
(long)new_thread->stack + pattr->stacksize_attr
- sizeof(double));
/* Initialize the rest of the frame: */
- new_thread->curframe->ctxtype = CTX_JB_NOSIG;
- /* Set the base of the stack: */
- new_thread->curframe->stackp =
- GET_STACK_JB(new_thread->curframe->ctx.jb);
- new_thread->sigframe_count = 0;
+ new_thread->ctxtype = CTX_JB_NOSIG;
/* Copy the thread attributes: */
memcpy(&new_thread->attr, pattr, sizeof(struct pthread_attr));
/*
* Check if this thread is to inherit the scheduling
- * attributes from its parent:
+ * attributes from its parent:
*/
if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
/* Copy the scheduling attributes: */
@@ -233,7 +225,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/*
* Use just the thread priority, leaving the
* other scheduling attributes as their
- * default values:
+ * default values:
*/
new_thread->base_priority =
new_thread->attr.prio;
@@ -292,8 +284,19 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Return a pointer to the thread structure: */
(*thread) = new_thread;
+ if (f_gc != 0) {
+ /* Install the scheduling timer: */
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = _clock_res_usec;
+ itimer.it_value = itimer.it_interval;
+ if (setitimer(_ITIMER_SCHED_TIMER, &itimer,
+ NULL) != 0)
+ PANIC("Cannot set interval timer");
+ }
+
/* Schedule the new user thread: */
_thread_kern_sched(NULL);
+
/*
* Start a garbage collector thread
* if necessary.
diff --git a/lib/libkse/thread/thr_detach.c b/lib/libkse/thread/thr_detach.c
index 3bade9d..6dd762a 100644
--- a/lib/libkse/thread/thr_detach.c
+++ b/lib/libkse/thread/thr_detach.c
@@ -65,7 +65,12 @@ pthread_detach(pthread_t pthread)
pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
/* Make the thread runnable: */
- PTHREAD_NEW_STATE(next_thread,PS_RUNNING);
+ PTHREAD_NEW_STATE(next_thread, PS_RUNNING);
+
+ /*
+ * Set the return value for the woken thread:
+ */
+ next_thread->error = ESRCH;
}
/*
diff --git a/lib/libkse/thread/thr_exit.c b/lib/libkse/thread/thr_exit.c
index 7fbeb65..aef12fe 100644
--- a/lib/libkse/thread/thr_exit.c
+++ b/lib/libkse/thread/thr_exit.c
@@ -141,7 +141,7 @@ _thread_exit_cleanup(void)
void
pthread_exit(void *status)
{
- int frame;
+ pthread_t pthread;
/* Check if this thread is already in the process of exiting: */
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
@@ -159,7 +159,6 @@ pthread_exit(void *status)
while (_thread_run->cleanup != NULL) {
pthread_cleanup_pop(1);
}
-
if (_thread_run->attr.cleanup_attr != NULL) {
_thread_run->attr.cleanup_attr(_thread_run->attr.arg_attr);
}
@@ -175,25 +174,6 @@ pthread_exit(void *status)
_thread_run->poll_data.fds = NULL;
}
- if ((frame = _thread_run->sigframe_count) == 0)
- _thread_exit_finish();
- else {
- /*
- * Jump back and unwind the signal frames to gracefully
- * cleanup.
- */
- ___longjmp(*_thread_run->sigframes[frame]->sig_jb, 1);
- }
-
- /* This point should not be reached. */
- PANIC("Dead thread has resumed");
-}
-
-void
-_thread_exit_finish(void)
-{
- pthread_t pthread;
-
/*
* Lock the garbage collector mutex to ensure that the garbage
* collector is not using the dead thread list.
@@ -233,6 +213,16 @@ _thread_exit_finish(void)
* detach this thread:
*/
PTHREAD_NEW_STATE(pthread, PS_RUNNING);
+
+ /*
+ * Set the return value for the woken thread:
+ */
+ if ((_thread_run->attr.flags & PTHREAD_DETACHED) != 0)
+ pthread->error = ESRCH;
+ else {
+ pthread->ret = _thread_run->ret;
+ pthread->error = 0;
+ }
}
/* Remove this thread from the thread list: */
@@ -240,5 +230,8 @@ _thread_exit_finish(void)
/* This thread will never be re-scheduled. */
_thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__);
+
+ /* This point should not be reached. */
+ PANIC("Dead thread has resumed");
}
#endif
diff --git a/lib/libkse/thread/thr_info.c b/lib/libkse/thread/thr_info.c
index ca91512..956ae7c 100644
--- a/lib/libkse/thread/thr_info.c
+++ b/lib/libkse/thread/thr_info.c
@@ -41,6 +41,13 @@
#include <errno.h>
#include "pthread_private.h"
+#ifndef NELEMENTS
+#define NELEMENTS(arr) (sizeof(arr) / sizeof(arr[0]))
+#endif
+
+static void dump_thread(int fd, pthread_t pthread, int long_version);
+
+
struct s_thread_info {
enum pthread_state state;
char *name;
@@ -77,12 +84,11 @@ _thread_dump_info(void)
char s[512];
int fd;
int i;
- int j;
pthread_t pthread;
char tmpfile[128];
pq_list_t *pq_list;
- for (i = 0; i < 100000; i++) {
+ for (i = 0; i < 100000; i++) {
snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i",
getpid(), i);
/* Open the dump file for append and create it if necessary: */
@@ -112,70 +118,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the global list: */
TAILQ_FOREACH(pthread, &_thread_list, tle) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
-
- /* Check if this is the running thread: */
- if (pthread == _thread_run) {
- /* Output a record for the running thread: */
- strcpy(s, "This is the running thread\n");
- _thread_sys_write(fd, s, strlen(s));
- }
- /* Check if this is the initial thread: */
- if (pthread == _thread_initial) {
- /* Output a record for the initial thread: */
- strcpy(s, "This is the initial thread\n");
- _thread_sys_write(fd, s, strlen(s));
- }
- /* Process according to thread state: */
- switch (pthread->state) {
- /* File descriptor read lock wait: */
- case PS_FDLR_WAIT:
- case PS_FDLW_WAIT:
- case PS_FDR_WAIT:
- case PS_FDW_WAIT:
- /* Write the lock details: */
- snprintf(s, sizeof(s), "fd %d[%s:%d]",
- pthread->data.fd.fd,
- pthread->data.fd.fname,
- pthread->data.fd.branch);
- _thread_sys_write(fd, s, strlen(s));
- snprintf(s, sizeof(s), "owner %pr/%pw\n",
- _thread_fd_table[pthread->data.fd.fd]->r_owner,
- _thread_fd_table[pthread->data.fd.fd]->w_owner);
- _thread_sys_write(fd, s, strlen(s));
- break;
- case PS_SIGWAIT:
- snprintf(s, sizeof(s), "sigmask (hi)");
- _thread_sys_write(fd, s, strlen(s));
- for (i = _SIG_WORDS - 1; i >= 0; i--) {
- snprintf(s, sizeof(s), "%08x\n",
- pthread->sigmask.__bits[i]);
- _thread_sys_write(fd, s, strlen(s));
- }
- snprintf(s, sizeof(s), "(lo)\n");
- _thread_sys_write(fd, s, strlen(s));
- break;
-
- /*
- * Trap other states that are not explicitly
- * coded to dump information:
- */
- default:
- /* Nothing to do here. */
- break;
- }
+ dump_thread(fd, pthread, /*long_verson*/ 1);
}
/* Output a header for ready threads: */
@@ -185,19 +128,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the ready queue: */
TAILQ_FOREACH (pq_list, &_readyq.pq_queue, pl_link) {
TAILQ_FOREACH(pthread, &pq_list->pl_head, pqe) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
}
@@ -207,19 +138,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the waiting queue: */
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
/* Output a header for threads in the work queue: */
@@ -228,19 +147,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the waiting queue: */
TAILQ_FOREACH (pthread, &_workq, qe) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
/* Check if there are no dead threads: */
@@ -255,42 +162,38 @@ _thread_dump_info(void)
/*
* Enter a loop to report each thread in the global
- * dead thread list:
+ * dead thread list:
*/
TAILQ_FOREACH(pthread, &_dead_list, dle) {
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "Thread %p prio %3d [%s:%d]\n",
- pthread, pthread->base_priority,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
}
/* Output a header for file descriptors: */
- snprintf(s, sizeof(s), "\n\n=============\nFILE DESCRIPTOR TABLE (table size %d)\n\n",_thread_dtablesize);
+ snprintf(s, sizeof(s), "\n\n=============\nFILE DESCRIPTOR "
+ "TABLE (table size %d)\n\n", _thread_dtablesize);
_thread_sys_write(fd, s, strlen(s));
/* Enter a loop to report file descriptor lock usage: */
for (i = 0; i < _thread_dtablesize; i++) {
/*
* Check if memory is allocated for this file
- * descriptor:
+ * descriptor:
*/
if (_thread_fd_table[i] != NULL) {
/* Report the file descriptor lock status: */
snprintf(s, sizeof(s),
- "fd[%3d] read owner %p count %d [%s:%d]\n write owner %p count %d [%s:%d]\n",
- i,
- _thread_fd_table[i]->r_owner,
- _thread_fd_table[i]->r_lockcount,
- _thread_fd_table[i]->r_fname,
- _thread_fd_table[i]->r_lineno,
- _thread_fd_table[i]->w_owner,
- _thread_fd_table[i]->w_lockcount,
- _thread_fd_table[i]->w_fname,
- _thread_fd_table[i]->w_lineno);
- _thread_sys_write(fd, s, strlen(s));
+ "fd[%3d] read owner %p count %d [%s:%d]\n"
+ " write owner %p count %d [%s:%d]\n",
+ i, _thread_fd_table[i]->r_owner,
+ _thread_fd_table[i]->r_lockcount,
+ _thread_fd_table[i]->r_fname,
+ _thread_fd_table[i]->r_lineno,
+ _thread_fd_table[i]->w_owner,
+ _thread_fd_table[i]->w_lockcount,
+ _thread_fd_table[i]->w_fname,
+ _thread_fd_table[i]->w_lineno);
+ _thread_sys_write(fd, s, strlen(s));
}
}
@@ -299,6 +202,78 @@ _thread_dump_info(void)
}
}
+static void
+dump_thread(int fd, pthread_t pthread, int long_version)
+{
+ char s[512];
+ int i;
+
+ /* Find the state: */
+ for (i = 0; i < NELEMENTS(thread_info) - 1; i++)
+ if (thread_info[i].state == pthread->state)
+ break;
+
+ /* Output a record for the thread: */
+ snprintf(s, sizeof(s),
+ "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
+ pthread, (pthread->name == NULL) ? "" : pthread->name,
+ pthread->active_priority, thread_info[i].name, pthread->fname,
+ pthread->lineno);
+ _thread_sys_write(fd, s, strlen(s));
+
+ if (long_version != 0) {
+ /* Check if this is the running thread: */
+ if (pthread == _thread_run) {
+ /* Output a record for the running thread: */
+ strcpy(s, "This is the running thread\n");
+ _thread_sys_write(fd, s, strlen(s));
+ }
+ /* Check if this is the initial thread: */
+ if (pthread == _thread_initial) {
+ /* Output a record for the initial thread: */
+ strcpy(s, "This is the initial thread\n");
+ _thread_sys_write(fd, s, strlen(s));
+ }
+ /* Process according to thread state: */
+ switch (pthread->state) {
+ /* File descriptor read lock wait: */
+ case PS_FDLR_WAIT:
+ case PS_FDLW_WAIT:
+ case PS_FDR_WAIT:
+ case PS_FDW_WAIT:
+ /* Write the lock details: */
+ snprintf(s, sizeof(s), "fd %d[%s:%d]",
+ pthread->data.fd.fd,
+ pthread->data.fd.fname,
+ pthread->data.fd.branch);
+ _thread_sys_write(fd, s, strlen(s));
+ snprintf(s, sizeof(s), "owner %pr/%pw\n",
+ _thread_fd_table[pthread->data.fd.fd]->r_owner,
+ _thread_fd_table[pthread->data.fd.fd]->w_owner);
+ _thread_sys_write(fd, s, strlen(s));
+ break;
+ case PS_SIGWAIT:
+ snprintf(s, sizeof(s), "sigmask (hi)");
+ _thread_sys_write(fd, s, strlen(s));
+ for (i = _SIG_WORDS - 1; i >= 0; i--) {
+ snprintf(s, sizeof(s), "%08x\n",
+ pthread->sigmask.__bits[i]);
+ _thread_sys_write(fd, s, strlen(s));
+ }
+ snprintf(s, sizeof(s), "(lo)\n");
+ _thread_sys_write(fd, s, strlen(s));
+ break;
+ /*
+ * Trap other states that are not explicitly
+ * coded to dump information:
+ */
+ default:
+ /* Nothing to do here. */
+ break;
+ }
+ }
+}
+
/* Set the thread name for debug: */
void
pthread_set_name_np(pthread_t thread, char *name)
diff --git a/lib/libkse/thread/thr_init.c b/lib/libkse/thread/thr_init.c
index 3cbd453..35731c4 100644
--- a/lib/libkse/thread/thr_init.c
+++ b/lib/libkse/thread/thr_init.c
@@ -92,7 +92,7 @@ _thread_init(void)
int mib[2];
struct clockinfo clockinfo;
struct sigaction act;
- struct itimerval itimer;
+ struct sigaltstack alt;
/* Check if this function has already been called: */
if (_thread_initial)
@@ -133,7 +133,7 @@ _thread_init(void)
/*
* Create a pipe that is written to by the signal handler to prevent
- * signals being missed in calls to _select:
+ * signals being missed in calls to _select:
*/
if (_thread_sys_pipe(_thread_kern_pipe) != 0) {
/* Cannot create pipe, so abort: */
@@ -168,12 +168,12 @@ _thread_init(void)
else if ((_thread_initial = (pthread_t) malloc(sizeof(struct pthread))) == NULL) {
/*
* Insufficient memory to initialise this application, so
- * abort:
+ * abort:
*/
PANIC("Cannot allocate memory for initial thread");
}
/* Allocate memory for the scheduler stack: */
- else if ((_thread_kern_sched_stack = malloc(PAGE_SIZE * 10)) == NULL)
+ else if ((_thread_kern_sched_stack = malloc(SCHED_STACK_SIZE)) == NULL)
PANIC("Failed to allocate stack for scheduler");
else {
/* Zero the global kernel thread structure: */
@@ -217,8 +217,8 @@ _thread_init(void)
/* Setup the context for the scheduler: */
_setjmp(_thread_kern_sched_jb);
- SET_STACK_JB(_thread_kern_sched_jb,
- _thread_kern_sched_stack + PAGE_SIZE*10 - sizeof(double));
+ SET_STACK_JB(_thread_kern_sched_jb, _thread_kern_sched_stack +
+ SCHED_STACK_SIZE - sizeof(double));
SET_RETURN_ADDR_JB(_thread_kern_sched_jb, _thread_kern_scheduler);
/*
@@ -253,12 +253,9 @@ _thread_init(void)
/* Initialize last active: */
_thread_initial->last_active = (long) _sched_ticks;
- /* Initialize the initial signal frame: */
- _thread_initial->sigframes[0] = &_thread_initial->sigframe0;
- _thread_initial->curframe = &_thread_initial->sigframe0;
- _thread_initial->curframe->ctxtype = CTX_JB_NOSIG;
- /* Set the base of the stack: */
- _thread_initial->curframe->stackp = (unsigned long) USRSTACK;
+ /* Initialize the initial context: */
+ _thread_initial->curframe = NULL;
+ _thread_initial->ctxtype = CTX_JB_NOSIG;
/* Initialise the rest of the fields: */
_thread_initial->poll_data.nfds = 0;
@@ -276,7 +273,7 @@ _thread_init(void)
/* Initialise the global signal action structure: */
sigfillset(&act.sa_mask);
act.sa_handler = (void (*) ()) _thread_sig_handler;
- act.sa_flags = SA_SIGINFO;
+ act.sa_flags = SA_SIGINFO | SA_ONSTACK;
/* Clear pending signals for the process: */
sigemptyset(&_process_sigpending);
@@ -284,6 +281,13 @@ _thread_init(void)
/* Clear the signal queue: */
memset(_thread_sigq, 0, sizeof(_thread_sigq));
+ /* Create and install an alternate signal stack: */
+ alt.ss_sp = malloc(SIGSTKSZ); /* recommended stack size */
+ alt.ss_size = SIGSTKSZ;
+ alt.ss_flags = 0;
+ if (_thread_sys_sigaltstack(&alt, NULL) != 0)
+ PANIC("Unable to install alternate signal stack");
+
/* Enter a loop to get the existing signal status: */
for (i = 1; i < NSIG; i++) {
/* Check for signals which cannot be trapped: */
@@ -295,7 +299,7 @@ _thread_init(void)
&_thread_sigact[i - 1]) != 0) {
/*
* Abort this process if signal
- * initialisation fails:
+ * initialisation fails:
*/
PANIC("Cannot read signal handler info");
}
@@ -313,7 +317,7 @@ _thread_init(void)
_thread_sys_sigaction(SIGINFO, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGCHLD, &act, NULL) != 0) {
/*
- * Abort this process if signal initialisation fails:
+ * Abort this process if signal initialisation fails:
*/
PANIC("Cannot initialise signal handler");
}
@@ -335,7 +339,7 @@ _thread_init(void)
if ((_thread_dtablesize = getdtablesize()) < 0) {
/*
* Cannot get the system defined table size, so abort
- * this process.
+ * this process.
*/
PANIC("Cannot get dtablesize");
}
@@ -346,7 +350,7 @@ _thread_init(void)
/*
* Cannot allocate memory for the file descriptor
- * table, so abort this process.
+ * table, so abort this process.
*/
PANIC("Cannot allocate memory for file descriptor table");
}
@@ -354,13 +358,13 @@ _thread_init(void)
if ((_thread_pfd_table = (struct pollfd *) malloc(sizeof(struct pollfd) * _thread_dtablesize)) == NULL) {
/*
* Cannot allocate memory for the file descriptor
- * table, so abort this process.
+ * table, so abort this process.
*/
PANIC("Cannot allocate memory for pollfd table");
} else {
/*
* Enter a loop to initialise the file descriptor
- * table:
+ * table:
*/
for (i = 0; i < _thread_dtablesize; i++) {
/* Initialise the file descriptor table: */
@@ -374,14 +378,6 @@ _thread_init(void)
PANIC("Cannot initialize stdio file "
"descriptor table entry");
}
-
- /* Install the scheduling timer: */
- itimer.it_interval.tv_sec = 0;
- itimer.it_interval.tv_usec = _clock_res_usec;
- itimer.it_value = itimer.it_interval;
- if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0)
- PANIC("Cannot set interval timer");
-
}
}
@@ -401,10 +397,10 @@ _thread_init(void)
}
/*
- * Special start up code for NetBSD/Alpha
+ * Special start up code for NetBSD/Alpha
*/
#if defined(__NetBSD__) && defined(__alpha__)
-int
+int
main(int argc, char *argv[], char *env);
int
diff --git a/lib/libkse/thread/thr_join.c b/lib/libkse/thread/thr_join.c
index cda31bd..b4a7c61 100644
--- a/lib/libkse/thread/thr_join.c
+++ b/lib/libkse/thread/thr_join.c
@@ -74,46 +74,74 @@ pthread_join(pthread_t pthread, void **thread_return)
else if (pthread->state != PS_DEAD) {
PTHREAD_ASSERT_NOT_IN_SYNCQ(_thread_run);
- /* Clear the interrupted flag: */
- _thread_run->interrupted = 0;
-
/*
- * Protect against being context switched out while
- * adding this thread to the join queue.
+ * Enter a loop in case this thread is woken prematurely
+ * in order to invoke a signal handler:
*/
- _thread_kern_sig_defer();
-
- /* Add the running thread to the join queue: */
- TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, sqe);
- _thread_run->flags |= PTHREAD_FLAGS_IN_JOINQ;
- _thread_run->data.thread = pthread;
-
- /* Schedule the next thread: */
- _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
-
- if (_thread_run->interrupted != 0) {
- TAILQ_REMOVE(&(pthread->join_queue), _thread_run, sqe);
- _thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
+ for (;;) {
+ /* Clear the interrupted flag: */
+ _thread_run->interrupted = 0;
+
+ /*
+ * Protect against being context switched out while
+ * adding this thread to the join queue.
+ */
+ _thread_kern_sig_defer();
+
+ /* Add the running thread to the join queue: */
+ TAILQ_INSERT_TAIL(&(pthread->join_queue),
+ _thread_run, sqe);
+ _thread_run->flags |= PTHREAD_FLAGS_IN_JOINQ;
+ _thread_run->data.thread = pthread;
+
+ /* Schedule the next thread: */
+ _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
+
+ if ((_thread_run->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
+ TAILQ_REMOVE(&(pthread->join_queue),
+ _thread_run, sqe);
+ _thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
+ }
+ _thread_run->data.thread = NULL;
+
+ _thread_kern_sig_undefer();
+
+ if (_thread_run->interrupted != 0) {
+ if (_thread_run->continuation != NULL)
+ _thread_run->continuation(_thread_run);
+ /*
+ * This thread was interrupted, probably to
+ * invoke a signal handler. Make sure the
+ * target thread is still joinable.
+ */
+ if (((_find_thread(pthread) != 0) &&
+ (_find_dead_thread(pthread) != 0)) ||
+ ((pthread->attr.flags &
+ PTHREAD_DETACHED) != 0)) {
+ /* Return an error: */
+ ret = ESRCH;
+
+ /* We're done; break out of the loop. */
+ break;
+ }
+ else if (pthread->state == PS_DEAD) {
+ /* We're done; break out of the loop. */
+ break;
+ }
+ } else {
+ /*
+ * The thread return value and error are set
+ * by the thread we're joining to when it
+ * exits or detaches:
+ */
+ ret = _thread_run->error;
+ if ((ret == 0) && (thread_return != NULL))
+ *thread_return = _thread_run->ret;
+
+ /* We're done; break out of the loop. */
+ break;
+ }
}
- _thread_run->data.thread = NULL;
-
- _thread_kern_sig_undefer();
-
- 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) {
- /* Check if the return value is required: */
- if (thread_return)
- /* Return the thread's return value: */
- *thread_return = pthread->ret;
- }
- else
- /* Return an error: */
- ret = ESRCH;
-
/* Check if the return value is required: */
} else if (thread_return != NULL)
/* Return the thread's return value: */
@@ -129,7 +157,7 @@ void
_join_backout(pthread_t pthread)
{
_thread_kern_sig_defer();
- if (pthread->state == PS_JOIN) {
+ if ((pthread->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
TAILQ_REMOVE(&pthread->data.thread->join_queue, pthread, sqe);
_thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
}
diff --git a/lib/libkse/thread/thr_kern.c b/lib/libkse/thread/thr_kern.c
index 23f16bc..67844e1 100644
--- a/lib/libkse/thread/thr_kern.c
+++ b/lib/libkse/thread/thr_kern.c
@@ -60,7 +60,7 @@
#endif
/* Static function prototype definitions: */
-static void
+static void
thread_kern_poll(int wait_reqd);
static void
@@ -77,7 +77,7 @@ static int last_tick = 0;
* return to a previous frame.
*/
void
-_thread_kern_sched_frame(int frame)
+_thread_kern_sched_frame(struct pthread_signal_frame *psf)
{
/*
* Flag the pthread kernel as executing scheduler code
@@ -86,13 +86,8 @@ _thread_kern_sched_frame(int frame)
*/
_thread_kern_in_sched = 1;
- /* Return to the specified frame: */
- _thread_run->curframe = _thread_run->sigframes[frame];
- _thread_run->sigframe_count = frame;
-
- if (_thread_run->sigframe_count == 0)
- /* Restore the threads priority: */
- _thread_run->active_priority &= ~PTHREAD_SIGNAL_PRIORITY;
+ /* Restore the signal frame: */
+ _thread_sigframe_restore(_thread_run, psf);
/* Switch to the thread scheduler: */
___longjmp(_thread_kern_sched_jb, 1);
@@ -127,16 +122,16 @@ _thread_kern_sched(ucontext_t *scp)
_thread_kern_scheduler();
} else {
/* Save the state of the current thread: */
- if (_setjmp(_thread_run->curframe->ctx.jb) == 0) {
+ if (_setjmp(_thread_run->ctx.jb) == 0) {
/* Flag the jump buffer was the last state saved: */
- _thread_run->curframe->ctxtype = CTX_JB_NOSIG;
- _thread_run->curframe->longjmp_val = 1;
+ _thread_run->ctxtype = CTX_JB_NOSIG;
+ _thread_run->longjmp_val = 1;
} else {
DBG_MSG("Returned from ___longjmp, thread %p\n",
_thread_run);
/*
* This point is reached when a longjmp() is called
- * to restore the state of a thread.
+ * to restore the state of a thread.
*
* This is the normal way out of the scheduler.
*/
@@ -147,7 +142,7 @@ _thread_kern_sched(ucontext_t *scp)
PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags &
PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
- /*
+ /*
* Cancellations override signals.
*
* Stick a cancellation point at the
@@ -183,7 +178,6 @@ _thread_kern_sched_sig(void)
void
_thread_kern_scheduler(void)
{
- struct pthread_signal_frame *psf;
struct timespec ts;
struct timeval tv;
pthread_t pthread, pthread_h;
@@ -205,7 +199,7 @@ _thread_kern_scheduler(void)
* ready to run. This loop completes when there are no more threads
* in the global list or when a thread has its state restored by
* either a sigreturn (if the state was saved as a sigcontext) or a
- * longjmp (if the state was saved by a setjmp).
+ * longjmp (if the state was saved by a setjmp).
*/
while (!(TAILQ_EMPTY(&_thread_list))) {
/* Get the current time of day: */
@@ -229,7 +223,7 @@ _thread_kern_scheduler(void)
if (_thread_run->state != PS_RUNNING) {
/*
* Save the current time as the time that the
- * thread became inactive:
+ * thread became inactive:
*/
_thread_run->last_inactive = (long)current_tick;
if (_thread_run->last_inactive <
@@ -266,7 +260,7 @@ _thread_kern_scheduler(void)
/*
* States which do not depend on file descriptor I/O
- * operations or timeouts:
+ * operations or timeouts:
*/
case PS_DEADLOCK:
case PS_FDLR_WAIT:
@@ -326,10 +320,16 @@ _thread_kern_scheduler(void)
}
/*
+ * Avoid polling file descriptors if there are none
+ * waiting:
+ */
+ if (TAILQ_EMPTY(&_workq) == 0) {
+ }
+ /*
* Poll file descriptors only if a new scheduling signal
* has occurred or if we have no more runnable threads.
*/
- if (((current_tick = _sched_ticks) != last_tick) ||
+ else if (((current_tick = _sched_ticks) != last_tick) ||
((_thread_run->state != PS_RUNNING) &&
(PTHREAD_PRIOQ_FIRST() == NULL))) {
/* Unprotect the scheduling queues: */
@@ -337,7 +337,7 @@ _thread_kern_scheduler(void)
/*
* Poll file descriptors to update the state of threads
- * waiting on file I/O where data may be available:
+ * waiting on file I/O where data may be available:
*/
thread_kern_poll(0);
@@ -392,7 +392,7 @@ _thread_kern_scheduler(void)
if (add_to_prioq != 0) {
/*
* Save the current time as the time that the
- * thread became inactive:
+ * thread became inactive:
*/
current_tick = _sched_ticks;
_thread_run->last_inactive = (long)current_tick;
@@ -445,7 +445,7 @@ _thread_kern_scheduler(void)
/*
* Lock the pthread kernel by changing the pointer to
* the running thread to point to the global kernel
- * thread structure:
+ * thread structure:
*/
_thread_run = &_thread_kern_thread;
DBG_MSG("No runnable threads, using kernel thread %p\n",
@@ -456,7 +456,7 @@ _thread_kern_scheduler(void)
/*
* There are no threads ready to run, so wait until
- * something happens that changes this condition:
+ * something happens that changes this condition:
*/
thread_kern_poll(1);
@@ -524,7 +524,7 @@ _thread_kern_scheduler(void)
/*
* Save the current time as the time that the thread
- * became active:
+ * became active:
*/
current_tick = _sched_ticks;
_thread_run->last_active = (long) current_tick;
@@ -532,7 +532,7 @@ _thread_kern_scheduler(void)
/*
* Check if this thread is running for the first time
* or running again after using its full time slice
- * allocation:
+ * allocation:
*/
if (_thread_run->slice_usec == -1) {
/* Reset the accumulated time slice period: */
@@ -551,36 +551,39 @@ _thread_kern_scheduler(void)
/*
* Continue the thread at its current frame:
*/
- psf = _thread_run->curframe;
- switch(psf->ctxtype) {
+ switch(_thread_run->ctxtype) {
case CTX_JB_NOSIG:
- ___longjmp(psf->ctx.jb, psf->longjmp_val);
+ ___longjmp(_thread_run->ctx.jb,
+ _thread_run->longjmp_val);
break;
case CTX_JB:
- __longjmp(psf->ctx.jb, psf->longjmp_val);
+ __longjmp(_thread_run->ctx.jb,
+ _thread_run->longjmp_val);
break;
case CTX_SJB:
- __siglongjmp(psf->ctx.sigjb, psf->longjmp_val);
+ __siglongjmp(_thread_run->ctx.sigjb,
+ _thread_run->longjmp_val);
break;
case CTX_UC:
/* XXX - Restore FP regsisters? */
- FP_RESTORE_UC(&psf->ctx.uc);
+ FP_RESTORE_UC(&_thread_run->ctx.uc);
/*
* Do a sigreturn to restart the thread that
- * was interrupted by a signal:
+ * was interrupted by a signal:
*/
_thread_kern_in_sched = 0;
#if NOT_YET
- _setcontext(&psf->ctx.uc);
+ _setcontext(&_thread_run->ctx.uc);
#else
/*
* Ensure the process signal mask is set
* correctly:
*/
- psf->ctx.uc.uc_sigmask = _process_sigmask;
- _thread_sys_sigreturn(&psf->ctx.uc);
+ _thread_run->ctx.uc.uc_sigmask =
+ _process_sigmask;
+ _thread_sys_sigreturn(&_thread_run->ctx.uc);
#endif
break;
}
@@ -800,14 +803,14 @@ thread_kern_poll(int wait_reqd)
/*
* Wait for a file descriptor to be ready for read, write, or
- * an exception, or a timeout to occur:
+ * an exception, or a timeout to occur:
*/
count = _thread_sys_poll(_thread_pfd_table, nfds, timeout_ms);
if (kern_pipe_added != 0)
/*
* Remove the pthread kernel pipe file descriptor
- * from the pollfd table:
+ * from the pollfd table:
*/
nfds = 1;
else
@@ -821,7 +824,7 @@ thread_kern_poll(int wait_reqd)
(_thread_pfd_table[0].revents & POLLRDNORM))) {
/*
* If the kernel read pipe was included in the
- * count:
+ * count:
*/
if (count > 0) {
/* Decrement the count of file descriptors: */
@@ -843,7 +846,7 @@ thread_kern_poll(int wait_reqd)
/*
* Enter a loop to look for threads waiting on file
* descriptors that are flagged as available by the
- * _poll syscall:
+ * _poll syscall:
*/
PTHREAD_WAITQ_SETACTIVE();
TAILQ_FOREACH(pthread, &_workq, qe) {
@@ -986,7 +989,7 @@ _thread_kern_set_timeout(const struct timespec * timeout)
if (timeout == NULL) {
/*
* Set the wakeup time to something that can be recognised as
- * different to an actual time of day:
+ * different to an actual time of day:
*/
_thread_run->wakeup_time.tv_sec = -1;
_thread_run->wakeup_time.tv_nsec = -1;
@@ -1042,7 +1045,7 @@ _thread_kern_sig_undefer(void)
if (_sigq_check_reqd != 0)
_thread_kern_sched(NULL);
- /*
+ /*
* Check for asynchronous cancellation before delivering any
* pending signals:
*/
@@ -1071,7 +1074,7 @@ dequeue_signals(void)
int num;
/*
- * Enter a loop to clear the pthread kernel pipe:
+ * Enter a loop to clear the pthread kernel pipe:
*/
while (((num = _thread_sys_read(_thread_kern_pipe[0], bufr,
sizeof(bufr))) > 0) || (num == -1 && errno == EINTR)) {
diff --git a/lib/libkse/thread/thr_mutex.c b/lib/libkse/thread/thr_mutex.c
index ec6c0f6..f3649db 100644
--- a/lib/libkse/thread/thr_mutex.c
+++ b/lib/libkse/thread/thr_mutex.c
@@ -406,13 +406,29 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
_thread_init();
if (mutex == NULL)
- ret = EINVAL;
+ return (EINVAL);
/*
* If the mutex is statically initialized, perform the dynamic
* initialization:
*/
- else if (*mutex != NULL || (ret = init_static(mutex)) == 0) {
+ if ((*mutex == NULL) &&
+ ((ret = init_static(mutex)) != 0))
+ return (ret);
+
+ /* Reset the interrupted flag: */
+ _thread_run->interrupted = 0;
+
+ /*
+ * Enter a loop waiting to become the mutex owner. We need a
+ * loop in case the waiting thread is interrupted by a signal
+ * to execute a signal handler. It is not (currently) possible
+ * to remain in the waiting queue while running a handler.
+ * Instead, the thread is interrupted and backed out of the
+ * waiting queue prior to executing the signal handler.
+ */
+ while (((*mutex)->m_owner != _thread_run) && (ret == 0) &&
+ (_thread_run->interrupted == 0)) {
/*
* Defer signals to protect the scheduling queues from
* access by the signal handler:
@@ -432,9 +448,6 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
_MUTEX_INIT_LINK(*mutex);
}
- /* Reset the interrupted flag: */
- _thread_run->interrupted = 0;
-
/* Process according to mutex type: */
switch ((*mutex)->m_protocol) {
/* Default POSIX mutex: */
@@ -624,12 +637,12 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
* necessary:
*/
_thread_kern_sig_undefer();
-
- if (_thread_run->interrupted != 0 &&
- _thread_run->continuation != NULL)
- _thread_run->continuation((void *) _thread_run);
}
+ if (_thread_run->interrupted != 0 &&
+ _thread_run->continuation != NULL)
+ _thread_run->continuation((void *) _thread_run);
+
/* Return the completion status: */
return (ret);
}
@@ -1381,7 +1394,7 @@ _mutex_lock_backout(pthread_t pthread)
* access by the signal handler:
*/
_thread_kern_sig_defer();
- if (pthread->state == PS_MUTEX_WAIT) {
+ if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
mutex = pthread->data.mutex;
/* Lock the mutex structure: */
@@ -1390,7 +1403,7 @@ _mutex_lock_backout(pthread_t pthread)
mutex_queue_remove(mutex, pthread);
/* This thread is no longer waiting for the mutex: */
- mutex->m_owner->data.mutex = NULL;
+ pthread->data.mutex = NULL;
/* Unlock the mutex structure: */
_SPINUNLOCK(&mutex->lock);
diff --git a/lib/libkse/thread/thr_private.h b/lib/libkse/thread/thr_private.h
index 5076510..e8fff12 100644
--- a/lib/libkse/thread/thr_private.h
+++ b/lib/libkse/thread/thr_private.h
@@ -327,6 +327,7 @@ struct pthread_cond {
pthread_mutex_t c_mutex;
void *c_data;
long c_flags;
+ int c_seqno;
/*
* Lock for accesses to this structure.
@@ -351,7 +352,7 @@ struct pthread_cond_attr {
*/
#define PTHREAD_COND_STATIC_INITIALIZER \
{ COND_TYPE_FAST, TAILQ_INITIALIZER, NULL, NULL, \
- 0, _SPINLOCK_INITIALIZER }
+ 0, 0, _SPINLOCK_INITIALIZER }
/*
* Semaphore definitions.
@@ -424,6 +425,9 @@ enum pthread_susp {
*/
#define PTHREAD_STACK_INITIAL 0x100000
+/* Size of the scheduler stack: */
+#define SCHED_STACK_SIZE PAGE_SIZE
+
/*
* Define the different priority ranges. All applications have thread
* priorities constrained within 0-31. The threads library raises the
@@ -574,13 +578,20 @@ union pthread_wait_data {
*/
typedef void (*thread_continuation_t) (void *);
+struct pthread_signal_frame;
+
struct pthread_state_data {
- int psd_interrupted;
+ struct pthread_signal_frame *psd_curframe;
sigset_t psd_sigmask;
- enum pthread_state psd_state;
- int psd_flags;
struct timespec psd_wakeup_time;
union pthread_wait_data psd_wait_data;
+ enum pthread_state psd_state;
+ int psd_flags;
+ int psd_interrupted;
+ int psd_longjmp_val;
+ int psd_sigmask_seqno;
+ int psd_signo;
+ int psd_sig_defer_count;
/* XXX - What about thread->timeout and/or thread->error? */
};
@@ -620,9 +631,6 @@ struct pthread_signal_frame {
*/
struct pthread_state_data saved_state;
- /* Beginning (bottom) of threads stack frame for this signal. */
- unsigned long stackp;
-
/*
* Threads return context; ctxtype identifies the type of context.
* For signal frame 0, these point to the context storage area
@@ -637,18 +645,10 @@ struct pthread_signal_frame {
} ctx;
thread_context_t ctxtype;
int longjmp_val;
-
- /* Threads "jump out of signal handler" destination frame. */
- int dst_frame;
-
- /*
- * Used to return back to the signal handling frame in case
- * the application tries to change contexts from the handler.
- */
- jmp_buf *sig_jb;
-
int signo; /* signal, arg 1 to sighandler */
int sig_has_args; /* use signal args if true */
+ ucontext_t uc;
+ siginfo_t siginfo;
};
/*
@@ -685,18 +685,20 @@ struct pthread {
struct pthread_attr attr;
/*
- * Used for tracking delivery of nested signal handlers.
- * Signal frame 0 is used for normal context (when no
- * signal handlers are active for the thread). Frame
- * 1 is used as the context for the first signal, and
- * frames 2 .. NSIG-1 are used when additional signals
- * arrive interrupting already active signal handlers.
+ * Threads return context; ctxtype identifies the type of context.
+ */
+ union {
+ jmp_buf jb;
+ sigjmp_buf sigjb;
+ ucontext_t uc;
+ } ctx;
+ thread_context_t ctxtype;
+ int longjmp_val;
+
+ /*
+ * Used for tracking delivery of signal handlers.
*/
- struct pthread_signal_frame *sigframes[NSIG];
- struct pthread_signal_frame sigframe0;
struct pthread_signal_frame *curframe;
- int sigframe_count;
- int sigframe_done;
/*
* Cancelability flags - the lower 2 bits are used by cancel
@@ -716,6 +718,7 @@ struct pthread {
*/
sigset_t sigmask;
sigset_t sigpend;
+ int sigmask_seqno;
int check_pending;
/* Thread state: */
@@ -1078,7 +1081,11 @@ SCLASS int _thread_dfl_count[NSIG];
* Pending signals and mask for this process:
*/
SCLASS sigset_t _process_sigpending;
-SCLASS sigset_t _process_sigmask;
+SCLASS sigset_t _process_sigmask
+#ifdef GLOBAL_PTHREAD_PRIVATE
+= { {0, 0, 0, 0} }
+#endif
+;
/*
* Scheduling queues:
@@ -1222,7 +1229,6 @@ void _waitq_clearactive(void);
#endif
void _thread_exit(char *, int, char *);
void _thread_exit_cleanup(void);
-void _thread_exit_finish(void);
void _thread_fd_unlock(int, int);
void _thread_fd_unlock_debug(int, int, char *, int);
void _thread_fd_unlock_owned(pthread_t);
@@ -1232,7 +1238,7 @@ void _thread_dump_info(void);
void _thread_init(void);
void _thread_kern_sched(ucontext_t *);
void _thread_kern_scheduler(void);
-void _thread_kern_sched_frame(int frame);
+void _thread_kern_sched_frame(struct pthread_signal_frame *psf);
void _thread_kern_sched_sig(void);
void _thread_kern_sched_state(enum pthread_state, char *fname, int lineno);
void _thread_kern_sched_state_unlock(enum pthread_state state,
@@ -1245,7 +1251,7 @@ void _thread_sig_check_pending(pthread_t pthread);
void _thread_sig_handle_pending(void);
void _thread_sig_send(pthread_t pthread, int sig);
void _thread_sig_wrapper(void);
-int _thread_sigframe_find(pthread_t pthread, void *stackp);
+void _thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf);
void _thread_start(void);
void _thread_seterrno(pthread_t, int);
int _thread_fd_table_init(int fd);
@@ -1262,6 +1268,7 @@ int _thread_sys_sigsuspend(const sigset_t *);
int _thread_sys_siginterrupt(int, int);
int _thread_sys_sigpause(int);
int _thread_sys_sigreturn(ucontext_t *);
+int _thread_sys_sigaltstack(const struct sigaltstack *, struct sigstack *);
int _thread_sys_sigstack(const struct sigstack *, struct sigstack *);
int _thread_sys_sigvec(int, struct sigvec *, struct sigvec *);
void _thread_sys_psignal(unsigned int, const char *);
diff --git a/lib/libkse/thread/thr_sig.c b/lib/libkse/thread/thr_sig.c
index f19582d..3bcd9c1 100644
--- a/lib/libkse/thread/thr_sig.c
+++ b/lib/libkse/thread/thr_sig.c
@@ -49,9 +49,7 @@ static void thread_sig_check_state(pthread_t pthread, int sig);
static pthread_t thread_sig_find(int sig);
static void thread_sig_handle_special(int sig);
static void thread_sig_savecontext(pthread_t pthread, ucontext_t *ucp);
-static void thread_sigframe_add(pthread_t thread, int sig);
-static void thread_sigframe_leave(pthread_t thread, int frame);
-static void thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf);
+static void thread_sigframe_add(pthread_t thread, int sig, int has_args);
static void thread_sigframe_save(pthread_t thread, struct pthread_signal_frame *psf);
/* #define DEBUG_SIGNAL */
@@ -72,21 +70,30 @@ static void thread_sigframe_save(pthread_t thread, struct pthread_signal_frame *
void
_thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
{
- pthread_t pthread;
- int current_frame;
+ pthread_t pthread, pthread_h;
+ void *stackp;
+ int in_sched = 0;
char c;
if (ucp == NULL)
PANIC("Thread signal handler received null context");
DBG_MSG("Got signal %d, current thread %p\n", sig, _thread_run);
+ if (_thread_kern_in_sched != 0)
+ in_sched = 1;
+ else {
+ stackp = (void *)GET_STACK_UC(ucp);
+ if ((stackp >= _thread_kern_sched_stack) &&
+ (stackp <= _thread_kern_sched_stack + SCHED_STACK_SIZE))
+ in_sched = 1;
+ }
/* Check if an interval timer signal: */
if (sig == _SCHED_SIGNAL) {
/* Update the scheduling clock: */
gettimeofday((struct timeval *)&_sched_tod, NULL);
_sched_ticks++;
- if (_thread_kern_in_sched != 0) {
+ if (in_sched != 0) {
/*
* The scheduler is already running; ignore this
* signal.
@@ -108,13 +115,13 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
/*
* Schedule the next thread. This function is not
* expected to return because it will do a longjmp
- * instead.
+ * instead.
*/
_thread_kern_sched(ucp);
/*
* This point should not be reached, so abort the
- * process:
+ * process:
*/
PANIC("Returned to signal function from scheduler");
}
@@ -124,8 +131,7 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
* is accessing the scheduling queues or if there is a currently
* running thread that has deferred signals.
*/
- else if ((_thread_kern_in_sched != 0) ||
- (_thread_run->sig_defer_count > 0)) {
+ else if ((in_sched != 0) || (_thread_run->sig_defer_count > 0)) {
/* Cast the signal number to a character variable: */
c = sig;
@@ -176,10 +182,9 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
* cannot be interrupted by other signals.
*/
else if (_thread_sigq[sig - 1].blocked == 0) {
- /* The signal is not blocked; handle the signal: */
- current_frame = _thread_run->sigframe_count;
-
/*
+ * The signal is not blocked; handle the signal.
+ *
* Ignore subsequent occurrences of this signal
* until the current signal is handled:
*/
@@ -204,6 +209,7 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
/* Handle special signals: */
thread_sig_handle_special(sig);
+ pthread_h = NULL;
if ((pthread = thread_sig_find(sig)) != NULL) {
DBG_MSG("Got signal %d, adding frame to thread %p\n",
sig, pthread);
@@ -221,9 +227,9 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
thread_sig_add(pthread, sig, /*has_args*/ 1);
/* Take a peek at the next ready to run thread: */
- pthread = PTHREAD_PRIOQ_FIRST();
+ pthread_h = PTHREAD_PRIOQ_FIRST();
DBG_MSG("Finished adding frame, head of prio list %p\n",
- pthread);
+ pthread_h);
}
else
DBG_MSG("No thread to handle signal %d\n", sig);
@@ -235,11 +241,9 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
* signal and the currently running thread is not in a
* signal handler.
*/
- if ((_thread_run->sigframe_count > current_frame) ||
- ((pthread != NULL) &&
- (pthread->active_priority > _thread_run->active_priority))) {
+ if ((pthread == _thread_run) || ((pthread_h != NULL) &&
+ (pthread_h->active_priority > _thread_run->active_priority))) {
/* Enter the kernel scheduler: */
- DBG_MSG("Entering scheduler from signal handler\n");
_thread_kern_sched(ucp);
}
}
@@ -253,17 +257,13 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
static void
thread_sig_savecontext(pthread_t pthread, ucontext_t *ucp)
{
- struct pthread_signal_frame *psf;
-
- psf = _thread_run->curframe;
-
- memcpy(&psf->ctx.uc, ucp, sizeof(*ucp));
+ memcpy(&pthread->ctx.uc, ucp, sizeof(*ucp));
/* XXX - Save FP registers too? */
- FP_SAVE_UC(&psf->ctx.uc);
+ FP_SAVE_UC(&pthread->ctx.uc);
/* Mark the context saved as a ucontext: */
- psf->ctxtype = CTX_UC;
+ pthread->ctxtype = CTX_UC;
}
/*
@@ -278,10 +278,13 @@ thread_sig_find(int sig)
DBG_MSG("Looking for thread to handle signal %d\n", sig);
/* Check if the signal requires a dump of thread information: */
- if (sig == SIGINFO)
+ if (sig == SIGINFO) {
/* Dump thread information to file: */
_thread_dump_info();
+ /* Unblock this signal to allow further dumps: */
+ _thread_sigq[sig - 1].blocked = 0;
+ }
/* Check if an interval timer signal: */
else if (sig == _SCHED_SIGNAL) {
/*
@@ -326,7 +329,7 @@ thread_sig_find(int sig)
* A signal handler is not invoked for threads
* in sigwait. Clear the blocked and pending
* flags.
- */
+ */
_thread_sigq[sig - 1].blocked = 0;
_thread_sigq[sig - 1].pending = 0;
@@ -375,7 +378,7 @@ thread_sig_find(int sig)
signaled_thread == NULL) {
/*
* Enter a loop to look for other threads
- * capable of receiving the signal:
+ * capable of receiving the signal:
*/
TAILQ_FOREACH(pthread, &_thread_list, tle) {
if (!sigismember(&pthread->sigmask,
@@ -565,8 +568,7 @@ thread_sig_handle_special(int sig)
static void
thread_sig_add(pthread_t pthread, int sig, int has_args)
{
- int restart, frame;
- int block_signals = 0;
+ int restart;
int suppress_handler = 0;
restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART;
@@ -657,8 +659,11 @@ thread_sig_add(pthread_t pthread, int sig, int has_args)
* States which cannot be interrupted but still require the
* signal handler to run:
*/
- case PS_COND_WAIT:
case PS_JOIN:
+ /* Only set the interrupted flag for PS_JOIN: */
+ pthread->interrupted = 1;
+ /* FALLTHROUGH */
+ case PS_COND_WAIT:
case PS_MUTEX_WAIT:
/*
* Remove the thread from the wait queue. It will
@@ -687,14 +692,6 @@ thread_sig_add(pthread_t pthread, int sig, int has_args)
* the actual handler.
*/
PTHREAD_WAITQ_REMOVE(pthread);
- /*
- * To ensure the thread is removed from the fd and file
- * queues before any other signal interrupts it, set the
- * signal mask to block all signals. As soon as the thread
- * is removed from the queue the signal mask will be
- * restored.
- */
- block_signals = 1;
break;
/*
@@ -736,22 +733,15 @@ thread_sig_add(pthread_t pthread, int sig, int has_args)
}
if (suppress_handler == 0) {
+ /* Setup a signal frame and save the current threads state: */
+ thread_sigframe_add(pthread, sig, has_args);
+
/*
- * Save the current state of the thread and add a
- * new signal frame.
+ * Signals are deferred until just before the threads
+ * signal handler is invoked:
*/
- frame = pthread->sigframe_count;
- thread_sigframe_save(pthread, pthread->curframe);
- thread_sigframe_add(pthread, sig);
- pthread->sigframes[frame + 1]->sig_has_args = has_args;
- SIGSETOR(pthread->sigmask, _thread_sigact[sig - 1].sa_mask);
- if (block_signals != 0) {
- /* Save the signal mask and block all signals: */
- pthread->sigframes[frame + 1]->saved_state.psd_sigmask =
- pthread->sigmask;
- sigfillset(&pthread->sigmask);
- }
-
+ pthread->sig_defer_count = 1;
+
/* Make sure the thread is runnable: */
if (pthread->state != PS_RUNNING)
PTHREAD_SET_STATE(pthread, PS_RUNNING);
@@ -925,19 +915,16 @@ _thread_sig_wrapper(void)
{
void (*sigfunc)(int, siginfo_t *, void *);
struct pthread_signal_frame *psf;
- pthread_t thread;
- int dead = 0;
- int i, sig, has_args;
- int frame, dst_frame;
+ pthread_t thread;
thread = _thread_run;
/* Get the current frame and state: */
- frame = thread->sigframe_count;
- PTHREAD_ASSERT(frame > 0, "Invalid signal frame in signal handler");
psf = thread->curframe;
+ thread->curframe = NULL;
+ PTHREAD_ASSERT(psf != NULL, "Invalid signal frame in signal handler");
- /* Check the threads previous state: */
+ /* Check the threads previous state: */
if (psf->saved_state.psd_state != PS_RUNNING) {
/*
* Do a little cleanup handling for those threads in
@@ -950,15 +937,26 @@ _thread_sig_wrapper(void)
case PS_FDLW_WAIT:
_fd_lock_backout(thread);
psf->saved_state.psd_state = PS_RUNNING;
- /* Reenable signals: */
- thread->sigmask = psf->saved_state.psd_sigmask;
break;
case PS_FILE_WAIT:
_flockfile_backout(thread);
psf->saved_state.psd_state = PS_RUNNING;
- /* Reenable signals: */
- thread->sigmask = psf->saved_state.psd_sigmask;
+ break;
+
+ case PS_COND_WAIT:
+ _cond_wait_backout(thread);
+ psf->saved_state.psd_state = PS_RUNNING;
+ break;
+
+ case PS_JOIN:
+ _join_backout(thread);
+ psf->saved_state.psd_state = PS_RUNNING;
+ break;
+
+ case PS_MUTEX_WAIT:
+ _mutex_lock_backout(thread);
+ psf->saved_state.psd_state = PS_RUNNING;
break;
default:
@@ -966,11 +964,20 @@ _thread_sig_wrapper(void)
}
}
+ /* Unblock the signal in case we don't return from the handler: */
+ _thread_sigq[psf->signo - 1].blocked = 0;
+
+ /*
+ * Lower the priority before calling the handler in case
+ * it never returns (longjmps back):
+ */
+ thread->active_priority &= ~PTHREAD_SIGNAL_PRIORITY;
+
/*
- * Unless the thread exits or longjmps out of the signal handler,
- * return to the previous frame:
+ * Reenable interruptions without checking for the need to
+ * context switch:
*/
- dst_frame = frame - 1;
+ thread->sig_defer_count = 0;
/*
* Check that a custom handler is installed and if the signal
@@ -979,141 +986,45 @@ _thread_sig_wrapper(void)
sigfunc = _thread_sigact[psf->signo - 1].sa_sigaction;
if (((__sighandler_t *)sigfunc != SIG_DFL) &&
((__sighandler_t *)sigfunc != SIG_IGN)) {
+ DBG_MSG("_thread_sig_wrapper: Calling signal handler for "
+ "thread 0x%p\n", thread);
/*
- * The signal jump buffer is allocated off the stack.
- * If the signal handler tries to [_][sig]longjmp() or
- * setcontext(), our wrapped versions of these routines
- * will copy the user supplied jump buffer or context
- * to the destination signal frame, set the destination
- * signal frame in psf->dst_frame, and _longjmp() back
- * to here.
- */
- jmp_buf jb;
-
- /*
- * Set up the context for abnormal returns out of signal
- * handlers.
+ * Dispatch the signal via the custom signal
+ * handler:
*/
- psf->sig_jb = &jb;
- if (_setjmp(jb) == 0) {
- DBG_MSG("_thread_sig_wrapper: Entering frame %d, "
- "stack 0x%lx\n", frame, GET_STACK_JB(jb));
- /*
- * Invalidate the destination frame before calling
- * the signal handler.
- */
- psf->dst_frame = -1;
-
- /*
- * Dispatch the signal via the custom signal
- * handler:
- */
- if (psf->sig_has_args == 0)
- (*(sigfunc))(psf->signo, NULL, NULL);
- else if ((_thread_sigact[psf->signo - 1].sa_flags &
- SA_SIGINFO) != 0)
- (*(sigfunc))(psf->signo,
- &_thread_sigq[psf->signo - 1].siginfo,
- &_thread_sigq[psf->signo - 1].uc);
- else
- (*(sigfunc))(psf->signo,
- (siginfo_t *)_thread_sigq[psf->signo - 1].siginfo.si_code,
- &_thread_sigq[psf->signo - 1].uc);
- }
- else {
- /*
- * The return from _setjmp() should only be non-zero
- * when the signal handler wants to xxxlongjmp() or
- * setcontext() to a different context, or if the
- * thread has exited (via pthread_exit).
- */
- /*
- * Grab a copy of the destination frame before it
- * gets clobbered after unwinding.
- */
- dst_frame = psf->dst_frame;
- DBG_MSG("Abnormal exit from handler for signal %d, "
- "frame %d\n", psf->signo, frame);
-
- /* Has the thread exited? */
- if ((dead = thread->flags & PTHREAD_EXITING) != 0)
- /* When exiting, unwind to frame 0. */
- dst_frame = 0;
- else if ((dst_frame < 0) || (dst_frame > frame))
- PANIC("Attempt to unwind to invalid "
- "signal frame");
-
- /* Unwind to the target frame: */
- for (i = frame; i > dst_frame; i--) {
- DBG_MSG("Leaving frame %d, signal %d\n", i,
- thread->sigframes[i]->signo);
- /* Leave the current signal frame: */
- thread_sigframe_leave(thread, i);
-
- /*
- * Save whatever is needed out of the state
- * data; as soon as the frame count is
- * is decremented, another signal can arrive
- * and corrupt this view of the state data.
- */
- sig = thread->sigframes[i]->signo;
- has_args = thread->sigframes[i]->sig_has_args;
-
- /*
- * We're done with this signal frame:
- */
- thread->curframe = thread->sigframes[i - 1];
- thread->sigframe_count = i - 1;
-
- /*
- * Only unblock the signal if it was a
- * process signal as opposed to a signal
- * generated by pthread_kill().
- */
- if (has_args != 0)
- _thread_sigq[sig - 1].blocked = 0;
- }
- }
+ if (psf->sig_has_args == 0)
+ (*(sigfunc))(psf->signo, NULL, NULL);
+ else if ((_thread_sigact[psf->signo - 1].sa_flags &
+ SA_SIGINFO) != 0)
+ (*(sigfunc))(psf->signo, &psf->siginfo, &psf->uc);
+ else
+ (*(sigfunc))(psf->signo,
+ (siginfo_t *)psf->siginfo.si_code, &psf->uc);
}
-
/*
- * Call the kernel scheduler to schedule the next
- * thread.
+ * Call the kernel scheduler to safely restore the frame and
+ * schedule the next thread:
*/
- if (dead == 0) {
- /* Restore the threads state: */
- thread_sigframe_restore(thread, thread->sigframes[dst_frame]);
- _thread_kern_sched_frame(dst_frame);
- }
- else {
- PTHREAD_ASSERT(dst_frame == 0,
- "Invalid signal frame for dead thread");
-
- /* Perform any necessary cleanup before exiting. */
- thread_sigframe_leave(thread, 0);
-
- /* This should never return: */
- _thread_exit_finish();
- PANIC("Return from _thread_exit_finish in signal wrapper");
- }
+ _thread_kern_sched_frame(psf);
}
static void
-thread_sigframe_add(pthread_t thread, int sig)
+thread_sigframe_add(pthread_t thread, int sig, int has_args)
{
+ struct pthread_signal_frame *psf = NULL;
unsigned long stackp = 0;
/* Get the top of the threads stack: */
- switch (thread->curframe->ctxtype) {
+ switch (thread->ctxtype) {
case CTX_JB:
case CTX_JB_NOSIG:
- stackp = GET_STACK_JB(thread->curframe->ctx.jb);
+ stackp = GET_STACK_JB(thread->ctx.jb);
break;
case CTX_SJB:
- stackp = GET_STACK_SJB(thread->curframe->ctx.sigjb);
+ stackp = GET_STACK_SJB(thread->ctx.sigjb);
break;
case CTX_UC:
- stackp = GET_STACK_UC(&thread->curframe->ctx.uc);
+ stackp = GET_STACK_UC(&thread->ctx.uc);
break;
default:
PANIC("Invalid thread context type");
@@ -1130,138 +1041,76 @@ thread_sigframe_add(pthread_t thread, int sig)
/* Allocate room on top of the stack for a new signal frame: */
stackp -= sizeof(struct pthread_signal_frame);
- /* Set up the new frame: */
- thread->sigframe_count++;
- thread->sigframes[thread->sigframe_count] =
- (struct pthread_signal_frame *) stackp;
- thread->curframe = thread->sigframes[thread->sigframe_count];
- thread->curframe->stackp = stackp;
- thread->curframe->ctxtype = CTX_JB_NOSIG;
- thread->curframe->longjmp_val = 1;
- thread->curframe->signo = sig;
+ psf = (struct pthread_signal_frame *) stackp;
- /*
- * Set up the context:
- */
- _setjmp(thread->curframe->ctx.jb);
- SET_STACK_JB(thread->curframe->ctx.jb, stackp);
- SET_RETURN_ADDR_JB(thread->curframe->ctx.jb, _thread_sig_wrapper);
-}
+ /* Save the current context in the signal frame: */
+ thread_sigframe_save(thread, psf);
-/*
- * Locate the signal frame from the specified stack pointer.
- */
-int
-_thread_sigframe_find(pthread_t pthread, void *stackp)
-{
- int frame;
+ /* Set handler specific information: */
+ psf->sig_has_args = has_args;
+ psf->signo = sig;
+ if (has_args) {
+ /* Copy the signal handler arguments to the signal frame: */
+ memcpy(&psf->uc, &_thread_sigq[psf->signo - 1].uc,
+ sizeof(psf->uc));
+ memcpy(&psf->siginfo, &_thread_sigq[psf->signo - 1].siginfo,
+ sizeof(psf->siginfo));
+ }
+ /* Set up the new frame: */
+ thread->curframe = psf;
+ thread->ctxtype = CTX_JB_NOSIG;
+ thread->longjmp_val = 1;
+ thread->flags &= PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE |
+ PTHREAD_FLAGS_IN_SYNCQ;
/*
- * Find the destination of the target frame based on the
- * given stack pointer.
+ * Set up the context:
*/
- for (frame = pthread->sigframe_count; frame >= 0; frame--) {
- if (stackp < (void *)pthread->sigframes[frame]->stackp)
- break;
- }
- return (frame);
+ stackp += sizeof(double);
+ _setjmp(thread->ctx.jb);
+ SET_STACK_JB(thread->ctx.jb, stackp);
+ SET_RETURN_ADDR_JB(thread->ctx.jb, _thread_sig_wrapper);
}
-
+
void
-thread_sigframe_leave(pthread_t thread, int frame)
+_thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf)
{
- struct pthread_state_data *psd;
-
- psd = &thread->sigframes[frame]->saved_state;
-
+ thread->ctxtype = psf->ctxtype;
+ memcpy(&thread->ctx.uc, &psf->ctx.uc, sizeof(thread->ctx.uc));
/*
- * Perform any necessary cleanup for this signal frame:
+ * Only restore the signal mask if it hasn't been changed
+ * by the application during invocation of the signal handler:
*/
- switch (psd->psd_state) {
- case PS_DEAD:
- case PS_DEADLOCK:
- case PS_RUNNING:
- case PS_SIGTHREAD:
- case PS_STATE_MAX:
- case PS_SUSPENDED:
- break;
-
- /*
- * Threads in the following states need to be removed
- * from queues.
- */
- case PS_COND_WAIT:
- _cond_wait_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_FDLR_WAIT:
- case PS_FDLW_WAIT:
- _fd_lock_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_FILE_WAIT:
- _flockfile_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_JOIN:
- _join_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_MUTEX_WAIT:
- _mutex_lock_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- 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_WAIT_WAIT:
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0) {
- PTHREAD_WAITQ_REMOVE(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WORKQ) != 0)
- PTHREAD_WORKQ_REMOVE(thread);
- }
- break;
- }
-}
-
-static void
-thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf)
-{
- thread->interrupted = psf->saved_state.psd_interrupted;
- thread->sigmask = psf->saved_state.psd_sigmask;
- thread->state = psf->saved_state.psd_state;
- thread->flags = psf->saved_state.psd_flags;
+ if (thread->sigmask_seqno == psf->saved_state.psd_sigmask_seqno)
+ thread->sigmask = psf->saved_state.psd_sigmask;
+ thread->curframe = psf->saved_state.psd_curframe;
thread->wakeup_time = psf->saved_state.psd_wakeup_time;
thread->data = psf->saved_state.psd_wait_data;
+ thread->state = psf->saved_state.psd_state;
+ thread->flags = psf->saved_state.psd_flags;
+ thread->interrupted = psf->saved_state.psd_interrupted;
+ thread->longjmp_val = psf->saved_state.psd_longjmp_val;
+ thread->signo = psf->saved_state.psd_signo;
+ thread->sig_defer_count = psf->saved_state.psd_sig_defer_count;
}
static void
thread_sigframe_save(pthread_t thread, struct pthread_signal_frame *psf)
{
- psf->saved_state.psd_interrupted = thread->interrupted;
+ psf->ctxtype = thread->ctxtype;
+ memcpy(&psf->ctx.uc, &thread->ctx.uc, sizeof(thread->ctx.uc));
psf->saved_state.psd_sigmask = thread->sigmask;
- psf->saved_state.psd_state = thread->state;
- psf->saved_state.psd_flags = thread->flags;
- thread->flags &= PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE |
- PTHREAD_FLAGS_IN_CONDQ | PTHREAD_FLAGS_IN_MUTEXQ |
- PTHREAD_FLAGS_IN_JOINQ;
+ psf->saved_state.psd_curframe = thread->curframe;
psf->saved_state.psd_wakeup_time = thread->wakeup_time;
psf->saved_state.psd_wait_data = thread->data;
+ psf->saved_state.psd_state = thread->state;
+ psf->saved_state.psd_flags = thread->flags &
+ (PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE);
+ psf->saved_state.psd_interrupted = thread->interrupted;
+ psf->saved_state.psd_longjmp_val = thread->longjmp_val;
+ psf->saved_state.psd_sigmask_seqno = thread->sigmask_seqno;
+ psf->saved_state.psd_signo = thread->signo;
+ psf->saved_state.psd_sig_defer_count = thread->sig_defer_count;
}
#endif
diff --git a/lib/libkse/thread/thr_sigaction.c b/lib/libkse/thread/thr_sigaction.c
index e78f329..4d13819 100644
--- a/lib/libkse/thread/thr_sigaction.c
+++ b/lib/libkse/thread/thr_sigaction.c
@@ -80,7 +80,7 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
* handler arguments.
*/
sigfillset(&gact.sa_mask);
- gact.sa_flags = SA_SIGINFO;
+ gact.sa_flags = SA_SIGINFO | SA_ONSTACK;
/*
* Check if the signal handler is being set to
diff --git a/lib/libkse/thread/thr_sigmask.c b/lib/libkse/thread/thr_sigmask.c
index bdb0b43..53d0774 100644
--- a/lib/libkse/thread/thr_sigmask.c
+++ b/lib/libkse/thread/thr_sigmask.c
@@ -81,6 +81,9 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
break;
}
+ /* Increment the sequence number: */
+ _thread_run->sigmask_seqno++;
+
/*
* Check if there are pending signals for the running
* thread or process that aren't blocked:
diff --git a/lib/libpthread/thread/thr_cond.c b/lib/libpthread/thread/thr_cond.c
index 50cf927..5ff0967 100644
--- a/lib/libpthread/thread/thr_cond.c
+++ b/lib/libpthread/thread/thr_cond.c
@@ -47,7 +47,7 @@ static inline void cond_queue_enq(pthread_cond_t, pthread_t);
/* Reinitialize a condition variable to defaults. */
int
-_cond_reinit(pthread_cond_t * cond)
+_cond_reinit(pthread_cond_t *cond)
{
int ret = 0;
@@ -63,13 +63,14 @@ _cond_reinit(pthread_cond_t * cond)
(*cond)->c_flags = COND_FLAGS_INITED;
(*cond)->c_type = COND_TYPE_FAST;
(*cond)->c_mutex = NULL;
+ (*cond)->c_seqno = 0;
memset(&(*cond)->lock, 0, sizeof((*cond)->lock));
}
return (ret);
}
int
-pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
+pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
{
enum pthread_cond_type type;
pthread_cond_t pcond;
@@ -118,6 +119,7 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
pcond->c_flags |= COND_FLAGS_INITED;
pcond->c_type = type;
pcond->c_mutex = NULL;
+ pcond->c_seqno = 0;
memset(&pcond->lock,0,sizeof(pcond->lock));
*cond = pcond;
}
@@ -128,7 +130,7 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr)
}
int
-pthread_cond_destroy(pthread_cond_t * cond)
+pthread_cond_destroy(pthread_cond_t *cond)
{
int rval = 0;
@@ -155,22 +157,37 @@ pthread_cond_destroy(pthread_cond_t * cond)
}
int
-pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
+pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
int rval = 0;
+ int done = 0;
int interrupted = 0;
+ int unlock_mutex = 1;
+ int seqno;
_thread_enter_cancellation_point();
if (cond == NULL)
- rval = EINVAL;
+ return (EINVAL);
/*
* If the condition variable is statically initialized,
* perform the dynamic initialization:
*/
- else if (*cond != NULL ||
- (rval = pthread_cond_init(cond, NULL)) == 0) {
+ if (*cond == NULL &&
+ (rval = pthread_cond_init(cond, NULL)) != 0)
+ return (rval);
+
+ /*
+ * Enter a loop waiting for a condition signal or broadcast
+ * to wake up this thread. A loop is needed in case the waiting
+ * thread is interrupted by a signal to execute a signal handler.
+ * It is not (currently) possible to remain in the waiting queue
+ * while running a handler. Instead, the thread is interrupted
+ * and backed out of the waiting queue prior to executing the
+ * signal handler.
+ */
+ do {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@@ -205,14 +222,16 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
*/
cond_queue_enq(*cond, _thread_run);
- /* Remember the mutex that is being used: */
+ /* Remember the mutex and sequence number: */
(*cond)->c_mutex = *mutex;
+ seqno = (*cond)->c_seqno;
/* Wait forever: */
_thread_run->wakeup_time.tv_sec = -1;
/* Unlock the mutex: */
- if ((rval = _mutex_cv_unlock(mutex)) != 0) {
+ if ((unlock_mutex != 0) &&
+ ((rval = _mutex_cv_unlock(mutex)) != 0)) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
@@ -230,19 +249,23 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
}
else {
/*
+ * Don't unlock the mutex in the event
+ * this thread has to be requeued in
+ * condition variable queue:
+ */
+ unlock_mutex = 0;
+
+ /*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
- if (_thread_run->interrupted != 0) {
- /*
- * Remember that this thread
- * was interrupted:
- */
- interrupted = 1;
+ done = (seqno != (*cond)->c_seqno);
+ if ((_thread_run->flags &
+ PTHREAD_FLAGS_IN_CONDQ) != 0) {
/*
* Lock the condition variable
* while removing the thread.
@@ -260,6 +283,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
}
/*
+ * Save the interrupted flag; locking
+ * the mutex will destroy it.
+ */
+ interrupted = _thread_run->interrupted;
+
+ /*
* Note that even though this thread may have
* been canceled, POSIX requires that the mutex
* be reaquired prior to cancellation.
@@ -279,11 +308,9 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
break;
}
- if (interrupted != 0) {
- if (_thread_run->continuation != NULL)
- _thread_run->continuation((void *) _thread_run);
- }
- }
+ if ((interrupted != 0) && (_thread_run->continuation != NULL))
+ _thread_run->continuation((void *) _thread_run);
+ } while ((done == 0) && (rval == 0));
_thread_leave_cancellation_point();
@@ -296,18 +323,33 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
const struct timespec * abstime)
{
int rval = 0;
+ int done = 0;
int interrupted = 0;
+ int unlock_mutex = 1;
+ int seqno;
_thread_enter_cancellation_point();
if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
abstime->tv_nsec >= 1000000000)
- rval = EINVAL;
+ return (EINVAL);
/*
* If the condition variable is statically initialized, perform dynamic
* initialization.
*/
- else if (*cond != NULL || (rval = pthread_cond_init(cond, NULL)) == 0) {
+ if (*cond == NULL && (rval = pthread_cond_init(cond, NULL)) != 0)
+ return (rval);
+
+ /*
+ * Enter a loop waiting for a condition signal or broadcast
+ * to wake up this thread. A loop is needed in case the waiting
+ * thread is interrupted by a signal to execute a signal handler.
+ * It is not (currently) possible to remain in the waiting queue
+ * while running a handler. Instead, the thread is interrupted
+ * and backed out of the waiting queue prior to executing the
+ * signal handler.
+ */
+ do {
/* Lock the condition variable structure: */
_SPINLOCK(&(*cond)->lock);
@@ -348,11 +390,13 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
*/
cond_queue_enq(*cond, _thread_run);
- /* Remember the mutex that is being used: */
+ /* Remember the mutex and sequence number: */
(*cond)->c_mutex = *mutex;
+ seqno = (*cond)->c_seqno;
/* Unlock the mutex: */
- if ((rval = _mutex_cv_unlock(mutex)) != 0) {
+ if ((unlock_mutex != 0) &&
+ ((rval = _mutex_cv_unlock(mutex)) != 0)) {
/*
* Cannot unlock the mutex, so remove
* the running thread from the condition
@@ -368,35 +412,39 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
_SPINUNLOCK(&(*cond)->lock);
} else {
/*
+ * Don't unlock the mutex in the event
+ * this thread has to be requeued in
+ * condition variable queue:
+ */
+ unlock_mutex = 0;
+
+ /*
* Schedule the next thread and unlock
* the condition variable structure:
*/
_thread_kern_sched_state_unlock(PS_COND_WAIT,
&(*cond)->lock, __FILE__, __LINE__);
+ done = (seqno != (*cond)->c_seqno);
+
/*
- * Check if the wait timedout or was
- * interrupted (canceled):
+ * Check if the wait timedout, was
+ * interrupted (canceled), or needs to
+ * be resumed after handling a signal.
*/
if ((_thread_run->timeout == 0) &&
- (_thread_run->interrupted == 0)) {
+ (_thread_run->interrupted == 0) &&
+ (done != 0)) {
/* Lock the mutex: */
rval = _mutex_cv_lock(mutex);
-
} else {
- /*
- * Remember if this thread was
- * interrupted:
- */
- interrupted = _thread_run->interrupted;
-
- /* Lock the condition variable structure: */
+ /* Lock the CV structure: */
_SPINLOCK(&(*cond)->lock);
/*
* The wait timed out; remove
* the thread from the condition
- * variable queue:
+ * variable queue:
*/
cond_queue_remove(*cond,
_thread_run);
@@ -405,11 +453,18 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
if (TAILQ_FIRST(&(*cond)->c_queue) == NULL)
(*cond)->c_mutex = NULL;
- /* Unock the condition variable structure: */
+ /* Unock the CV structure: */
_SPINUNLOCK(&(*cond)->lock);
/* Return a timeout error: */
- rval = ETIMEDOUT;
+ if (_thread_run->timeout != 0)
+ rval = ETIMEDOUT;
+ /*
+ * Save the interrupted flag;
+ * locking the mutex will
+ * destroy it.
+ */
+ interrupted = _thread_run->interrupted;
/*
* Lock the mutex and ignore any
@@ -435,11 +490,9 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
break;
}
- if (interrupted != 0) {
- if (_thread_run->continuation != NULL)
- _thread_run->continuation((void *) _thread_run);
- }
- }
+ if ((interrupted != 0) && (_thread_run->continuation != NULL))
+ _thread_run->continuation((void *) _thread_run);
+ } while ((done == 0) && (rval == 0));
_thread_leave_cancellation_point();
@@ -473,6 +526,9 @@ pthread_cond_signal(pthread_cond_t * cond)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
+ /* Increment the sequence number: */
+ (*cond)->c_seqno++;
+
if ((pthread = cond_queue_deq(*cond)) != NULL) {
/*
* Unless the thread is currently suspended,
@@ -538,6 +594,9 @@ pthread_cond_broadcast(pthread_cond_t * cond)
switch ((*cond)->c_type) {
/* Fast condition variable: */
case COND_TYPE_FAST:
+ /* Increment the sequence number: */
+ (*cond)->c_seqno++;
+
/*
* Enter a loop to bring all threads off the
* condition queue:
diff --git a/lib/libpthread/thread/thr_create.c b/lib/libpthread/thread/thr_create.c
index 0390f1b..430869e 100644
--- a/lib/libpthread/thread/thr_create.c
+++ b/lib/libpthread/thread/thr_create.c
@@ -49,16 +49,13 @@
static u_int64_t next_uniqueid = 1;
#define OFF(f) offsetof(struct pthread, f)
-#define SIGFRAME_OFF(f) offsetof(struct pthread_signal_frame, f)
int _thread_next_offset = OFF(tle.tqe_next);
int _thread_uniqueid_offset = OFF(uniqueid);
int _thread_state_offset = OFF(state);
int _thread_name_offset = OFF(name);
-int _thread_curframe_offset = OFF(curframe);
-int _thread_sigframe_ctx_offset = SIGFRAME_OFF(ctx);
-int _thread_sigframe_ctxtype_offset = SIGFRAME_OFF(ctxtype);
+int _thread_ctxtype_offset = OFF(ctxtype);
+int _thread_ctx_offset = OFF(ctx);
#undef OFF
-#undef SIGFRAME_OFF
int _thread_PS_RUNNING_value = PS_RUNNING;
int _thread_PS_DEAD_value = PS_DEAD;
@@ -66,12 +63,12 @@ int _thread_CTX_JB_NOSIG_value = CTX_JB_NOSIG;
int _thread_CTX_JB_value = CTX_JB;
int _thread_CTX_SJB_value = CTX_SJB;
int _thread_CTX_UC_value = CTX_UC;
-int _thread_sigframe_size_value = sizeof(struct pthread_signal_frame);
int
pthread_create(pthread_t * thread, const pthread_attr_t * attr,
void *(*start_routine) (void *), void *arg)
{
+ struct itimerval itimer;
int f_gc = 0;
int ret = 0;
pthread_t gc_thread;
@@ -127,7 +124,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
} else {
/* Allocate a new stack. */
stack = _next_stack + PTHREAD_STACK_GUARD;
-
+
/*
* Even if stack allocation fails, we don't want
* to try to use this location again, so
@@ -184,40 +181,35 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Initialise the thread for signals: */
new_thread->sigmask = _thread_run->sigmask;
+ new_thread->sigmask_seqno = 0;
- /* Initialize the first signal frame: */
- new_thread->sigframes[0] = &new_thread->sigframe0;
- new_thread->curframe = &new_thread->sigframe0;
+ /* Initialize the signal frame: */
+ new_thread->curframe = NULL;
/* Initialise the jump buffer: */
- _setjmp(new_thread->curframe->ctx.jb);
+ _setjmp(new_thread->ctx.jb);
/*
* Set up new stack frame so that it looks like it
* returned from a longjmp() to the beginning of
* _thread_start().
*/
- SET_RETURN_ADDR_JB(new_thread->curframe->ctx.jb,
- _thread_start);
+ SET_RETURN_ADDR_JB(new_thread->ctx.jb, _thread_start);
/* The stack starts high and builds down: */
- SET_STACK_JB(new_thread->curframe->ctx.jb,
+ SET_STACK_JB(new_thread->ctx.jb,
(long)new_thread->stack + pattr->stacksize_attr
- sizeof(double));
/* Initialize the rest of the frame: */
- new_thread->curframe->ctxtype = CTX_JB_NOSIG;
- /* Set the base of the stack: */
- new_thread->curframe->stackp =
- GET_STACK_JB(new_thread->curframe->ctx.jb);
- new_thread->sigframe_count = 0;
+ new_thread->ctxtype = CTX_JB_NOSIG;
/* Copy the thread attributes: */
memcpy(&new_thread->attr, pattr, sizeof(struct pthread_attr));
/*
* Check if this thread is to inherit the scheduling
- * attributes from its parent:
+ * attributes from its parent:
*/
if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
/* Copy the scheduling attributes: */
@@ -233,7 +225,7 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/*
* Use just the thread priority, leaving the
* other scheduling attributes as their
- * default values:
+ * default values:
*/
new_thread->base_priority =
new_thread->attr.prio;
@@ -292,8 +284,19 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Return a pointer to the thread structure: */
(*thread) = new_thread;
+ if (f_gc != 0) {
+ /* Install the scheduling timer: */
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = _clock_res_usec;
+ itimer.it_value = itimer.it_interval;
+ if (setitimer(_ITIMER_SCHED_TIMER, &itimer,
+ NULL) != 0)
+ PANIC("Cannot set interval timer");
+ }
+
/* Schedule the new user thread: */
_thread_kern_sched(NULL);
+
/*
* Start a garbage collector thread
* if necessary.
diff --git a/lib/libpthread/thread/thr_detach.c b/lib/libpthread/thread/thr_detach.c
index 3bade9d..6dd762a 100644
--- a/lib/libpthread/thread/thr_detach.c
+++ b/lib/libpthread/thread/thr_detach.c
@@ -65,7 +65,12 @@ pthread_detach(pthread_t pthread)
pthread->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
/* Make the thread runnable: */
- PTHREAD_NEW_STATE(next_thread,PS_RUNNING);
+ PTHREAD_NEW_STATE(next_thread, PS_RUNNING);
+
+ /*
+ * Set the return value for the woken thread:
+ */
+ next_thread->error = ESRCH;
}
/*
diff --git a/lib/libpthread/thread/thr_exit.c b/lib/libpthread/thread/thr_exit.c
index 7fbeb65..aef12fe 100644
--- a/lib/libpthread/thread/thr_exit.c
+++ b/lib/libpthread/thread/thr_exit.c
@@ -141,7 +141,7 @@ _thread_exit_cleanup(void)
void
pthread_exit(void *status)
{
- int frame;
+ pthread_t pthread;
/* Check if this thread is already in the process of exiting: */
if ((_thread_run->flags & PTHREAD_EXITING) != 0) {
@@ -159,7 +159,6 @@ pthread_exit(void *status)
while (_thread_run->cleanup != NULL) {
pthread_cleanup_pop(1);
}
-
if (_thread_run->attr.cleanup_attr != NULL) {
_thread_run->attr.cleanup_attr(_thread_run->attr.arg_attr);
}
@@ -175,25 +174,6 @@ pthread_exit(void *status)
_thread_run->poll_data.fds = NULL;
}
- if ((frame = _thread_run->sigframe_count) == 0)
- _thread_exit_finish();
- else {
- /*
- * Jump back and unwind the signal frames to gracefully
- * cleanup.
- */
- ___longjmp(*_thread_run->sigframes[frame]->sig_jb, 1);
- }
-
- /* This point should not be reached. */
- PANIC("Dead thread has resumed");
-}
-
-void
-_thread_exit_finish(void)
-{
- pthread_t pthread;
-
/*
* Lock the garbage collector mutex to ensure that the garbage
* collector is not using the dead thread list.
@@ -233,6 +213,16 @@ _thread_exit_finish(void)
* detach this thread:
*/
PTHREAD_NEW_STATE(pthread, PS_RUNNING);
+
+ /*
+ * Set the return value for the woken thread:
+ */
+ if ((_thread_run->attr.flags & PTHREAD_DETACHED) != 0)
+ pthread->error = ESRCH;
+ else {
+ pthread->ret = _thread_run->ret;
+ pthread->error = 0;
+ }
}
/* Remove this thread from the thread list: */
@@ -240,5 +230,8 @@ _thread_exit_finish(void)
/* This thread will never be re-scheduled. */
_thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__);
+
+ /* This point should not be reached. */
+ PANIC("Dead thread has resumed");
}
#endif
diff --git a/lib/libpthread/thread/thr_info.c b/lib/libpthread/thread/thr_info.c
index ca91512..956ae7c 100644
--- a/lib/libpthread/thread/thr_info.c
+++ b/lib/libpthread/thread/thr_info.c
@@ -41,6 +41,13 @@
#include <errno.h>
#include "pthread_private.h"
+#ifndef NELEMENTS
+#define NELEMENTS(arr) (sizeof(arr) / sizeof(arr[0]))
+#endif
+
+static void dump_thread(int fd, pthread_t pthread, int long_version);
+
+
struct s_thread_info {
enum pthread_state state;
char *name;
@@ -77,12 +84,11 @@ _thread_dump_info(void)
char s[512];
int fd;
int i;
- int j;
pthread_t pthread;
char tmpfile[128];
pq_list_t *pq_list;
- for (i = 0; i < 100000; i++) {
+ for (i = 0; i < 100000; i++) {
snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i",
getpid(), i);
/* Open the dump file for append and create it if necessary: */
@@ -112,70 +118,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the global list: */
TAILQ_FOREACH(pthread, &_thread_list, tle) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
-
- /* Check if this is the running thread: */
- if (pthread == _thread_run) {
- /* Output a record for the running thread: */
- strcpy(s, "This is the running thread\n");
- _thread_sys_write(fd, s, strlen(s));
- }
- /* Check if this is the initial thread: */
- if (pthread == _thread_initial) {
- /* Output a record for the initial thread: */
- strcpy(s, "This is the initial thread\n");
- _thread_sys_write(fd, s, strlen(s));
- }
- /* Process according to thread state: */
- switch (pthread->state) {
- /* File descriptor read lock wait: */
- case PS_FDLR_WAIT:
- case PS_FDLW_WAIT:
- case PS_FDR_WAIT:
- case PS_FDW_WAIT:
- /* Write the lock details: */
- snprintf(s, sizeof(s), "fd %d[%s:%d]",
- pthread->data.fd.fd,
- pthread->data.fd.fname,
- pthread->data.fd.branch);
- _thread_sys_write(fd, s, strlen(s));
- snprintf(s, sizeof(s), "owner %pr/%pw\n",
- _thread_fd_table[pthread->data.fd.fd]->r_owner,
- _thread_fd_table[pthread->data.fd.fd]->w_owner);
- _thread_sys_write(fd, s, strlen(s));
- break;
- case PS_SIGWAIT:
- snprintf(s, sizeof(s), "sigmask (hi)");
- _thread_sys_write(fd, s, strlen(s));
- for (i = _SIG_WORDS - 1; i >= 0; i--) {
- snprintf(s, sizeof(s), "%08x\n",
- pthread->sigmask.__bits[i]);
- _thread_sys_write(fd, s, strlen(s));
- }
- snprintf(s, sizeof(s), "(lo)\n");
- _thread_sys_write(fd, s, strlen(s));
- break;
-
- /*
- * Trap other states that are not explicitly
- * coded to dump information:
- */
- default:
- /* Nothing to do here. */
- break;
- }
+ dump_thread(fd, pthread, /*long_verson*/ 1);
}
/* Output a header for ready threads: */
@@ -185,19 +128,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the ready queue: */
TAILQ_FOREACH (pq_list, &_readyq.pq_queue, pl_link) {
TAILQ_FOREACH(pthread, &pq_list->pl_head, pqe) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
}
@@ -207,19 +138,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the waiting queue: */
TAILQ_FOREACH (pthread, &_waitingq, pqe) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
/* Output a header for threads in the work queue: */
@@ -228,19 +147,7 @@ _thread_dump_info(void)
/* Enter a loop to report each thread in the waiting queue: */
TAILQ_FOREACH (pthread, &_workq, qe) {
- /* Find the state: */
- for (j = 0; j < (sizeof(thread_info) /
- sizeof(struct s_thread_info)) - 1; j++)
- if (thread_info[j].state == pthread->state)
- break;
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
- pthread, (pthread->name == NULL) ?
- "":pthread->name, pthread->base_priority,
- thread_info[j].name,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
/* Check if there are no dead threads: */
@@ -255,42 +162,38 @@ _thread_dump_info(void)
/*
* Enter a loop to report each thread in the global
- * dead thread list:
+ * dead thread list:
*/
TAILQ_FOREACH(pthread, &_dead_list, dle) {
- /* Output a record for the current thread: */
- snprintf(s, sizeof(s),
- "Thread %p prio %3d [%s:%d]\n",
- pthread, pthread->base_priority,
- pthread->fname,pthread->lineno);
- _thread_sys_write(fd, s, strlen(s));
+ dump_thread(fd, pthread, /*long_version*/ 0);
}
}
/* Output a header for file descriptors: */
- snprintf(s, sizeof(s), "\n\n=============\nFILE DESCRIPTOR TABLE (table size %d)\n\n",_thread_dtablesize);
+ snprintf(s, sizeof(s), "\n\n=============\nFILE DESCRIPTOR "
+ "TABLE (table size %d)\n\n", _thread_dtablesize);
_thread_sys_write(fd, s, strlen(s));
/* Enter a loop to report file descriptor lock usage: */
for (i = 0; i < _thread_dtablesize; i++) {
/*
* Check if memory is allocated for this file
- * descriptor:
+ * descriptor:
*/
if (_thread_fd_table[i] != NULL) {
/* Report the file descriptor lock status: */
snprintf(s, sizeof(s),
- "fd[%3d] read owner %p count %d [%s:%d]\n write owner %p count %d [%s:%d]\n",
- i,
- _thread_fd_table[i]->r_owner,
- _thread_fd_table[i]->r_lockcount,
- _thread_fd_table[i]->r_fname,
- _thread_fd_table[i]->r_lineno,
- _thread_fd_table[i]->w_owner,
- _thread_fd_table[i]->w_lockcount,
- _thread_fd_table[i]->w_fname,
- _thread_fd_table[i]->w_lineno);
- _thread_sys_write(fd, s, strlen(s));
+ "fd[%3d] read owner %p count %d [%s:%d]\n"
+ " write owner %p count %d [%s:%d]\n",
+ i, _thread_fd_table[i]->r_owner,
+ _thread_fd_table[i]->r_lockcount,
+ _thread_fd_table[i]->r_fname,
+ _thread_fd_table[i]->r_lineno,
+ _thread_fd_table[i]->w_owner,
+ _thread_fd_table[i]->w_lockcount,
+ _thread_fd_table[i]->w_fname,
+ _thread_fd_table[i]->w_lineno);
+ _thread_sys_write(fd, s, strlen(s));
}
}
@@ -299,6 +202,78 @@ _thread_dump_info(void)
}
}
+static void
+dump_thread(int fd, pthread_t pthread, int long_version)
+{
+ char s[512];
+ int i;
+
+ /* Find the state: */
+ for (i = 0; i < NELEMENTS(thread_info) - 1; i++)
+ if (thread_info[i].state == pthread->state)
+ break;
+
+ /* Output a record for the thread: */
+ snprintf(s, sizeof(s),
+ "--------------------\nThread %p (%s) prio %3d state %s [%s:%d]\n",
+ pthread, (pthread->name == NULL) ? "" : pthread->name,
+ pthread->active_priority, thread_info[i].name, pthread->fname,
+ pthread->lineno);
+ _thread_sys_write(fd, s, strlen(s));
+
+ if (long_version != 0) {
+ /* Check if this is the running thread: */
+ if (pthread == _thread_run) {
+ /* Output a record for the running thread: */
+ strcpy(s, "This is the running thread\n");
+ _thread_sys_write(fd, s, strlen(s));
+ }
+ /* Check if this is the initial thread: */
+ if (pthread == _thread_initial) {
+ /* Output a record for the initial thread: */
+ strcpy(s, "This is the initial thread\n");
+ _thread_sys_write(fd, s, strlen(s));
+ }
+ /* Process according to thread state: */
+ switch (pthread->state) {
+ /* File descriptor read lock wait: */
+ case PS_FDLR_WAIT:
+ case PS_FDLW_WAIT:
+ case PS_FDR_WAIT:
+ case PS_FDW_WAIT:
+ /* Write the lock details: */
+ snprintf(s, sizeof(s), "fd %d[%s:%d]",
+ pthread->data.fd.fd,
+ pthread->data.fd.fname,
+ pthread->data.fd.branch);
+ _thread_sys_write(fd, s, strlen(s));
+ snprintf(s, sizeof(s), "owner %pr/%pw\n",
+ _thread_fd_table[pthread->data.fd.fd]->r_owner,
+ _thread_fd_table[pthread->data.fd.fd]->w_owner);
+ _thread_sys_write(fd, s, strlen(s));
+ break;
+ case PS_SIGWAIT:
+ snprintf(s, sizeof(s), "sigmask (hi)");
+ _thread_sys_write(fd, s, strlen(s));
+ for (i = _SIG_WORDS - 1; i >= 0; i--) {
+ snprintf(s, sizeof(s), "%08x\n",
+ pthread->sigmask.__bits[i]);
+ _thread_sys_write(fd, s, strlen(s));
+ }
+ snprintf(s, sizeof(s), "(lo)\n");
+ _thread_sys_write(fd, s, strlen(s));
+ break;
+ /*
+ * Trap other states that are not explicitly
+ * coded to dump information:
+ */
+ default:
+ /* Nothing to do here. */
+ break;
+ }
+ }
+}
+
/* Set the thread name for debug: */
void
pthread_set_name_np(pthread_t thread, char *name)
diff --git a/lib/libpthread/thread/thr_init.c b/lib/libpthread/thread/thr_init.c
index 3cbd453..35731c4 100644
--- a/lib/libpthread/thread/thr_init.c
+++ b/lib/libpthread/thread/thr_init.c
@@ -92,7 +92,7 @@ _thread_init(void)
int mib[2];
struct clockinfo clockinfo;
struct sigaction act;
- struct itimerval itimer;
+ struct sigaltstack alt;
/* Check if this function has already been called: */
if (_thread_initial)
@@ -133,7 +133,7 @@ _thread_init(void)
/*
* Create a pipe that is written to by the signal handler to prevent
- * signals being missed in calls to _select:
+ * signals being missed in calls to _select:
*/
if (_thread_sys_pipe(_thread_kern_pipe) != 0) {
/* Cannot create pipe, so abort: */
@@ -168,12 +168,12 @@ _thread_init(void)
else if ((_thread_initial = (pthread_t) malloc(sizeof(struct pthread))) == NULL) {
/*
* Insufficient memory to initialise this application, so
- * abort:
+ * abort:
*/
PANIC("Cannot allocate memory for initial thread");
}
/* Allocate memory for the scheduler stack: */
- else if ((_thread_kern_sched_stack = malloc(PAGE_SIZE * 10)) == NULL)
+ else if ((_thread_kern_sched_stack = malloc(SCHED_STACK_SIZE)) == NULL)
PANIC("Failed to allocate stack for scheduler");
else {
/* Zero the global kernel thread structure: */
@@ -217,8 +217,8 @@ _thread_init(void)
/* Setup the context for the scheduler: */
_setjmp(_thread_kern_sched_jb);
- SET_STACK_JB(_thread_kern_sched_jb,
- _thread_kern_sched_stack + PAGE_SIZE*10 - sizeof(double));
+ SET_STACK_JB(_thread_kern_sched_jb, _thread_kern_sched_stack +
+ SCHED_STACK_SIZE - sizeof(double));
SET_RETURN_ADDR_JB(_thread_kern_sched_jb, _thread_kern_scheduler);
/*
@@ -253,12 +253,9 @@ _thread_init(void)
/* Initialize last active: */
_thread_initial->last_active = (long) _sched_ticks;
- /* Initialize the initial signal frame: */
- _thread_initial->sigframes[0] = &_thread_initial->sigframe0;
- _thread_initial->curframe = &_thread_initial->sigframe0;
- _thread_initial->curframe->ctxtype = CTX_JB_NOSIG;
- /* Set the base of the stack: */
- _thread_initial->curframe->stackp = (unsigned long) USRSTACK;
+ /* Initialize the initial context: */
+ _thread_initial->curframe = NULL;
+ _thread_initial->ctxtype = CTX_JB_NOSIG;
/* Initialise the rest of the fields: */
_thread_initial->poll_data.nfds = 0;
@@ -276,7 +273,7 @@ _thread_init(void)
/* Initialise the global signal action structure: */
sigfillset(&act.sa_mask);
act.sa_handler = (void (*) ()) _thread_sig_handler;
- act.sa_flags = SA_SIGINFO;
+ act.sa_flags = SA_SIGINFO | SA_ONSTACK;
/* Clear pending signals for the process: */
sigemptyset(&_process_sigpending);
@@ -284,6 +281,13 @@ _thread_init(void)
/* Clear the signal queue: */
memset(_thread_sigq, 0, sizeof(_thread_sigq));
+ /* Create and install an alternate signal stack: */
+ alt.ss_sp = malloc(SIGSTKSZ); /* recommended stack size */
+ alt.ss_size = SIGSTKSZ;
+ alt.ss_flags = 0;
+ if (_thread_sys_sigaltstack(&alt, NULL) != 0)
+ PANIC("Unable to install alternate signal stack");
+
/* Enter a loop to get the existing signal status: */
for (i = 1; i < NSIG; i++) {
/* Check for signals which cannot be trapped: */
@@ -295,7 +299,7 @@ _thread_init(void)
&_thread_sigact[i - 1]) != 0) {
/*
* Abort this process if signal
- * initialisation fails:
+ * initialisation fails:
*/
PANIC("Cannot read signal handler info");
}
@@ -313,7 +317,7 @@ _thread_init(void)
_thread_sys_sigaction(SIGINFO, &act, NULL) != 0 ||
_thread_sys_sigaction(SIGCHLD, &act, NULL) != 0) {
/*
- * Abort this process if signal initialisation fails:
+ * Abort this process if signal initialisation fails:
*/
PANIC("Cannot initialise signal handler");
}
@@ -335,7 +339,7 @@ _thread_init(void)
if ((_thread_dtablesize = getdtablesize()) < 0) {
/*
* Cannot get the system defined table size, so abort
- * this process.
+ * this process.
*/
PANIC("Cannot get dtablesize");
}
@@ -346,7 +350,7 @@ _thread_init(void)
/*
* Cannot allocate memory for the file descriptor
- * table, so abort this process.
+ * table, so abort this process.
*/
PANIC("Cannot allocate memory for file descriptor table");
}
@@ -354,13 +358,13 @@ _thread_init(void)
if ((_thread_pfd_table = (struct pollfd *) malloc(sizeof(struct pollfd) * _thread_dtablesize)) == NULL) {
/*
* Cannot allocate memory for the file descriptor
- * table, so abort this process.
+ * table, so abort this process.
*/
PANIC("Cannot allocate memory for pollfd table");
} else {
/*
* Enter a loop to initialise the file descriptor
- * table:
+ * table:
*/
for (i = 0; i < _thread_dtablesize; i++) {
/* Initialise the file descriptor table: */
@@ -374,14 +378,6 @@ _thread_init(void)
PANIC("Cannot initialize stdio file "
"descriptor table entry");
}
-
- /* Install the scheduling timer: */
- itimer.it_interval.tv_sec = 0;
- itimer.it_interval.tv_usec = _clock_res_usec;
- itimer.it_value = itimer.it_interval;
- if (setitimer(_ITIMER_SCHED_TIMER, &itimer, NULL) != 0)
- PANIC("Cannot set interval timer");
-
}
}
@@ -401,10 +397,10 @@ _thread_init(void)
}
/*
- * Special start up code for NetBSD/Alpha
+ * Special start up code for NetBSD/Alpha
*/
#if defined(__NetBSD__) && defined(__alpha__)
-int
+int
main(int argc, char *argv[], char *env);
int
diff --git a/lib/libpthread/thread/thr_join.c b/lib/libpthread/thread/thr_join.c
index cda31bd..b4a7c61 100644
--- a/lib/libpthread/thread/thr_join.c
+++ b/lib/libpthread/thread/thr_join.c
@@ -74,46 +74,74 @@ pthread_join(pthread_t pthread, void **thread_return)
else if (pthread->state != PS_DEAD) {
PTHREAD_ASSERT_NOT_IN_SYNCQ(_thread_run);
- /* Clear the interrupted flag: */
- _thread_run->interrupted = 0;
-
/*
- * Protect against being context switched out while
- * adding this thread to the join queue.
+ * Enter a loop in case this thread is woken prematurely
+ * in order to invoke a signal handler:
*/
- _thread_kern_sig_defer();
-
- /* Add the running thread to the join queue: */
- TAILQ_INSERT_TAIL(&(pthread->join_queue), _thread_run, sqe);
- _thread_run->flags |= PTHREAD_FLAGS_IN_JOINQ;
- _thread_run->data.thread = pthread;
-
- /* Schedule the next thread: */
- _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
-
- if (_thread_run->interrupted != 0) {
- TAILQ_REMOVE(&(pthread->join_queue), _thread_run, sqe);
- _thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
+ for (;;) {
+ /* Clear the interrupted flag: */
+ _thread_run->interrupted = 0;
+
+ /*
+ * Protect against being context switched out while
+ * adding this thread to the join queue.
+ */
+ _thread_kern_sig_defer();
+
+ /* Add the running thread to the join queue: */
+ TAILQ_INSERT_TAIL(&(pthread->join_queue),
+ _thread_run, sqe);
+ _thread_run->flags |= PTHREAD_FLAGS_IN_JOINQ;
+ _thread_run->data.thread = pthread;
+
+ /* Schedule the next thread: */
+ _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__);
+
+ if ((_thread_run->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
+ TAILQ_REMOVE(&(pthread->join_queue),
+ _thread_run, sqe);
+ _thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
+ }
+ _thread_run->data.thread = NULL;
+
+ _thread_kern_sig_undefer();
+
+ if (_thread_run->interrupted != 0) {
+ if (_thread_run->continuation != NULL)
+ _thread_run->continuation(_thread_run);
+ /*
+ * This thread was interrupted, probably to
+ * invoke a signal handler. Make sure the
+ * target thread is still joinable.
+ */
+ if (((_find_thread(pthread) != 0) &&
+ (_find_dead_thread(pthread) != 0)) ||
+ ((pthread->attr.flags &
+ PTHREAD_DETACHED) != 0)) {
+ /* Return an error: */
+ ret = ESRCH;
+
+ /* We're done; break out of the loop. */
+ break;
+ }
+ else if (pthread->state == PS_DEAD) {
+ /* We're done; break out of the loop. */
+ break;
+ }
+ } else {
+ /*
+ * The thread return value and error are set
+ * by the thread we're joining to when it
+ * exits or detaches:
+ */
+ ret = _thread_run->error;
+ if ((ret == 0) && (thread_return != NULL))
+ *thread_return = _thread_run->ret;
+
+ /* We're done; break out of the loop. */
+ break;
+ }
}
- _thread_run->data.thread = NULL;
-
- _thread_kern_sig_undefer();
-
- 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) {
- /* Check if the return value is required: */
- if (thread_return)
- /* Return the thread's return value: */
- *thread_return = pthread->ret;
- }
- else
- /* Return an error: */
- ret = ESRCH;
-
/* Check if the return value is required: */
} else if (thread_return != NULL)
/* Return the thread's return value: */
@@ -129,7 +157,7 @@ void
_join_backout(pthread_t pthread)
{
_thread_kern_sig_defer();
- if (pthread->state == PS_JOIN) {
+ if ((pthread->flags & PTHREAD_FLAGS_IN_JOINQ) != 0) {
TAILQ_REMOVE(&pthread->data.thread->join_queue, pthread, sqe);
_thread_run->flags &= ~PTHREAD_FLAGS_IN_JOINQ;
}
diff --git a/lib/libpthread/thread/thr_kern.c b/lib/libpthread/thread/thr_kern.c
index 23f16bc..67844e1 100644
--- a/lib/libpthread/thread/thr_kern.c
+++ b/lib/libpthread/thread/thr_kern.c
@@ -60,7 +60,7 @@
#endif
/* Static function prototype definitions: */
-static void
+static void
thread_kern_poll(int wait_reqd);
static void
@@ -77,7 +77,7 @@ static int last_tick = 0;
* return to a previous frame.
*/
void
-_thread_kern_sched_frame(int frame)
+_thread_kern_sched_frame(struct pthread_signal_frame *psf)
{
/*
* Flag the pthread kernel as executing scheduler code
@@ -86,13 +86,8 @@ _thread_kern_sched_frame(int frame)
*/
_thread_kern_in_sched = 1;
- /* Return to the specified frame: */
- _thread_run->curframe = _thread_run->sigframes[frame];
- _thread_run->sigframe_count = frame;
-
- if (_thread_run->sigframe_count == 0)
- /* Restore the threads priority: */
- _thread_run->active_priority &= ~PTHREAD_SIGNAL_PRIORITY;
+ /* Restore the signal frame: */
+ _thread_sigframe_restore(_thread_run, psf);
/* Switch to the thread scheduler: */
___longjmp(_thread_kern_sched_jb, 1);
@@ -127,16 +122,16 @@ _thread_kern_sched(ucontext_t *scp)
_thread_kern_scheduler();
} else {
/* Save the state of the current thread: */
- if (_setjmp(_thread_run->curframe->ctx.jb) == 0) {
+ if (_setjmp(_thread_run->ctx.jb) == 0) {
/* Flag the jump buffer was the last state saved: */
- _thread_run->curframe->ctxtype = CTX_JB_NOSIG;
- _thread_run->curframe->longjmp_val = 1;
+ _thread_run->ctxtype = CTX_JB_NOSIG;
+ _thread_run->longjmp_val = 1;
} else {
DBG_MSG("Returned from ___longjmp, thread %p\n",
_thread_run);
/*
* This point is reached when a longjmp() is called
- * to restore the state of a thread.
+ * to restore the state of a thread.
*
* This is the normal way out of the scheduler.
*/
@@ -147,7 +142,7 @@ _thread_kern_sched(ucontext_t *scp)
PTHREAD_AT_CANCEL_POINT) == 0) &&
((_thread_run->cancelflags &
PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
- /*
+ /*
* Cancellations override signals.
*
* Stick a cancellation point at the
@@ -183,7 +178,6 @@ _thread_kern_sched_sig(void)
void
_thread_kern_scheduler(void)
{
- struct pthread_signal_frame *psf;
struct timespec ts;
struct timeval tv;
pthread_t pthread, pthread_h;
@@ -205,7 +199,7 @@ _thread_kern_scheduler(void)
* ready to run. This loop completes when there are no more threads
* in the global list or when a thread has its state restored by
* either a sigreturn (if the state was saved as a sigcontext) or a
- * longjmp (if the state was saved by a setjmp).
+ * longjmp (if the state was saved by a setjmp).
*/
while (!(TAILQ_EMPTY(&_thread_list))) {
/* Get the current time of day: */
@@ -229,7 +223,7 @@ _thread_kern_scheduler(void)
if (_thread_run->state != PS_RUNNING) {
/*
* Save the current time as the time that the
- * thread became inactive:
+ * thread became inactive:
*/
_thread_run->last_inactive = (long)current_tick;
if (_thread_run->last_inactive <
@@ -266,7 +260,7 @@ _thread_kern_scheduler(void)
/*
* States which do not depend on file descriptor I/O
- * operations or timeouts:
+ * operations or timeouts:
*/
case PS_DEADLOCK:
case PS_FDLR_WAIT:
@@ -326,10 +320,16 @@ _thread_kern_scheduler(void)
}
/*
+ * Avoid polling file descriptors if there are none
+ * waiting:
+ */
+ if (TAILQ_EMPTY(&_workq) == 0) {
+ }
+ /*
* Poll file descriptors only if a new scheduling signal
* has occurred or if we have no more runnable threads.
*/
- if (((current_tick = _sched_ticks) != last_tick) ||
+ else if (((current_tick = _sched_ticks) != last_tick) ||
((_thread_run->state != PS_RUNNING) &&
(PTHREAD_PRIOQ_FIRST() == NULL))) {
/* Unprotect the scheduling queues: */
@@ -337,7 +337,7 @@ _thread_kern_scheduler(void)
/*
* Poll file descriptors to update the state of threads
- * waiting on file I/O where data may be available:
+ * waiting on file I/O where data may be available:
*/
thread_kern_poll(0);
@@ -392,7 +392,7 @@ _thread_kern_scheduler(void)
if (add_to_prioq != 0) {
/*
* Save the current time as the time that the
- * thread became inactive:
+ * thread became inactive:
*/
current_tick = _sched_ticks;
_thread_run->last_inactive = (long)current_tick;
@@ -445,7 +445,7 @@ _thread_kern_scheduler(void)
/*
* Lock the pthread kernel by changing the pointer to
* the running thread to point to the global kernel
- * thread structure:
+ * thread structure:
*/
_thread_run = &_thread_kern_thread;
DBG_MSG("No runnable threads, using kernel thread %p\n",
@@ -456,7 +456,7 @@ _thread_kern_scheduler(void)
/*
* There are no threads ready to run, so wait until
- * something happens that changes this condition:
+ * something happens that changes this condition:
*/
thread_kern_poll(1);
@@ -524,7 +524,7 @@ _thread_kern_scheduler(void)
/*
* Save the current time as the time that the thread
- * became active:
+ * became active:
*/
current_tick = _sched_ticks;
_thread_run->last_active = (long) current_tick;
@@ -532,7 +532,7 @@ _thread_kern_scheduler(void)
/*
* Check if this thread is running for the first time
* or running again after using its full time slice
- * allocation:
+ * allocation:
*/
if (_thread_run->slice_usec == -1) {
/* Reset the accumulated time slice period: */
@@ -551,36 +551,39 @@ _thread_kern_scheduler(void)
/*
* Continue the thread at its current frame:
*/
- psf = _thread_run->curframe;
- switch(psf->ctxtype) {
+ switch(_thread_run->ctxtype) {
case CTX_JB_NOSIG:
- ___longjmp(psf->ctx.jb, psf->longjmp_val);
+ ___longjmp(_thread_run->ctx.jb,
+ _thread_run->longjmp_val);
break;
case CTX_JB:
- __longjmp(psf->ctx.jb, psf->longjmp_val);
+ __longjmp(_thread_run->ctx.jb,
+ _thread_run->longjmp_val);
break;
case CTX_SJB:
- __siglongjmp(psf->ctx.sigjb, psf->longjmp_val);
+ __siglongjmp(_thread_run->ctx.sigjb,
+ _thread_run->longjmp_val);
break;
case CTX_UC:
/* XXX - Restore FP regsisters? */
- FP_RESTORE_UC(&psf->ctx.uc);
+ FP_RESTORE_UC(&_thread_run->ctx.uc);
/*
* Do a sigreturn to restart the thread that
- * was interrupted by a signal:
+ * was interrupted by a signal:
*/
_thread_kern_in_sched = 0;
#if NOT_YET
- _setcontext(&psf->ctx.uc);
+ _setcontext(&_thread_run->ctx.uc);
#else
/*
* Ensure the process signal mask is set
* correctly:
*/
- psf->ctx.uc.uc_sigmask = _process_sigmask;
- _thread_sys_sigreturn(&psf->ctx.uc);
+ _thread_run->ctx.uc.uc_sigmask =
+ _process_sigmask;
+ _thread_sys_sigreturn(&_thread_run->ctx.uc);
#endif
break;
}
@@ -800,14 +803,14 @@ thread_kern_poll(int wait_reqd)
/*
* Wait for a file descriptor to be ready for read, write, or
- * an exception, or a timeout to occur:
+ * an exception, or a timeout to occur:
*/
count = _thread_sys_poll(_thread_pfd_table, nfds, timeout_ms);
if (kern_pipe_added != 0)
/*
* Remove the pthread kernel pipe file descriptor
- * from the pollfd table:
+ * from the pollfd table:
*/
nfds = 1;
else
@@ -821,7 +824,7 @@ thread_kern_poll(int wait_reqd)
(_thread_pfd_table[0].revents & POLLRDNORM))) {
/*
* If the kernel read pipe was included in the
- * count:
+ * count:
*/
if (count > 0) {
/* Decrement the count of file descriptors: */
@@ -843,7 +846,7 @@ thread_kern_poll(int wait_reqd)
/*
* Enter a loop to look for threads waiting on file
* descriptors that are flagged as available by the
- * _poll syscall:
+ * _poll syscall:
*/
PTHREAD_WAITQ_SETACTIVE();
TAILQ_FOREACH(pthread, &_workq, qe) {
@@ -986,7 +989,7 @@ _thread_kern_set_timeout(const struct timespec * timeout)
if (timeout == NULL) {
/*
* Set the wakeup time to something that can be recognised as
- * different to an actual time of day:
+ * different to an actual time of day:
*/
_thread_run->wakeup_time.tv_sec = -1;
_thread_run->wakeup_time.tv_nsec = -1;
@@ -1042,7 +1045,7 @@ _thread_kern_sig_undefer(void)
if (_sigq_check_reqd != 0)
_thread_kern_sched(NULL);
- /*
+ /*
* Check for asynchronous cancellation before delivering any
* pending signals:
*/
@@ -1071,7 +1074,7 @@ dequeue_signals(void)
int num;
/*
- * Enter a loop to clear the pthread kernel pipe:
+ * Enter a loop to clear the pthread kernel pipe:
*/
while (((num = _thread_sys_read(_thread_kern_pipe[0], bufr,
sizeof(bufr))) > 0) || (num == -1 && errno == EINTR)) {
diff --git a/lib/libpthread/thread/thr_mutex.c b/lib/libpthread/thread/thr_mutex.c
index ec6c0f6..f3649db 100644
--- a/lib/libpthread/thread/thr_mutex.c
+++ b/lib/libpthread/thread/thr_mutex.c
@@ -406,13 +406,29 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
_thread_init();
if (mutex == NULL)
- ret = EINVAL;
+ return (EINVAL);
/*
* If the mutex is statically initialized, perform the dynamic
* initialization:
*/
- else if (*mutex != NULL || (ret = init_static(mutex)) == 0) {
+ if ((*mutex == NULL) &&
+ ((ret = init_static(mutex)) != 0))
+ return (ret);
+
+ /* Reset the interrupted flag: */
+ _thread_run->interrupted = 0;
+
+ /*
+ * Enter a loop waiting to become the mutex owner. We need a
+ * loop in case the waiting thread is interrupted by a signal
+ * to execute a signal handler. It is not (currently) possible
+ * to remain in the waiting queue while running a handler.
+ * Instead, the thread is interrupted and backed out of the
+ * waiting queue prior to executing the signal handler.
+ */
+ while (((*mutex)->m_owner != _thread_run) && (ret == 0) &&
+ (_thread_run->interrupted == 0)) {
/*
* Defer signals to protect the scheduling queues from
* access by the signal handler:
@@ -432,9 +448,6 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
_MUTEX_INIT_LINK(*mutex);
}
- /* Reset the interrupted flag: */
- _thread_run->interrupted = 0;
-
/* Process according to mutex type: */
switch ((*mutex)->m_protocol) {
/* Default POSIX mutex: */
@@ -624,12 +637,12 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
* necessary:
*/
_thread_kern_sig_undefer();
-
- if (_thread_run->interrupted != 0 &&
- _thread_run->continuation != NULL)
- _thread_run->continuation((void *) _thread_run);
}
+ if (_thread_run->interrupted != 0 &&
+ _thread_run->continuation != NULL)
+ _thread_run->continuation((void *) _thread_run);
+
/* Return the completion status: */
return (ret);
}
@@ -1381,7 +1394,7 @@ _mutex_lock_backout(pthread_t pthread)
* access by the signal handler:
*/
_thread_kern_sig_defer();
- if (pthread->state == PS_MUTEX_WAIT) {
+ if ((pthread->flags & PTHREAD_FLAGS_IN_MUTEXQ) != 0) {
mutex = pthread->data.mutex;
/* Lock the mutex structure: */
@@ -1390,7 +1403,7 @@ _mutex_lock_backout(pthread_t pthread)
mutex_queue_remove(mutex, pthread);
/* This thread is no longer waiting for the mutex: */
- mutex->m_owner->data.mutex = NULL;
+ pthread->data.mutex = NULL;
/* Unlock the mutex structure: */
_SPINUNLOCK(&mutex->lock);
diff --git a/lib/libpthread/thread/thr_private.h b/lib/libpthread/thread/thr_private.h
index 5076510..e8fff12 100644
--- a/lib/libpthread/thread/thr_private.h
+++ b/lib/libpthread/thread/thr_private.h
@@ -327,6 +327,7 @@ struct pthread_cond {
pthread_mutex_t c_mutex;
void *c_data;
long c_flags;
+ int c_seqno;
/*
* Lock for accesses to this structure.
@@ -351,7 +352,7 @@ struct pthread_cond_attr {
*/
#define PTHREAD_COND_STATIC_INITIALIZER \
{ COND_TYPE_FAST, TAILQ_INITIALIZER, NULL, NULL, \
- 0, _SPINLOCK_INITIALIZER }
+ 0, 0, _SPINLOCK_INITIALIZER }
/*
* Semaphore definitions.
@@ -424,6 +425,9 @@ enum pthread_susp {
*/
#define PTHREAD_STACK_INITIAL 0x100000
+/* Size of the scheduler stack: */
+#define SCHED_STACK_SIZE PAGE_SIZE
+
/*
* Define the different priority ranges. All applications have thread
* priorities constrained within 0-31. The threads library raises the
@@ -574,13 +578,20 @@ union pthread_wait_data {
*/
typedef void (*thread_continuation_t) (void *);
+struct pthread_signal_frame;
+
struct pthread_state_data {
- int psd_interrupted;
+ struct pthread_signal_frame *psd_curframe;
sigset_t psd_sigmask;
- enum pthread_state psd_state;
- int psd_flags;
struct timespec psd_wakeup_time;
union pthread_wait_data psd_wait_data;
+ enum pthread_state psd_state;
+ int psd_flags;
+ int psd_interrupted;
+ int psd_longjmp_val;
+ int psd_sigmask_seqno;
+ int psd_signo;
+ int psd_sig_defer_count;
/* XXX - What about thread->timeout and/or thread->error? */
};
@@ -620,9 +631,6 @@ struct pthread_signal_frame {
*/
struct pthread_state_data saved_state;
- /* Beginning (bottom) of threads stack frame for this signal. */
- unsigned long stackp;
-
/*
* Threads return context; ctxtype identifies the type of context.
* For signal frame 0, these point to the context storage area
@@ -637,18 +645,10 @@ struct pthread_signal_frame {
} ctx;
thread_context_t ctxtype;
int longjmp_val;
-
- /* Threads "jump out of signal handler" destination frame. */
- int dst_frame;
-
- /*
- * Used to return back to the signal handling frame in case
- * the application tries to change contexts from the handler.
- */
- jmp_buf *sig_jb;
-
int signo; /* signal, arg 1 to sighandler */
int sig_has_args; /* use signal args if true */
+ ucontext_t uc;
+ siginfo_t siginfo;
};
/*
@@ -685,18 +685,20 @@ struct pthread {
struct pthread_attr attr;
/*
- * Used for tracking delivery of nested signal handlers.
- * Signal frame 0 is used for normal context (when no
- * signal handlers are active for the thread). Frame
- * 1 is used as the context for the first signal, and
- * frames 2 .. NSIG-1 are used when additional signals
- * arrive interrupting already active signal handlers.
+ * Threads return context; ctxtype identifies the type of context.
+ */
+ union {
+ jmp_buf jb;
+ sigjmp_buf sigjb;
+ ucontext_t uc;
+ } ctx;
+ thread_context_t ctxtype;
+ int longjmp_val;
+
+ /*
+ * Used for tracking delivery of signal handlers.
*/
- struct pthread_signal_frame *sigframes[NSIG];
- struct pthread_signal_frame sigframe0;
struct pthread_signal_frame *curframe;
- int sigframe_count;
- int sigframe_done;
/*
* Cancelability flags - the lower 2 bits are used by cancel
@@ -716,6 +718,7 @@ struct pthread {
*/
sigset_t sigmask;
sigset_t sigpend;
+ int sigmask_seqno;
int check_pending;
/* Thread state: */
@@ -1078,7 +1081,11 @@ SCLASS int _thread_dfl_count[NSIG];
* Pending signals and mask for this process:
*/
SCLASS sigset_t _process_sigpending;
-SCLASS sigset_t _process_sigmask;
+SCLASS sigset_t _process_sigmask
+#ifdef GLOBAL_PTHREAD_PRIVATE
+= { {0, 0, 0, 0} }
+#endif
+;
/*
* Scheduling queues:
@@ -1222,7 +1229,6 @@ void _waitq_clearactive(void);
#endif
void _thread_exit(char *, int, char *);
void _thread_exit_cleanup(void);
-void _thread_exit_finish(void);
void _thread_fd_unlock(int, int);
void _thread_fd_unlock_debug(int, int, char *, int);
void _thread_fd_unlock_owned(pthread_t);
@@ -1232,7 +1238,7 @@ void _thread_dump_info(void);
void _thread_init(void);
void _thread_kern_sched(ucontext_t *);
void _thread_kern_scheduler(void);
-void _thread_kern_sched_frame(int frame);
+void _thread_kern_sched_frame(struct pthread_signal_frame *psf);
void _thread_kern_sched_sig(void);
void _thread_kern_sched_state(enum pthread_state, char *fname, int lineno);
void _thread_kern_sched_state_unlock(enum pthread_state state,
@@ -1245,7 +1251,7 @@ void _thread_sig_check_pending(pthread_t pthread);
void _thread_sig_handle_pending(void);
void _thread_sig_send(pthread_t pthread, int sig);
void _thread_sig_wrapper(void);
-int _thread_sigframe_find(pthread_t pthread, void *stackp);
+void _thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf);
void _thread_start(void);
void _thread_seterrno(pthread_t, int);
int _thread_fd_table_init(int fd);
@@ -1262,6 +1268,7 @@ int _thread_sys_sigsuspend(const sigset_t *);
int _thread_sys_siginterrupt(int, int);
int _thread_sys_sigpause(int);
int _thread_sys_sigreturn(ucontext_t *);
+int _thread_sys_sigaltstack(const struct sigaltstack *, struct sigstack *);
int _thread_sys_sigstack(const struct sigstack *, struct sigstack *);
int _thread_sys_sigvec(int, struct sigvec *, struct sigvec *);
void _thread_sys_psignal(unsigned int, const char *);
diff --git a/lib/libpthread/thread/thr_sig.c b/lib/libpthread/thread/thr_sig.c
index f19582d..3bcd9c1 100644
--- a/lib/libpthread/thread/thr_sig.c
+++ b/lib/libpthread/thread/thr_sig.c
@@ -49,9 +49,7 @@ static void thread_sig_check_state(pthread_t pthread, int sig);
static pthread_t thread_sig_find(int sig);
static void thread_sig_handle_special(int sig);
static void thread_sig_savecontext(pthread_t pthread, ucontext_t *ucp);
-static void thread_sigframe_add(pthread_t thread, int sig);
-static void thread_sigframe_leave(pthread_t thread, int frame);
-static void thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf);
+static void thread_sigframe_add(pthread_t thread, int sig, int has_args);
static void thread_sigframe_save(pthread_t thread, struct pthread_signal_frame *psf);
/* #define DEBUG_SIGNAL */
@@ -72,21 +70,30 @@ static void thread_sigframe_save(pthread_t thread, struct pthread_signal_frame *
void
_thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
{
- pthread_t pthread;
- int current_frame;
+ pthread_t pthread, pthread_h;
+ void *stackp;
+ int in_sched = 0;
char c;
if (ucp == NULL)
PANIC("Thread signal handler received null context");
DBG_MSG("Got signal %d, current thread %p\n", sig, _thread_run);
+ if (_thread_kern_in_sched != 0)
+ in_sched = 1;
+ else {
+ stackp = (void *)GET_STACK_UC(ucp);
+ if ((stackp >= _thread_kern_sched_stack) &&
+ (stackp <= _thread_kern_sched_stack + SCHED_STACK_SIZE))
+ in_sched = 1;
+ }
/* Check if an interval timer signal: */
if (sig == _SCHED_SIGNAL) {
/* Update the scheduling clock: */
gettimeofday((struct timeval *)&_sched_tod, NULL);
_sched_ticks++;
- if (_thread_kern_in_sched != 0) {
+ if (in_sched != 0) {
/*
* The scheduler is already running; ignore this
* signal.
@@ -108,13 +115,13 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
/*
* Schedule the next thread. This function is not
* expected to return because it will do a longjmp
- * instead.
+ * instead.
*/
_thread_kern_sched(ucp);
/*
* This point should not be reached, so abort the
- * process:
+ * process:
*/
PANIC("Returned to signal function from scheduler");
}
@@ -124,8 +131,7 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
* is accessing the scheduling queues or if there is a currently
* running thread that has deferred signals.
*/
- else if ((_thread_kern_in_sched != 0) ||
- (_thread_run->sig_defer_count > 0)) {
+ else if ((in_sched != 0) || (_thread_run->sig_defer_count > 0)) {
/* Cast the signal number to a character variable: */
c = sig;
@@ -176,10 +182,9 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
* cannot be interrupted by other signals.
*/
else if (_thread_sigq[sig - 1].blocked == 0) {
- /* The signal is not blocked; handle the signal: */
- current_frame = _thread_run->sigframe_count;
-
/*
+ * The signal is not blocked; handle the signal.
+ *
* Ignore subsequent occurrences of this signal
* until the current signal is handled:
*/
@@ -204,6 +209,7 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
/* Handle special signals: */
thread_sig_handle_special(sig);
+ pthread_h = NULL;
if ((pthread = thread_sig_find(sig)) != NULL) {
DBG_MSG("Got signal %d, adding frame to thread %p\n",
sig, pthread);
@@ -221,9 +227,9 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
thread_sig_add(pthread, sig, /*has_args*/ 1);
/* Take a peek at the next ready to run thread: */
- pthread = PTHREAD_PRIOQ_FIRST();
+ pthread_h = PTHREAD_PRIOQ_FIRST();
DBG_MSG("Finished adding frame, head of prio list %p\n",
- pthread);
+ pthread_h);
}
else
DBG_MSG("No thread to handle signal %d\n", sig);
@@ -235,11 +241,9 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
* signal and the currently running thread is not in a
* signal handler.
*/
- if ((_thread_run->sigframe_count > current_frame) ||
- ((pthread != NULL) &&
- (pthread->active_priority > _thread_run->active_priority))) {
+ if ((pthread == _thread_run) || ((pthread_h != NULL) &&
+ (pthread_h->active_priority > _thread_run->active_priority))) {
/* Enter the kernel scheduler: */
- DBG_MSG("Entering scheduler from signal handler\n");
_thread_kern_sched(ucp);
}
}
@@ -253,17 +257,13 @@ _thread_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp)
static void
thread_sig_savecontext(pthread_t pthread, ucontext_t *ucp)
{
- struct pthread_signal_frame *psf;
-
- psf = _thread_run->curframe;
-
- memcpy(&psf->ctx.uc, ucp, sizeof(*ucp));
+ memcpy(&pthread->ctx.uc, ucp, sizeof(*ucp));
/* XXX - Save FP registers too? */
- FP_SAVE_UC(&psf->ctx.uc);
+ FP_SAVE_UC(&pthread->ctx.uc);
/* Mark the context saved as a ucontext: */
- psf->ctxtype = CTX_UC;
+ pthread->ctxtype = CTX_UC;
}
/*
@@ -278,10 +278,13 @@ thread_sig_find(int sig)
DBG_MSG("Looking for thread to handle signal %d\n", sig);
/* Check if the signal requires a dump of thread information: */
- if (sig == SIGINFO)
+ if (sig == SIGINFO) {
/* Dump thread information to file: */
_thread_dump_info();
+ /* Unblock this signal to allow further dumps: */
+ _thread_sigq[sig - 1].blocked = 0;
+ }
/* Check if an interval timer signal: */
else if (sig == _SCHED_SIGNAL) {
/*
@@ -326,7 +329,7 @@ thread_sig_find(int sig)
* A signal handler is not invoked for threads
* in sigwait. Clear the blocked and pending
* flags.
- */
+ */
_thread_sigq[sig - 1].blocked = 0;
_thread_sigq[sig - 1].pending = 0;
@@ -375,7 +378,7 @@ thread_sig_find(int sig)
signaled_thread == NULL) {
/*
* Enter a loop to look for other threads
- * capable of receiving the signal:
+ * capable of receiving the signal:
*/
TAILQ_FOREACH(pthread, &_thread_list, tle) {
if (!sigismember(&pthread->sigmask,
@@ -565,8 +568,7 @@ thread_sig_handle_special(int sig)
static void
thread_sig_add(pthread_t pthread, int sig, int has_args)
{
- int restart, frame;
- int block_signals = 0;
+ int restart;
int suppress_handler = 0;
restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART;
@@ -657,8 +659,11 @@ thread_sig_add(pthread_t pthread, int sig, int has_args)
* States which cannot be interrupted but still require the
* signal handler to run:
*/
- case PS_COND_WAIT:
case PS_JOIN:
+ /* Only set the interrupted flag for PS_JOIN: */
+ pthread->interrupted = 1;
+ /* FALLTHROUGH */
+ case PS_COND_WAIT:
case PS_MUTEX_WAIT:
/*
* Remove the thread from the wait queue. It will
@@ -687,14 +692,6 @@ thread_sig_add(pthread_t pthread, int sig, int has_args)
* the actual handler.
*/
PTHREAD_WAITQ_REMOVE(pthread);
- /*
- * To ensure the thread is removed from the fd and file
- * queues before any other signal interrupts it, set the
- * signal mask to block all signals. As soon as the thread
- * is removed from the queue the signal mask will be
- * restored.
- */
- block_signals = 1;
break;
/*
@@ -736,22 +733,15 @@ thread_sig_add(pthread_t pthread, int sig, int has_args)
}
if (suppress_handler == 0) {
+ /* Setup a signal frame and save the current threads state: */
+ thread_sigframe_add(pthread, sig, has_args);
+
/*
- * Save the current state of the thread and add a
- * new signal frame.
+ * Signals are deferred until just before the threads
+ * signal handler is invoked:
*/
- frame = pthread->sigframe_count;
- thread_sigframe_save(pthread, pthread->curframe);
- thread_sigframe_add(pthread, sig);
- pthread->sigframes[frame + 1]->sig_has_args = has_args;
- SIGSETOR(pthread->sigmask, _thread_sigact[sig - 1].sa_mask);
- if (block_signals != 0) {
- /* Save the signal mask and block all signals: */
- pthread->sigframes[frame + 1]->saved_state.psd_sigmask =
- pthread->sigmask;
- sigfillset(&pthread->sigmask);
- }
-
+ pthread->sig_defer_count = 1;
+
/* Make sure the thread is runnable: */
if (pthread->state != PS_RUNNING)
PTHREAD_SET_STATE(pthread, PS_RUNNING);
@@ -925,19 +915,16 @@ _thread_sig_wrapper(void)
{
void (*sigfunc)(int, siginfo_t *, void *);
struct pthread_signal_frame *psf;
- pthread_t thread;
- int dead = 0;
- int i, sig, has_args;
- int frame, dst_frame;
+ pthread_t thread;
thread = _thread_run;
/* Get the current frame and state: */
- frame = thread->sigframe_count;
- PTHREAD_ASSERT(frame > 0, "Invalid signal frame in signal handler");
psf = thread->curframe;
+ thread->curframe = NULL;
+ PTHREAD_ASSERT(psf != NULL, "Invalid signal frame in signal handler");
- /* Check the threads previous state: */
+ /* Check the threads previous state: */
if (psf->saved_state.psd_state != PS_RUNNING) {
/*
* Do a little cleanup handling for those threads in
@@ -950,15 +937,26 @@ _thread_sig_wrapper(void)
case PS_FDLW_WAIT:
_fd_lock_backout(thread);
psf->saved_state.psd_state = PS_RUNNING;
- /* Reenable signals: */
- thread->sigmask = psf->saved_state.psd_sigmask;
break;
case PS_FILE_WAIT:
_flockfile_backout(thread);
psf->saved_state.psd_state = PS_RUNNING;
- /* Reenable signals: */
- thread->sigmask = psf->saved_state.psd_sigmask;
+ break;
+
+ case PS_COND_WAIT:
+ _cond_wait_backout(thread);
+ psf->saved_state.psd_state = PS_RUNNING;
+ break;
+
+ case PS_JOIN:
+ _join_backout(thread);
+ psf->saved_state.psd_state = PS_RUNNING;
+ break;
+
+ case PS_MUTEX_WAIT:
+ _mutex_lock_backout(thread);
+ psf->saved_state.psd_state = PS_RUNNING;
break;
default:
@@ -966,11 +964,20 @@ _thread_sig_wrapper(void)
}
}
+ /* Unblock the signal in case we don't return from the handler: */
+ _thread_sigq[psf->signo - 1].blocked = 0;
+
+ /*
+ * Lower the priority before calling the handler in case
+ * it never returns (longjmps back):
+ */
+ thread->active_priority &= ~PTHREAD_SIGNAL_PRIORITY;
+
/*
- * Unless the thread exits or longjmps out of the signal handler,
- * return to the previous frame:
+ * Reenable interruptions without checking for the need to
+ * context switch:
*/
- dst_frame = frame - 1;
+ thread->sig_defer_count = 0;
/*
* Check that a custom handler is installed and if the signal
@@ -979,141 +986,45 @@ _thread_sig_wrapper(void)
sigfunc = _thread_sigact[psf->signo - 1].sa_sigaction;
if (((__sighandler_t *)sigfunc != SIG_DFL) &&
((__sighandler_t *)sigfunc != SIG_IGN)) {
+ DBG_MSG("_thread_sig_wrapper: Calling signal handler for "
+ "thread 0x%p\n", thread);
/*
- * The signal jump buffer is allocated off the stack.
- * If the signal handler tries to [_][sig]longjmp() or
- * setcontext(), our wrapped versions of these routines
- * will copy the user supplied jump buffer or context
- * to the destination signal frame, set the destination
- * signal frame in psf->dst_frame, and _longjmp() back
- * to here.
- */
- jmp_buf jb;
-
- /*
- * Set up the context for abnormal returns out of signal
- * handlers.
+ * Dispatch the signal via the custom signal
+ * handler:
*/
- psf->sig_jb = &jb;
- if (_setjmp(jb) == 0) {
- DBG_MSG("_thread_sig_wrapper: Entering frame %d, "
- "stack 0x%lx\n", frame, GET_STACK_JB(jb));
- /*
- * Invalidate the destination frame before calling
- * the signal handler.
- */
- psf->dst_frame = -1;
-
- /*
- * Dispatch the signal via the custom signal
- * handler:
- */
- if (psf->sig_has_args == 0)
- (*(sigfunc))(psf->signo, NULL, NULL);
- else if ((_thread_sigact[psf->signo - 1].sa_flags &
- SA_SIGINFO) != 0)
- (*(sigfunc))(psf->signo,
- &_thread_sigq[psf->signo - 1].siginfo,
- &_thread_sigq[psf->signo - 1].uc);
- else
- (*(sigfunc))(psf->signo,
- (siginfo_t *)_thread_sigq[psf->signo - 1].siginfo.si_code,
- &_thread_sigq[psf->signo - 1].uc);
- }
- else {
- /*
- * The return from _setjmp() should only be non-zero
- * when the signal handler wants to xxxlongjmp() or
- * setcontext() to a different context, or if the
- * thread has exited (via pthread_exit).
- */
- /*
- * Grab a copy of the destination frame before it
- * gets clobbered after unwinding.
- */
- dst_frame = psf->dst_frame;
- DBG_MSG("Abnormal exit from handler for signal %d, "
- "frame %d\n", psf->signo, frame);
-
- /* Has the thread exited? */
- if ((dead = thread->flags & PTHREAD_EXITING) != 0)
- /* When exiting, unwind to frame 0. */
- dst_frame = 0;
- else if ((dst_frame < 0) || (dst_frame > frame))
- PANIC("Attempt to unwind to invalid "
- "signal frame");
-
- /* Unwind to the target frame: */
- for (i = frame; i > dst_frame; i--) {
- DBG_MSG("Leaving frame %d, signal %d\n", i,
- thread->sigframes[i]->signo);
- /* Leave the current signal frame: */
- thread_sigframe_leave(thread, i);
-
- /*
- * Save whatever is needed out of the state
- * data; as soon as the frame count is
- * is decremented, another signal can arrive
- * and corrupt this view of the state data.
- */
- sig = thread->sigframes[i]->signo;
- has_args = thread->sigframes[i]->sig_has_args;
-
- /*
- * We're done with this signal frame:
- */
- thread->curframe = thread->sigframes[i - 1];
- thread->sigframe_count = i - 1;
-
- /*
- * Only unblock the signal if it was a
- * process signal as opposed to a signal
- * generated by pthread_kill().
- */
- if (has_args != 0)
- _thread_sigq[sig - 1].blocked = 0;
- }
- }
+ if (psf->sig_has_args == 0)
+ (*(sigfunc))(psf->signo, NULL, NULL);
+ else if ((_thread_sigact[psf->signo - 1].sa_flags &
+ SA_SIGINFO) != 0)
+ (*(sigfunc))(psf->signo, &psf->siginfo, &psf->uc);
+ else
+ (*(sigfunc))(psf->signo,
+ (siginfo_t *)psf->siginfo.si_code, &psf->uc);
}
-
/*
- * Call the kernel scheduler to schedule the next
- * thread.
+ * Call the kernel scheduler to safely restore the frame and
+ * schedule the next thread:
*/
- if (dead == 0) {
- /* Restore the threads state: */
- thread_sigframe_restore(thread, thread->sigframes[dst_frame]);
- _thread_kern_sched_frame(dst_frame);
- }
- else {
- PTHREAD_ASSERT(dst_frame == 0,
- "Invalid signal frame for dead thread");
-
- /* Perform any necessary cleanup before exiting. */
- thread_sigframe_leave(thread, 0);
-
- /* This should never return: */
- _thread_exit_finish();
- PANIC("Return from _thread_exit_finish in signal wrapper");
- }
+ _thread_kern_sched_frame(psf);
}
static void
-thread_sigframe_add(pthread_t thread, int sig)
+thread_sigframe_add(pthread_t thread, int sig, int has_args)
{
+ struct pthread_signal_frame *psf = NULL;
unsigned long stackp = 0;
/* Get the top of the threads stack: */
- switch (thread->curframe->ctxtype) {
+ switch (thread->ctxtype) {
case CTX_JB:
case CTX_JB_NOSIG:
- stackp = GET_STACK_JB(thread->curframe->ctx.jb);
+ stackp = GET_STACK_JB(thread->ctx.jb);
break;
case CTX_SJB:
- stackp = GET_STACK_SJB(thread->curframe->ctx.sigjb);
+ stackp = GET_STACK_SJB(thread->ctx.sigjb);
break;
case CTX_UC:
- stackp = GET_STACK_UC(&thread->curframe->ctx.uc);
+ stackp = GET_STACK_UC(&thread->ctx.uc);
break;
default:
PANIC("Invalid thread context type");
@@ -1130,138 +1041,76 @@ thread_sigframe_add(pthread_t thread, int sig)
/* Allocate room on top of the stack for a new signal frame: */
stackp -= sizeof(struct pthread_signal_frame);
- /* Set up the new frame: */
- thread->sigframe_count++;
- thread->sigframes[thread->sigframe_count] =
- (struct pthread_signal_frame *) stackp;
- thread->curframe = thread->sigframes[thread->sigframe_count];
- thread->curframe->stackp = stackp;
- thread->curframe->ctxtype = CTX_JB_NOSIG;
- thread->curframe->longjmp_val = 1;
- thread->curframe->signo = sig;
+ psf = (struct pthread_signal_frame *) stackp;
- /*
- * Set up the context:
- */
- _setjmp(thread->curframe->ctx.jb);
- SET_STACK_JB(thread->curframe->ctx.jb, stackp);
- SET_RETURN_ADDR_JB(thread->curframe->ctx.jb, _thread_sig_wrapper);
-}
+ /* Save the current context in the signal frame: */
+ thread_sigframe_save(thread, psf);
-/*
- * Locate the signal frame from the specified stack pointer.
- */
-int
-_thread_sigframe_find(pthread_t pthread, void *stackp)
-{
- int frame;
+ /* Set handler specific information: */
+ psf->sig_has_args = has_args;
+ psf->signo = sig;
+ if (has_args) {
+ /* Copy the signal handler arguments to the signal frame: */
+ memcpy(&psf->uc, &_thread_sigq[psf->signo - 1].uc,
+ sizeof(psf->uc));
+ memcpy(&psf->siginfo, &_thread_sigq[psf->signo - 1].siginfo,
+ sizeof(psf->siginfo));
+ }
+ /* Set up the new frame: */
+ thread->curframe = psf;
+ thread->ctxtype = CTX_JB_NOSIG;
+ thread->longjmp_val = 1;
+ thread->flags &= PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE |
+ PTHREAD_FLAGS_IN_SYNCQ;
/*
- * Find the destination of the target frame based on the
- * given stack pointer.
+ * Set up the context:
*/
- for (frame = pthread->sigframe_count; frame >= 0; frame--) {
- if (stackp < (void *)pthread->sigframes[frame]->stackp)
- break;
- }
- return (frame);
+ stackp += sizeof(double);
+ _setjmp(thread->ctx.jb);
+ SET_STACK_JB(thread->ctx.jb, stackp);
+ SET_RETURN_ADDR_JB(thread->ctx.jb, _thread_sig_wrapper);
}
-
+
void
-thread_sigframe_leave(pthread_t thread, int frame)
+_thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf)
{
- struct pthread_state_data *psd;
-
- psd = &thread->sigframes[frame]->saved_state;
-
+ thread->ctxtype = psf->ctxtype;
+ memcpy(&thread->ctx.uc, &psf->ctx.uc, sizeof(thread->ctx.uc));
/*
- * Perform any necessary cleanup for this signal frame:
+ * Only restore the signal mask if it hasn't been changed
+ * by the application during invocation of the signal handler:
*/
- switch (psd->psd_state) {
- case PS_DEAD:
- case PS_DEADLOCK:
- case PS_RUNNING:
- case PS_SIGTHREAD:
- case PS_STATE_MAX:
- case PS_SUSPENDED:
- break;
-
- /*
- * Threads in the following states need to be removed
- * from queues.
- */
- case PS_COND_WAIT:
- _cond_wait_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_FDLR_WAIT:
- case PS_FDLW_WAIT:
- _fd_lock_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_FILE_WAIT:
- _flockfile_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_JOIN:
- _join_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- case PS_MUTEX_WAIT:
- _mutex_lock_backout(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0)
- PTHREAD_WAITQ_REMOVE(thread);
- break;
-
- 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_WAIT_WAIT:
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WAITQ) != 0) {
- PTHREAD_WAITQ_REMOVE(thread);
- if ((psd->psd_flags & PTHREAD_FLAGS_IN_WORKQ) != 0)
- PTHREAD_WORKQ_REMOVE(thread);
- }
- break;
- }
-}
-
-static void
-thread_sigframe_restore(pthread_t thread, struct pthread_signal_frame *psf)
-{
- thread->interrupted = psf->saved_state.psd_interrupted;
- thread->sigmask = psf->saved_state.psd_sigmask;
- thread->state = psf->saved_state.psd_state;
- thread->flags = psf->saved_state.psd_flags;
+ if (thread->sigmask_seqno == psf->saved_state.psd_sigmask_seqno)
+ thread->sigmask = psf->saved_state.psd_sigmask;
+ thread->curframe = psf->saved_state.psd_curframe;
thread->wakeup_time = psf->saved_state.psd_wakeup_time;
thread->data = psf->saved_state.psd_wait_data;
+ thread->state = psf->saved_state.psd_state;
+ thread->flags = psf->saved_state.psd_flags;
+ thread->interrupted = psf->saved_state.psd_interrupted;
+ thread->longjmp_val = psf->saved_state.psd_longjmp_val;
+ thread->signo = psf->saved_state.psd_signo;
+ thread->sig_defer_count = psf->saved_state.psd_sig_defer_count;
}
static void
thread_sigframe_save(pthread_t thread, struct pthread_signal_frame *psf)
{
- psf->saved_state.psd_interrupted = thread->interrupted;
+ psf->ctxtype = thread->ctxtype;
+ memcpy(&psf->ctx.uc, &thread->ctx.uc, sizeof(thread->ctx.uc));
psf->saved_state.psd_sigmask = thread->sigmask;
- psf->saved_state.psd_state = thread->state;
- psf->saved_state.psd_flags = thread->flags;
- thread->flags &= PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE |
- PTHREAD_FLAGS_IN_CONDQ | PTHREAD_FLAGS_IN_MUTEXQ |
- PTHREAD_FLAGS_IN_JOINQ;
+ psf->saved_state.psd_curframe = thread->curframe;
psf->saved_state.psd_wakeup_time = thread->wakeup_time;
psf->saved_state.psd_wait_data = thread->data;
+ psf->saved_state.psd_state = thread->state;
+ psf->saved_state.psd_flags = thread->flags &
+ (PTHREAD_FLAGS_PRIVATE | PTHREAD_FLAGS_TRACE);
+ psf->saved_state.psd_interrupted = thread->interrupted;
+ psf->saved_state.psd_longjmp_val = thread->longjmp_val;
+ psf->saved_state.psd_sigmask_seqno = thread->sigmask_seqno;
+ psf->saved_state.psd_signo = thread->signo;
+ psf->saved_state.psd_sig_defer_count = thread->sig_defer_count;
}
#endif
diff --git a/lib/libpthread/thread/thr_sigaction.c b/lib/libpthread/thread/thr_sigaction.c
index e78f329..4d13819 100644
--- a/lib/libpthread/thread/thr_sigaction.c
+++ b/lib/libpthread/thread/thr_sigaction.c
@@ -80,7 +80,7 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact)
* handler arguments.
*/
sigfillset(&gact.sa_mask);
- gact.sa_flags = SA_SIGINFO;
+ gact.sa_flags = SA_SIGINFO | SA_ONSTACK;
/*
* Check if the signal handler is being set to
diff --git a/lib/libpthread/thread/thr_sigmask.c b/lib/libpthread/thread/thr_sigmask.c
index bdb0b43..53d0774 100644
--- a/lib/libpthread/thread/thr_sigmask.c
+++ b/lib/libpthread/thread/thr_sigmask.c
@@ -81,6 +81,9 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
break;
}
+ /* Increment the sequence number: */
+ _thread_run->sigmask_seqno++;
+
/*
* Check if there are pending signals for the running
* thread or process that aren't blocked:
OpenPOWER on IntegriCloud