diff options
Diffstat (limited to 'lib')
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: |