diff options
39 files changed, 1332 insertions, 205 deletions
diff --git a/lib/libc_r/uthread/Makefile.inc b/lib/libc_r/uthread/Makefile.inc index 4697305..f46f007 100644 --- a/lib/libc_r/uthread/Makefile.inc +++ b/lib/libc_r/uthread/Makefile.inc @@ -60,6 +60,7 @@ SRCS+= \ uthread_info.c \ uthread_init.c \ uthread_ioctl.c \ + uthread_jmp.c \ uthread_join.c \ uthread_kern.c \ uthread_kill.c \ diff --git a/lib/libc_r/uthread/pthread_private.h b/lib/libc_r/uthread/pthread_private.h index 8a126f4..8bc2d3f 100644 --- a/lib/libc_r/uthread/pthread_private.h +++ b/lib/libc_r/uthread/pthread_private.h @@ -85,7 +85,11 @@ #define PTHREAD_WAITQ_CLEARACTIVE() _waitq_clearactive() #define PTHREAD_WAITQ_SETACTIVE() _waitq_setactive() #else -#define PTHREAD_WAITQ_REMOVE(thrd) TAILQ_REMOVE(&_waitingq,thrd,pqe) +#define PTHREAD_WAITQ_REMOVE(thrd) do { \ + TAILQ_REMOVE(&_waitingq,thrd,pqe); \ + (thrd)->flags &= ~PTHREAD_FLAGS_IN_WAITQ; \ +} while (0) + #define PTHREAD_WAITQ_INSERT(thrd) do { \ if ((thrd)->wakeup_time.tv_sec == -1) \ TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \ @@ -101,6 +105,7 @@ else \ TAILQ_INSERT_BEFORE(tid,thrd,pqe); \ } \ + (thrd)->flags | PTHREAD_FLAGS_IN_WAITQ; \ } while (0) #define PTHREAD_WAITQ_CLEARACTIVE() #define PTHREAD_WAITQ_SETACTIVE() @@ -463,6 +468,12 @@ union pthread_wait_data { }; /* + * Define a continuation routine that can be used to perform a + * transfer of control: + */ +typedef void (*thread_continuation_t) (void *); + +/* * Thread structure. */ struct pthread { @@ -514,6 +525,24 @@ struct pthread { * if sig_saved is FALSE. */ jmp_buf saved_jmp_buf; + jmp_buf *sighandler_jmp_buf; + + /* + * Saved jump buffers for use when doing nested [sig|_]longjmp()s, as + * when doing signal delivery. + */ + union { + jmp_buf jmp; + sigjmp_buf sigjmp; + } nested_jmp; + int longjmp_val; + +#define JMPFLAGS_NONE 0x00 +#define JMPFLAGS_LONGJMP 0x01 +#define JMPFLAGS__LONGJMP 0x02 +#define JMPFLAGS_SIGLONGJMP 0x04 +#define JMPFLAGS_DEFERRED 0x08 + int jmpflags; /* * TRUE if the last state saved was a signal context. FALSE if the @@ -521,6 +550,11 @@ struct pthread { */ int sig_saved; + /* + * Used for tracking delivery of nested signal handlers. + */ + int signal_nest_level; + /* * Cancelability flags - the lower 2 bits are used by cancel * definitions in pthread.h @@ -530,14 +564,22 @@ struct pthread { #define PTHREAD_CANCEL_NEEDED 0x0010 int cancelflags; + thread_continuation_t continuation; + /* * Current signal mask and pending signals. */ sigset_t sigmask; sigset_t sigpend; +#ifndef _NO_UNDISPATCH + /* Non-zero if there are undispatched signals for this thread. */ + int undispatched_signals; +#endif + /* Thread state: */ enum pthread_state state; + enum pthread_state oldstate; /* Time that this thread was last made active. */ struct timeval last_active; @@ -1188,8 +1230,17 @@ pid_t _thread_sys_wait4(pid_t, int *, int, struct rusage *); #ifdef _SYS_POLL_H_ int _thread_sys_poll(struct pollfd *, unsigned, int); #endif + /* #include <sys/mman.h> */ +#ifdef _SYS_MMAN_H_ int _thread_sys_msync(void *, size_t, int); +#endif + +/* #include <setjmp.h> */ +#ifdef _SETJMP_H_ +extern void __longjmp(jmp_buf, int); +extern void __siglongjmp(sigjmp_buf, int); +#endif __END_DECLS #endif /* !_PTHREAD_PRIVATE_H */ diff --git a/lib/libc_r/uthread/uthread_cancel.c b/lib/libc_r/uthread/uthread_cancel.c index de7c491..f22bfb5 100644 --- a/lib/libc_r/uthread/uthread_cancel.c +++ b/lib/libc_r/uthread/uthread_cancel.c @@ -2,11 +2,12 @@ * David Leonard <d@openbsd.org>, 1999. Public domain. * $FreeBSD$ */ - #include <sys/errno.h> #include <pthread.h> #include "pthread_private.h" +static void finish_cancellation(void *arg); + int pthread_cancel(pthread_t pthread) { @@ -71,11 +72,13 @@ pthread_cancel(pthread_t pthread) * queue. Mark the thread as interrupted and * needing cancellation, and set the state to * running. When the thread resumes, it will - * exit after removing itself from the queue. + * remove itself from the queue and call the + * cancellation completion routine. */ pthread->interrupted = 1; pthread->cancelflags |= PTHREAD_CANCEL_NEEDED; PTHREAD_NEW_STATE(pthread,PS_RUNNING); + pthread->continuation = finish_cancellation; break; case PS_DEAD: @@ -172,7 +175,6 @@ pthread_testcancel(void) void _thread_enter_cancellation_point(void) { - /* Look for a cancellation before we block: */ pthread_testcancel(); _thread_run->cancelflags |= PTHREAD_AT_CANCEL_POINT; @@ -181,8 +183,20 @@ _thread_enter_cancellation_point(void) void _thread_leave_cancellation_point(void) { - _thread_run->cancelflags &= ~PTHREAD_AT_CANCEL_POINT; /* Look for a cancellation after we unblock: */ pthread_testcancel(); } + +static void +finish_cancellation(void *arg) +{ + _thread_run->continuation = NULL; + _thread_run->interrupted = 0; + + if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { + _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; + _thread_exit_cleanup(); + pthread_exit(PTHREAD_CANCELED); + } +} diff --git a/lib/libc_r/uthread/uthread_cond.c b/lib/libc_r/uthread/uthread_cond.c index 3e215af..ced48e3 100644 --- a/lib/libc_r/uthread/uthread_cond.c +++ b/lib/libc_r/uthread/uthread_cond.c @@ -157,7 +157,8 @@ pthread_cond_destroy(pthread_cond_t * cond) int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) { - int rval = 0; + int rval = 0; + int interrupted = 0; if (cond == NULL) rval = EINVAL; @@ -238,6 +239,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) if (_thread_run->interrupted != 0) { /* + * Remember that this thread + * was interrupted: + */ + interrupted = 1; + + /* * Lock the condition variable * while removing the thread. */ @@ -273,11 +280,8 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) break; } - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (interrupted != 0 && _thread_run->continuation != NULL) + _thread_run->continuation((void *) _thread_run); _thread_leave_cancellation_point(); } @@ -290,7 +294,8 @@ int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec * abstime) { - int rval = 0; + int rval = 0; + int interrupted = 0; if (cond == NULL || abstime == NULL) rval = EINVAL; @@ -386,6 +391,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, rval = _mutex_cv_lock(mutex); } else { + /* + * Remember if this thread was + * interrupted: + */ + interrupted = _thread_run->interrupted; + /* Lock the condition variable structure: */ _SPINLOCK(&(*cond)->lock); @@ -431,11 +442,8 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, break; } - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (interrupted != 0 && _thread_run->continuation != NULL) + _thread_run->continuation((void *) _thread_run); _thread_leave_cancellation_point(); } diff --git a/lib/libc_r/uthread/uthread_create.c b/lib/libc_r/uthread/uthread_create.c index fd9e746..b8a1c46 100644 --- a/lib/libc_r/uthread/uthread_create.c +++ b/lib/libc_r/uthread/uthread_create.c @@ -189,19 +189,24 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, */ #if defined(__FreeBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[0]._jb[2] = (long) _thread_start; - new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = 0; - new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = (long) _thread_start; + new_thread->saved_jmp_buf[0]._jb[2] = + (long)_thread_start; + new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = + 0; + new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = + (long)_thread_start; #else - new_thread->saved_jmp_buf[0]._jb[0] = (long) _thread_start; + new_thread->saved_jmp_buf[0]._jb[0] = + (long)_thread_start; #endif #elif defined(__NetBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[2] = (long) _thread_start; + new_thread->saved_jmp_buf[2] = (long)_thread_start; new_thread->saved_jmp_buf[4 + R_RA] = 0; - new_thread->saved_jmp_buf[4 + R_T12] = (long) _thread_start; + new_thread->saved_jmp_buf[4 + R_T12] = + (long)_thread_start; #else - new_thread->saved_jmp_buf[0] = (long) _thread_start; + new_thread->saved_jmp_buf[0] = (long)_thread_start; #endif #else #error "Don't recognize this operating system!" @@ -210,15 +215,22 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* The stack starts high and builds down: */ #if defined(__FreeBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[0]._jb[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double); + new_thread->saved_jmp_buf[0]._jb[4 + R_SP] = + (long)new_thread->stack + pattr->stacksize_attr + - sizeof(double); #else - new_thread->saved_jmp_buf[0]._jb[2] = (int) (new_thread->stack + pattr->stacksize_attr - sizeof(double)); + new_thread->saved_jmp_buf[0]._jb[2] = + (int)(new_thread->stack + pattr->stacksize_attr - + sizeof(double)); #endif #elif defined(__NetBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double); + new_thread->saved_jmp_buf[4 + R_SP] = + (long)new_thread->stack + pattr->stacksize_attr - + sizeof(double); #else - new_thread->saved_jmp_buf[2] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double); + new_thread->saved_jmp_buf[2] = (long)new_thread->stack + + pattr->stacksize_attr - sizeof(double); #endif #else #error "Don't recognize this operating system!" @@ -263,6 +275,8 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, new_thread->flags = 0; new_thread->poll_data.nfds = 0; new_thread->poll_data.fds = NULL; + new_thread->jmpflags = 0; + new_thread->continuation = NULL; /* * Defer signals to protect the scheduling queues diff --git a/lib/libc_r/uthread/uthread_fd.c b/lib/libc_r/uthread/uthread_fd.c index d9a1636..77ccad1 100644 --- a/lib/libc_r/uthread/uthread_fd.c +++ b/lib/libc_r/uthread/uthread_fd.c @@ -480,14 +480,10 @@ _thread_fd_lock(int fd, int lock_type, struct timespec * timeout) _SPINUNLOCK(&_thread_fd_table[fd]->lock); if (_thread_run->interrupted != 0) { - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) == 0) { - ret = -1; - errno = EINTR; - } else { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + ret = -1; + errno = EINTR; + if (_thread_run->continuation != NULL) + _thread_run->continuation((void *)_thread_run); } } @@ -816,14 +812,10 @@ _thread_fd_lock_debug(int fd, int lock_type, struct timespec * timeout, _SPINUNLOCK(&_thread_fd_table[fd]->lock); if (_thread_run->interrupted != 0) { - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) == 0) { - ret = -1; - errno = EINTR; - } else { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + ret = -1; + errno = EINTR; + if (_thread_run->continuation != NULL) + _thread_run->continuation((void *)_thread_run); } } diff --git a/lib/libc_r/uthread/uthread_file.c b/lib/libc_r/uthread/uthread_file.c index f1bac17..88b6a8f 100644 --- a/lib/libc_r/uthread/uthread_file.c +++ b/lib/libc_r/uthread/uthread_file.c @@ -255,12 +255,9 @@ _flockfile_debug(FILE * fp, char *fname, int lineno) _thread_kern_sig_undefer(); - if (((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) && - (_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (_thread_run->interrupted != 0 && + _thread_run->continuation != NULL) + _thread_run->continuation((void *)_thread_run); } } return; diff --git a/lib/libc_r/uthread/uthread_init.c b/lib/libc_r/uthread/uthread_init.c index b6429bb..dd5f53f 100644 --- a/lib/libc_r/uthread/uthread_init.c +++ b/lib/libc_r/uthread/uthread_init.c @@ -184,6 +184,10 @@ _thread_init(void) /* Initialize the scheduling switch hook routine: */ _sched_switch_hook = NULL; + /* Give this thread default attributes: */ + memcpy((void *) &_thread_initial->attr, &pthread_attr_default, + sizeof(struct pthread_attr)); + /* Initialize the thread stack cache: */ SLIST_INIT(&_stackq); @@ -199,6 +203,14 @@ _thread_init(void) -1, 0) == MAP_FAILED) PANIC("Cannot allocate red zone for initial thread"); + /* Set the main thread stack pointer. */ + _thread_initial->stack = (void *) USRSTACK - + PTHREAD_STACK_INITIAL; + + /* Set the stack attributes: */ + _thread_initial->attr.stackaddr_attr = _thread_initial->stack; + _thread_initial->attr.stacksize_attr = PTHREAD_STACK_INITIAL; + /* * Write a magic value to the thread structure * to help identify valid ones: diff --git a/lib/libc_r/uthread/uthread_jmp.c b/lib/libc_r/uthread/uthread_jmp.c new file mode 100644 index 0000000..4be77cf --- /dev/null +++ b/lib/libc_r/uthread/uthread_jmp.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2000 Jason Evans <jasone@canonware.com>. + * All rights reserved. + * Copyright (C) 2000 Daniel M. Eischen <eischen@vigrid.com>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <unistd.h> +#include <setjmp.h> +#include <sys/param.h> +#include <sys/user.h> +#ifdef _THREAD_SAFE +#include <machine/reg.h> +#include <pthread.h> +#include "pthread_private.h" + +/* + * Offset into the jmp_buf. This is highly machine-dependent, but is a + * necessary evil in order to compare stack pointers and make decisions based on + * where a *longjmp() is jumping to. + */ +#if defined(__i386__) +#define JMP_BUF_SP_OFFSET 2 +#elif defined(__alpha) +#define JMP_BUF_SP_OFFSET (4 + R_SP) +#else +#error "Don't recognize this architecture!" +#endif + +void +_libc_siglongjmp(sigjmp_buf env, int savemask) +{ + void *jmp_stackp; + void *stack_begin, *stack_end; + + if (_thread_run->signal_nest_level == 0) + __siglongjmp(env, savemask); + + /* Get the stack pointer from the jump buffer. */ + jmp_stackp = (void *)env->_sjb[JMP_BUF_SP_OFFSET]; + + /* Get the bounds of the current threads stack. */ + if (_thread_run->stack != NULL) { + stack_begin = _thread_run->stack; + stack_end = stack_begin + _thread_run->attr.stacksize_attr; + } else { + stack_end = (void *)USRSTACK; + stack_begin = stack_end - PTHREAD_STACK_INITIAL; + } + + /* + * Make sure we aren't jumping to a different stack. Make sure + * jmp_stackp is between stack_begin and stack end, to correctly detect + * this condition regardless of whether the stack grows up or down. + */ + if (((jmp_stackp < stack_begin) && (jmp_stackp < stack_end)) || + ((jmp_stackp > stack_begin) && (jmp_stackp > stack_end))) + PANIC("longjmp()ing between thread contexts is undefined by " + "POSIX 1003.1"); + + memcpy(_thread_run->nested_jmp.sigjmp, env, + sizeof(_thread_run->nested_jmp.sigjmp)); + + /* + * Only save oldstate once so that dispatching multiple signals will not + * lose the thread's original state. + */ + if (_thread_run->jmpflags == JMPFLAGS_NONE) + _thread_run->oldstate = _thread_run->state; + PTHREAD_SET_STATE(_thread_run, PS_RUNNING); + _thread_run->jmpflags = JMPFLAGS_SIGLONGJMP; + _thread_run->longjmp_val = savemask; + ___longjmp(*_thread_run->sighandler_jmp_buf, 1); +} + +__weak_reference(_libc_siglongjmp, siglongjmp); + +void +_libc_longjmp(jmp_buf env, int val) +{ + void *jmp_stackp; + void *stack_begin, *stack_end; + + if (_thread_run->signal_nest_level == 0) + __longjmp(env, val); + + /* Get the stack pointer from the jump buffer. */ + jmp_stackp = (void *)env->_jb[JMP_BUF_SP_OFFSET]; + + /* Get the bounds of the current threads stack. */ + if (_thread_run->stack != NULL) { + stack_begin = _thread_run->stack; + stack_end = stack_begin + _thread_run->attr.stacksize_attr; + } else { + stack_end = (void *)USRSTACK; + stack_begin = stack_end - PTHREAD_STACK_INITIAL; + } + + /* + * Make sure we aren't jumping to a different stack. Make sure + * jmp_stackp is between stack_begin and stack end, to correctly detect + * this condition regardless of whether the stack grows up or down. + */ + if (((jmp_stackp < stack_begin) && (jmp_stackp < stack_end)) || + ((jmp_stackp > stack_begin) && (jmp_stackp > stack_end))) + PANIC("longjmp()ing between thread contexts is undefined by " + "POSIX 1003.1"); + + memcpy(_thread_run->nested_jmp.jmp, env, + sizeof(_thread_run->nested_jmp.jmp)); + + /* + * Only save oldstate once so that dispatching multiple signals will not + * lose the thread's original state. + */ + if (_thread_run->jmpflags == JMPFLAGS_NONE) + _thread_run->oldstate = _thread_run->state; + PTHREAD_SET_STATE(_thread_run, PS_RUNNING); + _thread_run->jmpflags = JMPFLAGS_LONGJMP; + _thread_run->longjmp_val = val; + ___longjmp(*_thread_run->sighandler_jmp_buf, 1); +} + +__weak_reference(_libc_longjmp, longjmp); + +void +_libc__longjmp(jmp_buf env, int val) +{ + void *jmp_stackp; + void *stack_begin, *stack_end; + + if (_thread_run->signal_nest_level == 0) + ___longjmp(env, val); + + /* Get the stack pointer from the jump buffer. */ + jmp_stackp = (void *)env->_jb[JMP_BUF_SP_OFFSET]; + + /* Get the bounds of the current threads stack. */ + if (_thread_run->stack != NULL) { + stack_begin = _thread_run->stack; + stack_end = stack_begin + _thread_run->attr.stacksize_attr; + } else { + stack_end = (void *)USRSTACK; + stack_begin = stack_end - PTHREAD_STACK_INITIAL; + } + + /* + * Make sure we aren't jumping to a different stack. Make sure + * jmp_stackp is between stack_begin and stack end, to correctly detect + * this condition regardless of whether the stack grows up or down. + */ + if (((jmp_stackp < stack_begin) && (jmp_stackp < stack_end)) || + ((jmp_stackp > stack_begin) && (jmp_stackp > stack_end))) + PANIC("longjmp()ing between thread contexts is undefined by " + "POSIX 1003.1"); + + memcpy(_thread_run->nested_jmp.jmp, env, + sizeof(_thread_run->nested_jmp.jmp)); + + /* + * Only save oldstate once so that dispatching multiple signals will not + * lose the thread's original state. + */ + if (_thread_run->jmpflags == JMPFLAGS_NONE) + _thread_run->oldstate = _thread_run->state; + PTHREAD_SET_STATE(_thread_run, PS_RUNNING); + _thread_run->jmpflags = JMPFLAGS__LONGJMP; + _thread_run->longjmp_val = val; + ___longjmp(*_thread_run->sighandler_jmp_buf, 1); +} + +__weak_reference(_libc__longjmp, _longjmp); +#endif diff --git a/lib/libc_r/uthread/uthread_join.c b/lib/libc_r/uthread/uthread_join.c index 155dc64..1cffc96 100644 --- a/lib/libc_r/uthread/uthread_join.c +++ b/lib/libc_r/uthread/uthread_join.c @@ -97,11 +97,9 @@ pthread_join(pthread_t pthread, void **thread_return) _thread_kern_sig_undefer(); - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (_thread_run->interrupted != 0 && + _thread_run->continuation != NULL) + _thread_run->continuation(_thread_run); /* Check if the thread is not detached: */ if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) { diff --git a/lib/libc_r/uthread/uthread_kern.c b/lib/libc_r/uthread/uthread_kern.c index b3fbc3a..b833061 100644 --- a/lib/libc_r/uthread/uthread_kern.c +++ b/lib/libc_r/uthread/uthread_kern.c @@ -122,6 +122,17 @@ __asm__("fnsave %0": :"m"(*fdata)); pthread_testcancel(); } +#ifndef _NO_UNDISPATCH + /* + * Check for undispatched signals due to calls to + * pthread_kill(). + */ + if (_thread_run->undispatched_signals != 0) { + _thread_run->undispatched_signals = 0; + _dispatch_signals(); + } +#endif + if (_sched_switch_hook != NULL) { /* Run the installed switch hook: */ thread_run_switch_hook(_last_user_thread, _thread_run); @@ -365,8 +376,7 @@ __asm__("fnsave %0": :"m"(*fdata)); * something happens that changes this condition: */ _thread_kern_poll(1); - } - else { + } else { /* Remove the thread from the ready queue: */ PTHREAD_PRIOQ_REMOVE(pthread_h); @@ -537,8 +547,38 @@ __asm__("fnsave %0": :"m"(*fdata)); } } + /* + * Check if this thread is being continued from a + * longjmp() out of a signal handler: + */ + if ((_thread_run->jmpflags & JMPFLAGS_LONGJMP) != 0) { + _thread_run->jmpflags = 0; + __longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } + /* + * Check if this thread is being continued from a + * _longjmp() out of a signal handler: + */ + else if ((_thread_run->jmpflags & JMPFLAGS__LONGJMP) != + 0) { + _thread_run->jmpflags = 0; + ___longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } + /* + * Check if this thread is being continued from a + * siglongjmp() out of a signal handler: + */ + else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) + != 0) { + _thread_run->jmpflags = 0; + __siglongjmp( + _thread_run->nested_jmp.sigjmp, + _thread_run->longjmp_val); + } /* Check if a signal context was saved: */ - if (_thread_run->sig_saved == 1) { + else if (_thread_run->sig_saved == 1) { #ifndef __alpha__ /* * Point to the floating point data in the @@ -571,7 +611,7 @@ __asm__("fnsave %0": :"m"(*fdata)); * was context switched out (by a longjmp to * a different thread): */ - longjmp(_thread_run->saved_jmp_buf, 1); + __longjmp(_thread_run->saved_jmp_buf, 1); } /* This point should not be reached. */ diff --git a/lib/libc_r/uthread/uthread_mutex.c b/lib/libc_r/uthread/uthread_mutex.c index 6526a35..6d75ea5 100644 --- a/lib/libc_r/uthread/uthread_mutex.c +++ b/lib/libc_r/uthread/uthread_mutex.c @@ -622,11 +622,9 @@ pthread_mutex_lock(pthread_mutex_t * mutex) */ _thread_kern_sig_undefer(); - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (_thread_run->interrupted != 0 && + _thread_run->continuation != NULL) + _thread_run->continuation((void *) _thread_run); } /* Return the completion status: */ diff --git a/lib/libc_r/uthread/uthread_priority_queue.c b/lib/libc_r/uthread/uthread_priority_queue.c index 6fdf844..1b9fcba 100644 --- a/lib/libc_r/uthread/uthread_priority_queue.c +++ b/lib/libc_r/uthread/uthread_priority_queue.c @@ -287,7 +287,7 @@ _waitq_insert(pthread_t pthread) TAILQ_INSERT_TAIL(&_waitingq, pthread, pqe); else { tid = TAILQ_FIRST(&_waitingq); - while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) && + while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) && ((tid->wakeup_time.tv_sec < pthread->wakeup_time.tv_sec) || ((tid->wakeup_time.tv_sec == pthread->wakeup_time.tv_sec) && (tid->wakeup_time.tv_nsec <= pthread->wakeup_time.tv_nsec)))) diff --git a/lib/libc_r/uthread/uthread_setprio.c b/lib/libc_r/uthread/uthread_setprio.c index 5f7b44a..c630db5 100644 --- a/lib/libc_r/uthread/uthread_setprio.c +++ b/lib/libc_r/uthread/uthread_setprio.c @@ -31,7 +31,6 @@ * * $FreeBSD$ */ -#include <errno.h> #ifdef _THREAD_SAFE #include <pthread.h> #include "pthread_private.h" diff --git a/lib/libc_r/uthread/uthread_sig.c b/lib/libc_r/uthread/uthread_sig.c index 160bdab..0f18477 100644 --- a/lib/libc_r/uthread/uthread_sig.c +++ b/lib/libc_r/uthread/uthread_sig.c @@ -37,19 +37,24 @@ #include <signal.h> #include <fcntl.h> #include <unistd.h> +#include <setjmp.h> #include <errno.h> #ifdef _THREAD_SAFE #include <pthread.h> #include "pthread_private.h" /* Prototypes: */ -static void _thread_sig_check_state(pthread_t pthread, int sig); +static void thread_sig_check_state(pthread_t pthread, int sig); +static void thread_sig_finish_longjmp(void *arg); +static void handle_state_change(pthread_t pthread); + /* Static variables: */ static spinlock_t signal_lock = _SPINLOCK_INITIALIZER; static unsigned int pending_sigs[NSIG]; static unsigned int handled_sigs[NSIG]; static int volatile check_pending = 0; +static int volatile check_waiting = 0; /* Initialize signal handling facility: */ void @@ -73,7 +78,7 @@ _thread_sig_init(void) void _thread_sig_handler(int sig, int code, ucontext_t * scp) { - pthread_t pthread; + pthread_t pthread, pthread_next; int i; char c; @@ -126,8 +131,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) /* Indicate that there are queued signals in the pipe. */ _sigq_check_reqd = 1; - } - else { + } else { if (_atomic_lock(&signal_lock.access_lock)) { /* There is another signal handler running: */ pending_sigs[sig - 1]++; @@ -145,6 +149,18 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) signal_lock.access_lock = 0; else { sigaddset(&pthread->sigmask, sig); + + /* + * Make sure not to deliver the same signal to + * the thread twice. sigpend is potentially + * modified by the call chain + * _thread_sig_handle() --> + * thread_sig_check_state(), which can happen + * just above. + */ + if (sigismember(&pthread->sigpend, sig)) + sigdelset(&pthread->sigpend, sig); + signal_lock.access_lock = 0; _thread_sig_deliver(pthread, sig); sigdelset(&pthread->sigmask, sig); @@ -161,18 +177,57 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) pthread = _thread_sig_handle(i, scp); if (pthread != NULL) { sigaddset(&pthread->sigmask, i); + /* Save the old state: */ + pthread->oldstate = pthread->state; signal_lock.access_lock = 0; _thread_sig_deliver(pthread, i); sigdelset(&pthread->sigmask, i); if (_atomic_lock(&signal_lock.access_lock)) { check_pending = 1; + /* + * Have the lock holder take care + * of any state changes: + */ + if (pthread->state != pthread->oldstate) + check_waiting = 1; return; } + if (pthread->state != pthread->oldstate) + handle_state_change(pthread); } } } + while (check_waiting != 0) { + check_waiting = 0; + /* + * Enter a loop to wake up all threads waiting + * for a process to complete: + */ + for (pthread = TAILQ_FIRST(&_waitingq); + pthread != NULL; pthread = pthread_next) { + pthread_next = TAILQ_NEXT(pthread, pqe); + if (pthread->state == PS_RUNNING) + handle_state_change(pthread); + } + } + /* Release the lock: */ signal_lock.access_lock = 0; } + + /* + * Check to see if the current thread performed a + * [sig|_]longjmp() out of a signal handler. + */ + if ((_thread_run->jmpflags & (JMPFLAGS_LONGJMP | + JMPFLAGS__LONGJMP)) != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + __longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + __siglongjmp(_thread_run->nested_jmp.sigjmp, + _thread_run->longjmp_val); + } } } @@ -353,7 +408,7 @@ _thread_sig_handle(int sig, ucontext_t * scp) * Perform any state changes due to signal * arrival: */ - _thread_sig_check_state(pthread, sig); + thread_sig_check_state(pthread, sig); return (pthread); } } @@ -363,9 +418,99 @@ _thread_sig_handle(int sig, ucontext_t * scp) return (NULL); } +static void +thread_sig_finish_longjmp(void *arg) +{ + /* + * Check to see if the current thread performed a [_]longjmp() out of a + * signal handler. + */ + if ((_thread_run->jmpflags & (JMPFLAGS_LONGJMP | JMPFLAGS__LONGJMP)) + != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + _thread_run->continuation = NULL; + __longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } + /* + * Check to see if the current thread performed a siglongjmp + * out of a signal handler: + */ + else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + _thread_run->continuation = NULL; + __siglongjmp(_thread_run->nested_jmp.sigjmp, + _thread_run->longjmp_val); + } +} + +static void +handle_state_change(pthread_t pthread) +{ + /* + * We should only need to handle threads whose state was + * changed to running: + */ + if (pthread->state == PS_RUNNING) { + switch (pthread->oldstate) { + /* + * States which do not change when a signal is trapped: + */ + case PS_DEAD: + case PS_DEADLOCK: + case PS_RUNNING: + case PS_SIGTHREAD: + case PS_STATE_MAX: + break; + + /* + * States which need to return to critical sections + * before they can switch contexts: + */ + case PS_COND_WAIT: + case PS_FDLR_WAIT: + case PS_FDLW_WAIT: + case PS_FILE_WAIT: + case PS_JOIN: + case PS_MUTEX_WAIT: + /* Indicate that the thread was interrupted: */ + pthread->interrupted = 1; + /* + * Defer the [sig|_]longjmp until leaving the critical + * region: + */ + pthread->jmpflags |= JMPFLAGS_DEFERRED; + + /* Set the continuation routine: */ + pthread->continuation = thread_sig_finish_longjmp; + /* FALLTHROUGH */ + case PS_FDR_WAIT: + case PS_FDW_WAIT: + case PS_POLL_WAIT: + case PS_SELECT_WAIT: + case PS_SIGSUSPEND: + case PS_SIGWAIT: + case PS_SLEEP_WAIT: + case PS_SPINBLOCK: + case PS_SUSPENDED: + case PS_WAIT_WAIT: + if ((pthread->flags & PTHREAD_FLAGS_IN_WAITQ) != 0) { + PTHREAD_WAITQ_REMOVE(pthread); + if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ) + PTHREAD_WORKQ_REMOVE(pthread); + } + break; + } + + if ((pthread->flags & PTHREAD_FLAGS_IN_PRIOQ) == 0) + PTHREAD_PRIOQ_INSERT_TAIL(pthread); + } +} + + /* Perform thread specific actions in response to a signal: */ static void -_thread_sig_check_state(pthread_t pthread, int sig) +thread_sig_check_state(pthread_t pthread, int sig) { /* * Process according to thread state: @@ -402,7 +547,6 @@ _thread_sig_check_state(pthread_t pthread, int sig) sigaddset(&pthread->sigpend,sig); break; - /* * The wait state is a special case due to the handling of * SIGCHLD signals. @@ -483,12 +627,25 @@ _thread_sig_send(pthread_t pthread, int sig) } else if (pthread->state != PS_SIGWAIT && !sigismember(&pthread->sigmask, sig)) { /* Perform any state changes due to signal arrival: */ - _thread_sig_check_state(pthread, sig); + thread_sig_check_state(pthread, sig); - /* Call the installed signal handler: */ - _thread_sig_deliver(pthread, sig); - } - else { +#ifndef _NO_UNDISPATCH + if (_thread_run != pthread) { + /* + * Make a note to call the signal handler once + * the signaled thread is running. This is + * necessary in order to make sure that the + * signal is delivered on the correct stack. + */ + pthread->undispatched_signals++; + } else { +#endif + /* Call the installed signal handler. */ + _thread_sig_deliver(pthread, sig); +#ifndef _NO_UNDISPATCH + } +#endif + } else { /* Increment the pending signal count. */ sigaddset(&pthread->sigpend,sig); } @@ -553,6 +710,7 @@ _thread_sig_deliver(pthread_t pthread, int sig) { sigset_t mask; pthread_t pthread_saved; + jmp_buf jb, *saved_sighandler_jmp_buf; /* * Check that a custom handler is installed @@ -568,7 +726,7 @@ _thread_sig_deliver(pthread_t pthread, int sig) /* * Add the current signal and signal handler - * mask to the threads current signal mask: + * mask to the thread's current signal mask: */ SIGSETOR(pthread->sigmask, _thread_sigact[sig - 1].sa_mask); sigaddset(&pthread->sigmask, sig); @@ -577,16 +735,36 @@ _thread_sig_deliver(pthread_t pthread, int sig) if (_thread_run->sig_defer_count > 0) pthread->sig_defer_count++; - _thread_run = pthread; + /* Increment the number of nested signals being handled. */ + pthread->signal_nest_level++; /* - * Dispatch the signal via the custom signal - * handler: + * The jump buffer is allocated off the stack and the current + * jump buffer is saved. If the signal handler tries to + * [sig|_]longjmp(), our version of [sig|_]longjmp() will copy + * the user supplied jump buffer into + * _thread_run->nested_jmp.[sig]jmp and _longjmp() back to here. */ - (*(_thread_sigact[sig - 1].sa_handler))(sig); + saved_sighandler_jmp_buf = pthread->sighandler_jmp_buf; + pthread->sighandler_jmp_buf = &jb; + + _thread_run = pthread; + + if (_setjmp(jb) == 0) { + /* + * Dispatch the signal via the custom signal + * handler: + */ + (*(_thread_sigact[sig - 1].sa_handler))(sig); + } _thread_run = pthread_saved; + pthread->sighandler_jmp_buf = saved_sighandler_jmp_buf; + + /* Decrement the signal nest level. */ + pthread->signal_nest_level--; + /* Current thread inside critical region? */ if (_thread_run->sig_defer_count > 0) pthread->sig_defer_count--; diff --git a/lib/libkse/thread/Makefile.inc b/lib/libkse/thread/Makefile.inc index 4697305..f46f007 100644 --- a/lib/libkse/thread/Makefile.inc +++ b/lib/libkse/thread/Makefile.inc @@ -60,6 +60,7 @@ SRCS+= \ uthread_info.c \ uthread_init.c \ uthread_ioctl.c \ + uthread_jmp.c \ uthread_join.c \ uthread_kern.c \ uthread_kill.c \ diff --git a/lib/libkse/thread/thr_cancel.c b/lib/libkse/thread/thr_cancel.c index de7c491..f22bfb5 100644 --- a/lib/libkse/thread/thr_cancel.c +++ b/lib/libkse/thread/thr_cancel.c @@ -2,11 +2,12 @@ * David Leonard <d@openbsd.org>, 1999. Public domain. * $FreeBSD$ */ - #include <sys/errno.h> #include <pthread.h> #include "pthread_private.h" +static void finish_cancellation(void *arg); + int pthread_cancel(pthread_t pthread) { @@ -71,11 +72,13 @@ pthread_cancel(pthread_t pthread) * queue. Mark the thread as interrupted and * needing cancellation, and set the state to * running. When the thread resumes, it will - * exit after removing itself from the queue. + * remove itself from the queue and call the + * cancellation completion routine. */ pthread->interrupted = 1; pthread->cancelflags |= PTHREAD_CANCEL_NEEDED; PTHREAD_NEW_STATE(pthread,PS_RUNNING); + pthread->continuation = finish_cancellation; break; case PS_DEAD: @@ -172,7 +175,6 @@ pthread_testcancel(void) void _thread_enter_cancellation_point(void) { - /* Look for a cancellation before we block: */ pthread_testcancel(); _thread_run->cancelflags |= PTHREAD_AT_CANCEL_POINT; @@ -181,8 +183,20 @@ _thread_enter_cancellation_point(void) void _thread_leave_cancellation_point(void) { - _thread_run->cancelflags &= ~PTHREAD_AT_CANCEL_POINT; /* Look for a cancellation after we unblock: */ pthread_testcancel(); } + +static void +finish_cancellation(void *arg) +{ + _thread_run->continuation = NULL; + _thread_run->interrupted = 0; + + if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { + _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; + _thread_exit_cleanup(); + pthread_exit(PTHREAD_CANCELED); + } +} diff --git a/lib/libkse/thread/thr_cond.c b/lib/libkse/thread/thr_cond.c index 3e215af..ced48e3 100644 --- a/lib/libkse/thread/thr_cond.c +++ b/lib/libkse/thread/thr_cond.c @@ -157,7 +157,8 @@ pthread_cond_destroy(pthread_cond_t * cond) int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) { - int rval = 0; + int rval = 0; + int interrupted = 0; if (cond == NULL) rval = EINVAL; @@ -238,6 +239,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) if (_thread_run->interrupted != 0) { /* + * Remember that this thread + * was interrupted: + */ + interrupted = 1; + + /* * Lock the condition variable * while removing the thread. */ @@ -273,11 +280,8 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) break; } - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (interrupted != 0 && _thread_run->continuation != NULL) + _thread_run->continuation((void *) _thread_run); _thread_leave_cancellation_point(); } @@ -290,7 +294,8 @@ int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec * abstime) { - int rval = 0; + int rval = 0; + int interrupted = 0; if (cond == NULL || abstime == NULL) rval = EINVAL; @@ -386,6 +391,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, rval = _mutex_cv_lock(mutex); } else { + /* + * Remember if this thread was + * interrupted: + */ + interrupted = _thread_run->interrupted; + /* Lock the condition variable structure: */ _SPINLOCK(&(*cond)->lock); @@ -431,11 +442,8 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, break; } - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (interrupted != 0 && _thread_run->continuation != NULL) + _thread_run->continuation((void *) _thread_run); _thread_leave_cancellation_point(); } diff --git a/lib/libkse/thread/thr_create.c b/lib/libkse/thread/thr_create.c index fd9e746..b8a1c46 100644 --- a/lib/libkse/thread/thr_create.c +++ b/lib/libkse/thread/thr_create.c @@ -189,19 +189,24 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, */ #if defined(__FreeBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[0]._jb[2] = (long) _thread_start; - new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = 0; - new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = (long) _thread_start; + new_thread->saved_jmp_buf[0]._jb[2] = + (long)_thread_start; + new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = + 0; + new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = + (long)_thread_start; #else - new_thread->saved_jmp_buf[0]._jb[0] = (long) _thread_start; + new_thread->saved_jmp_buf[0]._jb[0] = + (long)_thread_start; #endif #elif defined(__NetBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[2] = (long) _thread_start; + new_thread->saved_jmp_buf[2] = (long)_thread_start; new_thread->saved_jmp_buf[4 + R_RA] = 0; - new_thread->saved_jmp_buf[4 + R_T12] = (long) _thread_start; + new_thread->saved_jmp_buf[4 + R_T12] = + (long)_thread_start; #else - new_thread->saved_jmp_buf[0] = (long) _thread_start; + new_thread->saved_jmp_buf[0] = (long)_thread_start; #endif #else #error "Don't recognize this operating system!" @@ -210,15 +215,22 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* The stack starts high and builds down: */ #if defined(__FreeBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[0]._jb[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double); + new_thread->saved_jmp_buf[0]._jb[4 + R_SP] = + (long)new_thread->stack + pattr->stacksize_attr + - sizeof(double); #else - new_thread->saved_jmp_buf[0]._jb[2] = (int) (new_thread->stack + pattr->stacksize_attr - sizeof(double)); + new_thread->saved_jmp_buf[0]._jb[2] = + (int)(new_thread->stack + pattr->stacksize_attr - + sizeof(double)); #endif #elif defined(__NetBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double); + new_thread->saved_jmp_buf[4 + R_SP] = + (long)new_thread->stack + pattr->stacksize_attr - + sizeof(double); #else - new_thread->saved_jmp_buf[2] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double); + new_thread->saved_jmp_buf[2] = (long)new_thread->stack + + pattr->stacksize_attr - sizeof(double); #endif #else #error "Don't recognize this operating system!" @@ -263,6 +275,8 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, new_thread->flags = 0; new_thread->poll_data.nfds = 0; new_thread->poll_data.fds = NULL; + new_thread->jmpflags = 0; + new_thread->continuation = NULL; /* * Defer signals to protect the scheduling queues diff --git a/lib/libkse/thread/thr_init.c b/lib/libkse/thread/thr_init.c index b6429bb..dd5f53f 100644 --- a/lib/libkse/thread/thr_init.c +++ b/lib/libkse/thread/thr_init.c @@ -184,6 +184,10 @@ _thread_init(void) /* Initialize the scheduling switch hook routine: */ _sched_switch_hook = NULL; + /* Give this thread default attributes: */ + memcpy((void *) &_thread_initial->attr, &pthread_attr_default, + sizeof(struct pthread_attr)); + /* Initialize the thread stack cache: */ SLIST_INIT(&_stackq); @@ -199,6 +203,14 @@ _thread_init(void) -1, 0) == MAP_FAILED) PANIC("Cannot allocate red zone for initial thread"); + /* Set the main thread stack pointer. */ + _thread_initial->stack = (void *) USRSTACK - + PTHREAD_STACK_INITIAL; + + /* Set the stack attributes: */ + _thread_initial->attr.stackaddr_attr = _thread_initial->stack; + _thread_initial->attr.stacksize_attr = PTHREAD_STACK_INITIAL; + /* * Write a magic value to the thread structure * to help identify valid ones: diff --git a/lib/libkse/thread/thr_join.c b/lib/libkse/thread/thr_join.c index 155dc64..1cffc96 100644 --- a/lib/libkse/thread/thr_join.c +++ b/lib/libkse/thread/thr_join.c @@ -97,11 +97,9 @@ pthread_join(pthread_t pthread, void **thread_return) _thread_kern_sig_undefer(); - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (_thread_run->interrupted != 0 && + _thread_run->continuation != NULL) + _thread_run->continuation(_thread_run); /* Check if the thread is not detached: */ if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) { diff --git a/lib/libkse/thread/thr_kern.c b/lib/libkse/thread/thr_kern.c index b3fbc3a..b833061 100644 --- a/lib/libkse/thread/thr_kern.c +++ b/lib/libkse/thread/thr_kern.c @@ -122,6 +122,17 @@ __asm__("fnsave %0": :"m"(*fdata)); pthread_testcancel(); } +#ifndef _NO_UNDISPATCH + /* + * Check for undispatched signals due to calls to + * pthread_kill(). + */ + if (_thread_run->undispatched_signals != 0) { + _thread_run->undispatched_signals = 0; + _dispatch_signals(); + } +#endif + if (_sched_switch_hook != NULL) { /* Run the installed switch hook: */ thread_run_switch_hook(_last_user_thread, _thread_run); @@ -365,8 +376,7 @@ __asm__("fnsave %0": :"m"(*fdata)); * something happens that changes this condition: */ _thread_kern_poll(1); - } - else { + } else { /* Remove the thread from the ready queue: */ PTHREAD_PRIOQ_REMOVE(pthread_h); @@ -537,8 +547,38 @@ __asm__("fnsave %0": :"m"(*fdata)); } } + /* + * Check if this thread is being continued from a + * longjmp() out of a signal handler: + */ + if ((_thread_run->jmpflags & JMPFLAGS_LONGJMP) != 0) { + _thread_run->jmpflags = 0; + __longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } + /* + * Check if this thread is being continued from a + * _longjmp() out of a signal handler: + */ + else if ((_thread_run->jmpflags & JMPFLAGS__LONGJMP) != + 0) { + _thread_run->jmpflags = 0; + ___longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } + /* + * Check if this thread is being continued from a + * siglongjmp() out of a signal handler: + */ + else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) + != 0) { + _thread_run->jmpflags = 0; + __siglongjmp( + _thread_run->nested_jmp.sigjmp, + _thread_run->longjmp_val); + } /* Check if a signal context was saved: */ - if (_thread_run->sig_saved == 1) { + else if (_thread_run->sig_saved == 1) { #ifndef __alpha__ /* * Point to the floating point data in the @@ -571,7 +611,7 @@ __asm__("fnsave %0": :"m"(*fdata)); * was context switched out (by a longjmp to * a different thread): */ - longjmp(_thread_run->saved_jmp_buf, 1); + __longjmp(_thread_run->saved_jmp_buf, 1); } /* This point should not be reached. */ diff --git a/lib/libkse/thread/thr_mutex.c b/lib/libkse/thread/thr_mutex.c index 6526a35..6d75ea5 100644 --- a/lib/libkse/thread/thr_mutex.c +++ b/lib/libkse/thread/thr_mutex.c @@ -622,11 +622,9 @@ pthread_mutex_lock(pthread_mutex_t * mutex) */ _thread_kern_sig_undefer(); - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (_thread_run->interrupted != 0 && + _thread_run->continuation != NULL) + _thread_run->continuation((void *) _thread_run); } /* Return the completion status: */ diff --git a/lib/libkse/thread/thr_priority_queue.c b/lib/libkse/thread/thr_priority_queue.c index 6fdf844..1b9fcba 100644 --- a/lib/libkse/thread/thr_priority_queue.c +++ b/lib/libkse/thread/thr_priority_queue.c @@ -287,7 +287,7 @@ _waitq_insert(pthread_t pthread) TAILQ_INSERT_TAIL(&_waitingq, pthread, pqe); else { tid = TAILQ_FIRST(&_waitingq); - while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) && + while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) && ((tid->wakeup_time.tv_sec < pthread->wakeup_time.tv_sec) || ((tid->wakeup_time.tv_sec == pthread->wakeup_time.tv_sec) && (tid->wakeup_time.tv_nsec <= pthread->wakeup_time.tv_nsec)))) diff --git a/lib/libkse/thread/thr_private.h b/lib/libkse/thread/thr_private.h index 8a126f4..8bc2d3f 100644 --- a/lib/libkse/thread/thr_private.h +++ b/lib/libkse/thread/thr_private.h @@ -85,7 +85,11 @@ #define PTHREAD_WAITQ_CLEARACTIVE() _waitq_clearactive() #define PTHREAD_WAITQ_SETACTIVE() _waitq_setactive() #else -#define PTHREAD_WAITQ_REMOVE(thrd) TAILQ_REMOVE(&_waitingq,thrd,pqe) +#define PTHREAD_WAITQ_REMOVE(thrd) do { \ + TAILQ_REMOVE(&_waitingq,thrd,pqe); \ + (thrd)->flags &= ~PTHREAD_FLAGS_IN_WAITQ; \ +} while (0) + #define PTHREAD_WAITQ_INSERT(thrd) do { \ if ((thrd)->wakeup_time.tv_sec == -1) \ TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \ @@ -101,6 +105,7 @@ else \ TAILQ_INSERT_BEFORE(tid,thrd,pqe); \ } \ + (thrd)->flags | PTHREAD_FLAGS_IN_WAITQ; \ } while (0) #define PTHREAD_WAITQ_CLEARACTIVE() #define PTHREAD_WAITQ_SETACTIVE() @@ -463,6 +468,12 @@ union pthread_wait_data { }; /* + * Define a continuation routine that can be used to perform a + * transfer of control: + */ +typedef void (*thread_continuation_t) (void *); + +/* * Thread structure. */ struct pthread { @@ -514,6 +525,24 @@ struct pthread { * if sig_saved is FALSE. */ jmp_buf saved_jmp_buf; + jmp_buf *sighandler_jmp_buf; + + /* + * Saved jump buffers for use when doing nested [sig|_]longjmp()s, as + * when doing signal delivery. + */ + union { + jmp_buf jmp; + sigjmp_buf sigjmp; + } nested_jmp; + int longjmp_val; + +#define JMPFLAGS_NONE 0x00 +#define JMPFLAGS_LONGJMP 0x01 +#define JMPFLAGS__LONGJMP 0x02 +#define JMPFLAGS_SIGLONGJMP 0x04 +#define JMPFLAGS_DEFERRED 0x08 + int jmpflags; /* * TRUE if the last state saved was a signal context. FALSE if the @@ -521,6 +550,11 @@ struct pthread { */ int sig_saved; + /* + * Used for tracking delivery of nested signal handlers. + */ + int signal_nest_level; + /* * Cancelability flags - the lower 2 bits are used by cancel * definitions in pthread.h @@ -530,14 +564,22 @@ struct pthread { #define PTHREAD_CANCEL_NEEDED 0x0010 int cancelflags; + thread_continuation_t continuation; + /* * Current signal mask and pending signals. */ sigset_t sigmask; sigset_t sigpend; +#ifndef _NO_UNDISPATCH + /* Non-zero if there are undispatched signals for this thread. */ + int undispatched_signals; +#endif + /* Thread state: */ enum pthread_state state; + enum pthread_state oldstate; /* Time that this thread was last made active. */ struct timeval last_active; @@ -1188,8 +1230,17 @@ pid_t _thread_sys_wait4(pid_t, int *, int, struct rusage *); #ifdef _SYS_POLL_H_ int _thread_sys_poll(struct pollfd *, unsigned, int); #endif + /* #include <sys/mman.h> */ +#ifdef _SYS_MMAN_H_ int _thread_sys_msync(void *, size_t, int); +#endif + +/* #include <setjmp.h> */ +#ifdef _SETJMP_H_ +extern void __longjmp(jmp_buf, int); +extern void __siglongjmp(sigjmp_buf, int); +#endif __END_DECLS #endif /* !_PTHREAD_PRIVATE_H */ diff --git a/lib/libkse/thread/thr_setprio.c b/lib/libkse/thread/thr_setprio.c index 5f7b44a..c630db5 100644 --- a/lib/libkse/thread/thr_setprio.c +++ b/lib/libkse/thread/thr_setprio.c @@ -31,7 +31,6 @@ * * $FreeBSD$ */ -#include <errno.h> #ifdef _THREAD_SAFE #include <pthread.h> #include "pthread_private.h" diff --git a/lib/libkse/thread/thr_sig.c b/lib/libkse/thread/thr_sig.c index 160bdab..0f18477 100644 --- a/lib/libkse/thread/thr_sig.c +++ b/lib/libkse/thread/thr_sig.c @@ -37,19 +37,24 @@ #include <signal.h> #include <fcntl.h> #include <unistd.h> +#include <setjmp.h> #include <errno.h> #ifdef _THREAD_SAFE #include <pthread.h> #include "pthread_private.h" /* Prototypes: */ -static void _thread_sig_check_state(pthread_t pthread, int sig); +static void thread_sig_check_state(pthread_t pthread, int sig); +static void thread_sig_finish_longjmp(void *arg); +static void handle_state_change(pthread_t pthread); + /* Static variables: */ static spinlock_t signal_lock = _SPINLOCK_INITIALIZER; static unsigned int pending_sigs[NSIG]; static unsigned int handled_sigs[NSIG]; static int volatile check_pending = 0; +static int volatile check_waiting = 0; /* Initialize signal handling facility: */ void @@ -73,7 +78,7 @@ _thread_sig_init(void) void _thread_sig_handler(int sig, int code, ucontext_t * scp) { - pthread_t pthread; + pthread_t pthread, pthread_next; int i; char c; @@ -126,8 +131,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) /* Indicate that there are queued signals in the pipe. */ _sigq_check_reqd = 1; - } - else { + } else { if (_atomic_lock(&signal_lock.access_lock)) { /* There is another signal handler running: */ pending_sigs[sig - 1]++; @@ -145,6 +149,18 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) signal_lock.access_lock = 0; else { sigaddset(&pthread->sigmask, sig); + + /* + * Make sure not to deliver the same signal to + * the thread twice. sigpend is potentially + * modified by the call chain + * _thread_sig_handle() --> + * thread_sig_check_state(), which can happen + * just above. + */ + if (sigismember(&pthread->sigpend, sig)) + sigdelset(&pthread->sigpend, sig); + signal_lock.access_lock = 0; _thread_sig_deliver(pthread, sig); sigdelset(&pthread->sigmask, sig); @@ -161,18 +177,57 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) pthread = _thread_sig_handle(i, scp); if (pthread != NULL) { sigaddset(&pthread->sigmask, i); + /* Save the old state: */ + pthread->oldstate = pthread->state; signal_lock.access_lock = 0; _thread_sig_deliver(pthread, i); sigdelset(&pthread->sigmask, i); if (_atomic_lock(&signal_lock.access_lock)) { check_pending = 1; + /* + * Have the lock holder take care + * of any state changes: + */ + if (pthread->state != pthread->oldstate) + check_waiting = 1; return; } + if (pthread->state != pthread->oldstate) + handle_state_change(pthread); } } } + while (check_waiting != 0) { + check_waiting = 0; + /* + * Enter a loop to wake up all threads waiting + * for a process to complete: + */ + for (pthread = TAILQ_FIRST(&_waitingq); + pthread != NULL; pthread = pthread_next) { + pthread_next = TAILQ_NEXT(pthread, pqe); + if (pthread->state == PS_RUNNING) + handle_state_change(pthread); + } + } + /* Release the lock: */ signal_lock.access_lock = 0; } + + /* + * Check to see if the current thread performed a + * [sig|_]longjmp() out of a signal handler. + */ + if ((_thread_run->jmpflags & (JMPFLAGS_LONGJMP | + JMPFLAGS__LONGJMP)) != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + __longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + __siglongjmp(_thread_run->nested_jmp.sigjmp, + _thread_run->longjmp_val); + } } } @@ -353,7 +408,7 @@ _thread_sig_handle(int sig, ucontext_t * scp) * Perform any state changes due to signal * arrival: */ - _thread_sig_check_state(pthread, sig); + thread_sig_check_state(pthread, sig); return (pthread); } } @@ -363,9 +418,99 @@ _thread_sig_handle(int sig, ucontext_t * scp) return (NULL); } +static void +thread_sig_finish_longjmp(void *arg) +{ + /* + * Check to see if the current thread performed a [_]longjmp() out of a + * signal handler. + */ + if ((_thread_run->jmpflags & (JMPFLAGS_LONGJMP | JMPFLAGS__LONGJMP)) + != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + _thread_run->continuation = NULL; + __longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } + /* + * Check to see if the current thread performed a siglongjmp + * out of a signal handler: + */ + else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + _thread_run->continuation = NULL; + __siglongjmp(_thread_run->nested_jmp.sigjmp, + _thread_run->longjmp_val); + } +} + +static void +handle_state_change(pthread_t pthread) +{ + /* + * We should only need to handle threads whose state was + * changed to running: + */ + if (pthread->state == PS_RUNNING) { + switch (pthread->oldstate) { + /* + * States which do not change when a signal is trapped: + */ + case PS_DEAD: + case PS_DEADLOCK: + case PS_RUNNING: + case PS_SIGTHREAD: + case PS_STATE_MAX: + break; + + /* + * States which need to return to critical sections + * before they can switch contexts: + */ + case PS_COND_WAIT: + case PS_FDLR_WAIT: + case PS_FDLW_WAIT: + case PS_FILE_WAIT: + case PS_JOIN: + case PS_MUTEX_WAIT: + /* Indicate that the thread was interrupted: */ + pthread->interrupted = 1; + /* + * Defer the [sig|_]longjmp until leaving the critical + * region: + */ + pthread->jmpflags |= JMPFLAGS_DEFERRED; + + /* Set the continuation routine: */ + pthread->continuation = thread_sig_finish_longjmp; + /* FALLTHROUGH */ + case PS_FDR_WAIT: + case PS_FDW_WAIT: + case PS_POLL_WAIT: + case PS_SELECT_WAIT: + case PS_SIGSUSPEND: + case PS_SIGWAIT: + case PS_SLEEP_WAIT: + case PS_SPINBLOCK: + case PS_SUSPENDED: + case PS_WAIT_WAIT: + if ((pthread->flags & PTHREAD_FLAGS_IN_WAITQ) != 0) { + PTHREAD_WAITQ_REMOVE(pthread); + if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ) + PTHREAD_WORKQ_REMOVE(pthread); + } + break; + } + + if ((pthread->flags & PTHREAD_FLAGS_IN_PRIOQ) == 0) + PTHREAD_PRIOQ_INSERT_TAIL(pthread); + } +} + + /* Perform thread specific actions in response to a signal: */ static void -_thread_sig_check_state(pthread_t pthread, int sig) +thread_sig_check_state(pthread_t pthread, int sig) { /* * Process according to thread state: @@ -402,7 +547,6 @@ _thread_sig_check_state(pthread_t pthread, int sig) sigaddset(&pthread->sigpend,sig); break; - /* * The wait state is a special case due to the handling of * SIGCHLD signals. @@ -483,12 +627,25 @@ _thread_sig_send(pthread_t pthread, int sig) } else if (pthread->state != PS_SIGWAIT && !sigismember(&pthread->sigmask, sig)) { /* Perform any state changes due to signal arrival: */ - _thread_sig_check_state(pthread, sig); + thread_sig_check_state(pthread, sig); - /* Call the installed signal handler: */ - _thread_sig_deliver(pthread, sig); - } - else { +#ifndef _NO_UNDISPATCH + if (_thread_run != pthread) { + /* + * Make a note to call the signal handler once + * the signaled thread is running. This is + * necessary in order to make sure that the + * signal is delivered on the correct stack. + */ + pthread->undispatched_signals++; + } else { +#endif + /* Call the installed signal handler. */ + _thread_sig_deliver(pthread, sig); +#ifndef _NO_UNDISPATCH + } +#endif + } else { /* Increment the pending signal count. */ sigaddset(&pthread->sigpend,sig); } @@ -553,6 +710,7 @@ _thread_sig_deliver(pthread_t pthread, int sig) { sigset_t mask; pthread_t pthread_saved; + jmp_buf jb, *saved_sighandler_jmp_buf; /* * Check that a custom handler is installed @@ -568,7 +726,7 @@ _thread_sig_deliver(pthread_t pthread, int sig) /* * Add the current signal and signal handler - * mask to the threads current signal mask: + * mask to the thread's current signal mask: */ SIGSETOR(pthread->sigmask, _thread_sigact[sig - 1].sa_mask); sigaddset(&pthread->sigmask, sig); @@ -577,16 +735,36 @@ _thread_sig_deliver(pthread_t pthread, int sig) if (_thread_run->sig_defer_count > 0) pthread->sig_defer_count++; - _thread_run = pthread; + /* Increment the number of nested signals being handled. */ + pthread->signal_nest_level++; /* - * Dispatch the signal via the custom signal - * handler: + * The jump buffer is allocated off the stack and the current + * jump buffer is saved. If the signal handler tries to + * [sig|_]longjmp(), our version of [sig|_]longjmp() will copy + * the user supplied jump buffer into + * _thread_run->nested_jmp.[sig]jmp and _longjmp() back to here. */ - (*(_thread_sigact[sig - 1].sa_handler))(sig); + saved_sighandler_jmp_buf = pthread->sighandler_jmp_buf; + pthread->sighandler_jmp_buf = &jb; + + _thread_run = pthread; + + if (_setjmp(jb) == 0) { + /* + * Dispatch the signal via the custom signal + * handler: + */ + (*(_thread_sigact[sig - 1].sa_handler))(sig); + } _thread_run = pthread_saved; + pthread->sighandler_jmp_buf = saved_sighandler_jmp_buf; + + /* Decrement the signal nest level. */ + pthread->signal_nest_level--; + /* Current thread inside critical region? */ if (_thread_run->sig_defer_count > 0) pthread->sig_defer_count--; diff --git a/lib/libpthread/thread/Makefile.inc b/lib/libpthread/thread/Makefile.inc index 4697305..f46f007 100644 --- a/lib/libpthread/thread/Makefile.inc +++ b/lib/libpthread/thread/Makefile.inc @@ -60,6 +60,7 @@ SRCS+= \ uthread_info.c \ uthread_init.c \ uthread_ioctl.c \ + uthread_jmp.c \ uthread_join.c \ uthread_kern.c \ uthread_kill.c \ diff --git a/lib/libpthread/thread/thr_cancel.c b/lib/libpthread/thread/thr_cancel.c index de7c491..f22bfb5 100644 --- a/lib/libpthread/thread/thr_cancel.c +++ b/lib/libpthread/thread/thr_cancel.c @@ -2,11 +2,12 @@ * David Leonard <d@openbsd.org>, 1999. Public domain. * $FreeBSD$ */ - #include <sys/errno.h> #include <pthread.h> #include "pthread_private.h" +static void finish_cancellation(void *arg); + int pthread_cancel(pthread_t pthread) { @@ -71,11 +72,13 @@ pthread_cancel(pthread_t pthread) * queue. Mark the thread as interrupted and * needing cancellation, and set the state to * running. When the thread resumes, it will - * exit after removing itself from the queue. + * remove itself from the queue and call the + * cancellation completion routine. */ pthread->interrupted = 1; pthread->cancelflags |= PTHREAD_CANCEL_NEEDED; PTHREAD_NEW_STATE(pthread,PS_RUNNING); + pthread->continuation = finish_cancellation; break; case PS_DEAD: @@ -172,7 +175,6 @@ pthread_testcancel(void) void _thread_enter_cancellation_point(void) { - /* Look for a cancellation before we block: */ pthread_testcancel(); _thread_run->cancelflags |= PTHREAD_AT_CANCEL_POINT; @@ -181,8 +183,20 @@ _thread_enter_cancellation_point(void) void _thread_leave_cancellation_point(void) { - _thread_run->cancelflags &= ~PTHREAD_AT_CANCEL_POINT; /* Look for a cancellation after we unblock: */ pthread_testcancel(); } + +static void +finish_cancellation(void *arg) +{ + _thread_run->continuation = NULL; + _thread_run->interrupted = 0; + + if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { + _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; + _thread_exit_cleanup(); + pthread_exit(PTHREAD_CANCELED); + } +} diff --git a/lib/libpthread/thread/thr_cond.c b/lib/libpthread/thread/thr_cond.c index 3e215af..ced48e3 100644 --- a/lib/libpthread/thread/thr_cond.c +++ b/lib/libpthread/thread/thr_cond.c @@ -157,7 +157,8 @@ pthread_cond_destroy(pthread_cond_t * cond) int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) { - int rval = 0; + int rval = 0; + int interrupted = 0; if (cond == NULL) rval = EINVAL; @@ -238,6 +239,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) if (_thread_run->interrupted != 0) { /* + * Remember that this thread + * was interrupted: + */ + interrupted = 1; + + /* * Lock the condition variable * while removing the thread. */ @@ -273,11 +280,8 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) break; } - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (interrupted != 0 && _thread_run->continuation != NULL) + _thread_run->continuation((void *) _thread_run); _thread_leave_cancellation_point(); } @@ -290,7 +294,8 @@ int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec * abstime) { - int rval = 0; + int rval = 0; + int interrupted = 0; if (cond == NULL || abstime == NULL) rval = EINVAL; @@ -386,6 +391,12 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, rval = _mutex_cv_lock(mutex); } else { + /* + * Remember if this thread was + * interrupted: + */ + interrupted = _thread_run->interrupted; + /* Lock the condition variable structure: */ _SPINLOCK(&(*cond)->lock); @@ -431,11 +442,8 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, break; } - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (interrupted != 0 && _thread_run->continuation != NULL) + _thread_run->continuation((void *) _thread_run); _thread_leave_cancellation_point(); } diff --git a/lib/libpthread/thread/thr_create.c b/lib/libpthread/thread/thr_create.c index fd9e746..b8a1c46 100644 --- a/lib/libpthread/thread/thr_create.c +++ b/lib/libpthread/thread/thr_create.c @@ -189,19 +189,24 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, */ #if defined(__FreeBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[0]._jb[2] = (long) _thread_start; - new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = 0; - new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = (long) _thread_start; + new_thread->saved_jmp_buf[0]._jb[2] = + (long)_thread_start; + new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = + 0; + new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = + (long)_thread_start; #else - new_thread->saved_jmp_buf[0]._jb[0] = (long) _thread_start; + new_thread->saved_jmp_buf[0]._jb[0] = + (long)_thread_start; #endif #elif defined(__NetBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[2] = (long) _thread_start; + new_thread->saved_jmp_buf[2] = (long)_thread_start; new_thread->saved_jmp_buf[4 + R_RA] = 0; - new_thread->saved_jmp_buf[4 + R_T12] = (long) _thread_start; + new_thread->saved_jmp_buf[4 + R_T12] = + (long)_thread_start; #else - new_thread->saved_jmp_buf[0] = (long) _thread_start; + new_thread->saved_jmp_buf[0] = (long)_thread_start; #endif #else #error "Don't recognize this operating system!" @@ -210,15 +215,22 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* The stack starts high and builds down: */ #if defined(__FreeBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[0]._jb[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double); + new_thread->saved_jmp_buf[0]._jb[4 + R_SP] = + (long)new_thread->stack + pattr->stacksize_attr + - sizeof(double); #else - new_thread->saved_jmp_buf[0]._jb[2] = (int) (new_thread->stack + pattr->stacksize_attr - sizeof(double)); + new_thread->saved_jmp_buf[0]._jb[2] = + (int)(new_thread->stack + pattr->stacksize_attr - + sizeof(double)); #endif #elif defined(__NetBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double); + new_thread->saved_jmp_buf[4 + R_SP] = + (long)new_thread->stack + pattr->stacksize_attr - + sizeof(double); #else - new_thread->saved_jmp_buf[2] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double); + new_thread->saved_jmp_buf[2] = (long)new_thread->stack + + pattr->stacksize_attr - sizeof(double); #endif #else #error "Don't recognize this operating system!" @@ -263,6 +275,8 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, new_thread->flags = 0; new_thread->poll_data.nfds = 0; new_thread->poll_data.fds = NULL; + new_thread->jmpflags = 0; + new_thread->continuation = NULL; /* * Defer signals to protect the scheduling queues diff --git a/lib/libpthread/thread/thr_init.c b/lib/libpthread/thread/thr_init.c index b6429bb..dd5f53f 100644 --- a/lib/libpthread/thread/thr_init.c +++ b/lib/libpthread/thread/thr_init.c @@ -184,6 +184,10 @@ _thread_init(void) /* Initialize the scheduling switch hook routine: */ _sched_switch_hook = NULL; + /* Give this thread default attributes: */ + memcpy((void *) &_thread_initial->attr, &pthread_attr_default, + sizeof(struct pthread_attr)); + /* Initialize the thread stack cache: */ SLIST_INIT(&_stackq); @@ -199,6 +203,14 @@ _thread_init(void) -1, 0) == MAP_FAILED) PANIC("Cannot allocate red zone for initial thread"); + /* Set the main thread stack pointer. */ + _thread_initial->stack = (void *) USRSTACK - + PTHREAD_STACK_INITIAL; + + /* Set the stack attributes: */ + _thread_initial->attr.stackaddr_attr = _thread_initial->stack; + _thread_initial->attr.stacksize_attr = PTHREAD_STACK_INITIAL; + /* * Write a magic value to the thread structure * to help identify valid ones: diff --git a/lib/libpthread/thread/thr_join.c b/lib/libpthread/thread/thr_join.c index 155dc64..1cffc96 100644 --- a/lib/libpthread/thread/thr_join.c +++ b/lib/libpthread/thread/thr_join.c @@ -97,11 +97,9 @@ pthread_join(pthread_t pthread, void **thread_return) _thread_kern_sig_undefer(); - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (_thread_run->interrupted != 0 && + _thread_run->continuation != NULL) + _thread_run->continuation(_thread_run); /* Check if the thread is not detached: */ if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) { diff --git a/lib/libpthread/thread/thr_kern.c b/lib/libpthread/thread/thr_kern.c index b3fbc3a..b833061 100644 --- a/lib/libpthread/thread/thr_kern.c +++ b/lib/libpthread/thread/thr_kern.c @@ -122,6 +122,17 @@ __asm__("fnsave %0": :"m"(*fdata)); pthread_testcancel(); } +#ifndef _NO_UNDISPATCH + /* + * Check for undispatched signals due to calls to + * pthread_kill(). + */ + if (_thread_run->undispatched_signals != 0) { + _thread_run->undispatched_signals = 0; + _dispatch_signals(); + } +#endif + if (_sched_switch_hook != NULL) { /* Run the installed switch hook: */ thread_run_switch_hook(_last_user_thread, _thread_run); @@ -365,8 +376,7 @@ __asm__("fnsave %0": :"m"(*fdata)); * something happens that changes this condition: */ _thread_kern_poll(1); - } - else { + } else { /* Remove the thread from the ready queue: */ PTHREAD_PRIOQ_REMOVE(pthread_h); @@ -537,8 +547,38 @@ __asm__("fnsave %0": :"m"(*fdata)); } } + /* + * Check if this thread is being continued from a + * longjmp() out of a signal handler: + */ + if ((_thread_run->jmpflags & JMPFLAGS_LONGJMP) != 0) { + _thread_run->jmpflags = 0; + __longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } + /* + * Check if this thread is being continued from a + * _longjmp() out of a signal handler: + */ + else if ((_thread_run->jmpflags & JMPFLAGS__LONGJMP) != + 0) { + _thread_run->jmpflags = 0; + ___longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } + /* + * Check if this thread is being continued from a + * siglongjmp() out of a signal handler: + */ + else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) + != 0) { + _thread_run->jmpflags = 0; + __siglongjmp( + _thread_run->nested_jmp.sigjmp, + _thread_run->longjmp_val); + } /* Check if a signal context was saved: */ - if (_thread_run->sig_saved == 1) { + else if (_thread_run->sig_saved == 1) { #ifndef __alpha__ /* * Point to the floating point data in the @@ -571,7 +611,7 @@ __asm__("fnsave %0": :"m"(*fdata)); * was context switched out (by a longjmp to * a different thread): */ - longjmp(_thread_run->saved_jmp_buf, 1); + __longjmp(_thread_run->saved_jmp_buf, 1); } /* This point should not be reached. */ diff --git a/lib/libpthread/thread/thr_mutex.c b/lib/libpthread/thread/thr_mutex.c index 6526a35..6d75ea5 100644 --- a/lib/libpthread/thread/thr_mutex.c +++ b/lib/libpthread/thread/thr_mutex.c @@ -622,11 +622,9 @@ pthread_mutex_lock(pthread_mutex_t * mutex) */ _thread_kern_sig_undefer(); - if ((_thread_run->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) { - _thread_run->cancelflags &= ~PTHREAD_CANCEL_NEEDED; - _thread_exit_cleanup(); - pthread_exit(PTHREAD_CANCELED); - } + if (_thread_run->interrupted != 0 && + _thread_run->continuation != NULL) + _thread_run->continuation((void *) _thread_run); } /* Return the completion status: */ diff --git a/lib/libpthread/thread/thr_priority_queue.c b/lib/libpthread/thread/thr_priority_queue.c index 6fdf844..1b9fcba 100644 --- a/lib/libpthread/thread/thr_priority_queue.c +++ b/lib/libpthread/thread/thr_priority_queue.c @@ -287,7 +287,7 @@ _waitq_insert(pthread_t pthread) TAILQ_INSERT_TAIL(&_waitingq, pthread, pqe); else { tid = TAILQ_FIRST(&_waitingq); - while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) && + while ((tid != NULL) && (tid->wakeup_time.tv_sec != -1) && ((tid->wakeup_time.tv_sec < pthread->wakeup_time.tv_sec) || ((tid->wakeup_time.tv_sec == pthread->wakeup_time.tv_sec) && (tid->wakeup_time.tv_nsec <= pthread->wakeup_time.tv_nsec)))) diff --git a/lib/libpthread/thread/thr_private.h b/lib/libpthread/thread/thr_private.h index 8a126f4..8bc2d3f 100644 --- a/lib/libpthread/thread/thr_private.h +++ b/lib/libpthread/thread/thr_private.h @@ -85,7 +85,11 @@ #define PTHREAD_WAITQ_CLEARACTIVE() _waitq_clearactive() #define PTHREAD_WAITQ_SETACTIVE() _waitq_setactive() #else -#define PTHREAD_WAITQ_REMOVE(thrd) TAILQ_REMOVE(&_waitingq,thrd,pqe) +#define PTHREAD_WAITQ_REMOVE(thrd) do { \ + TAILQ_REMOVE(&_waitingq,thrd,pqe); \ + (thrd)->flags &= ~PTHREAD_FLAGS_IN_WAITQ; \ +} while (0) + #define PTHREAD_WAITQ_INSERT(thrd) do { \ if ((thrd)->wakeup_time.tv_sec == -1) \ TAILQ_INSERT_TAIL(&_waitingq,thrd,pqe); \ @@ -101,6 +105,7 @@ else \ TAILQ_INSERT_BEFORE(tid,thrd,pqe); \ } \ + (thrd)->flags | PTHREAD_FLAGS_IN_WAITQ; \ } while (0) #define PTHREAD_WAITQ_CLEARACTIVE() #define PTHREAD_WAITQ_SETACTIVE() @@ -463,6 +468,12 @@ union pthread_wait_data { }; /* + * Define a continuation routine that can be used to perform a + * transfer of control: + */ +typedef void (*thread_continuation_t) (void *); + +/* * Thread structure. */ struct pthread { @@ -514,6 +525,24 @@ struct pthread { * if sig_saved is FALSE. */ jmp_buf saved_jmp_buf; + jmp_buf *sighandler_jmp_buf; + + /* + * Saved jump buffers for use when doing nested [sig|_]longjmp()s, as + * when doing signal delivery. + */ + union { + jmp_buf jmp; + sigjmp_buf sigjmp; + } nested_jmp; + int longjmp_val; + +#define JMPFLAGS_NONE 0x00 +#define JMPFLAGS_LONGJMP 0x01 +#define JMPFLAGS__LONGJMP 0x02 +#define JMPFLAGS_SIGLONGJMP 0x04 +#define JMPFLAGS_DEFERRED 0x08 + int jmpflags; /* * TRUE if the last state saved was a signal context. FALSE if the @@ -521,6 +550,11 @@ struct pthread { */ int sig_saved; + /* + * Used for tracking delivery of nested signal handlers. + */ + int signal_nest_level; + /* * Cancelability flags - the lower 2 bits are used by cancel * definitions in pthread.h @@ -530,14 +564,22 @@ struct pthread { #define PTHREAD_CANCEL_NEEDED 0x0010 int cancelflags; + thread_continuation_t continuation; + /* * Current signal mask and pending signals. */ sigset_t sigmask; sigset_t sigpend; +#ifndef _NO_UNDISPATCH + /* Non-zero if there are undispatched signals for this thread. */ + int undispatched_signals; +#endif + /* Thread state: */ enum pthread_state state; + enum pthread_state oldstate; /* Time that this thread was last made active. */ struct timeval last_active; @@ -1188,8 +1230,17 @@ pid_t _thread_sys_wait4(pid_t, int *, int, struct rusage *); #ifdef _SYS_POLL_H_ int _thread_sys_poll(struct pollfd *, unsigned, int); #endif + /* #include <sys/mman.h> */ +#ifdef _SYS_MMAN_H_ int _thread_sys_msync(void *, size_t, int); +#endif + +/* #include <setjmp.h> */ +#ifdef _SETJMP_H_ +extern void __longjmp(jmp_buf, int); +extern void __siglongjmp(sigjmp_buf, int); +#endif __END_DECLS #endif /* !_PTHREAD_PRIVATE_H */ diff --git a/lib/libpthread/thread/thr_setprio.c b/lib/libpthread/thread/thr_setprio.c index 5f7b44a..c630db5 100644 --- a/lib/libpthread/thread/thr_setprio.c +++ b/lib/libpthread/thread/thr_setprio.c @@ -31,7 +31,6 @@ * * $FreeBSD$ */ -#include <errno.h> #ifdef _THREAD_SAFE #include <pthread.h> #include "pthread_private.h" diff --git a/lib/libpthread/thread/thr_sig.c b/lib/libpthread/thread/thr_sig.c index 160bdab..0f18477 100644 --- a/lib/libpthread/thread/thr_sig.c +++ b/lib/libpthread/thread/thr_sig.c @@ -37,19 +37,24 @@ #include <signal.h> #include <fcntl.h> #include <unistd.h> +#include <setjmp.h> #include <errno.h> #ifdef _THREAD_SAFE #include <pthread.h> #include "pthread_private.h" /* Prototypes: */ -static void _thread_sig_check_state(pthread_t pthread, int sig); +static void thread_sig_check_state(pthread_t pthread, int sig); +static void thread_sig_finish_longjmp(void *arg); +static void handle_state_change(pthread_t pthread); + /* Static variables: */ static spinlock_t signal_lock = _SPINLOCK_INITIALIZER; static unsigned int pending_sigs[NSIG]; static unsigned int handled_sigs[NSIG]; static int volatile check_pending = 0; +static int volatile check_waiting = 0; /* Initialize signal handling facility: */ void @@ -73,7 +78,7 @@ _thread_sig_init(void) void _thread_sig_handler(int sig, int code, ucontext_t * scp) { - pthread_t pthread; + pthread_t pthread, pthread_next; int i; char c; @@ -126,8 +131,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) /* Indicate that there are queued signals in the pipe. */ _sigq_check_reqd = 1; - } - else { + } else { if (_atomic_lock(&signal_lock.access_lock)) { /* There is another signal handler running: */ pending_sigs[sig - 1]++; @@ -145,6 +149,18 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) signal_lock.access_lock = 0; else { sigaddset(&pthread->sigmask, sig); + + /* + * Make sure not to deliver the same signal to + * the thread twice. sigpend is potentially + * modified by the call chain + * _thread_sig_handle() --> + * thread_sig_check_state(), which can happen + * just above. + */ + if (sigismember(&pthread->sigpend, sig)) + sigdelset(&pthread->sigpend, sig); + signal_lock.access_lock = 0; _thread_sig_deliver(pthread, sig); sigdelset(&pthread->sigmask, sig); @@ -161,18 +177,57 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp) pthread = _thread_sig_handle(i, scp); if (pthread != NULL) { sigaddset(&pthread->sigmask, i); + /* Save the old state: */ + pthread->oldstate = pthread->state; signal_lock.access_lock = 0; _thread_sig_deliver(pthread, i); sigdelset(&pthread->sigmask, i); if (_atomic_lock(&signal_lock.access_lock)) { check_pending = 1; + /* + * Have the lock holder take care + * of any state changes: + */ + if (pthread->state != pthread->oldstate) + check_waiting = 1; return; } + if (pthread->state != pthread->oldstate) + handle_state_change(pthread); } } } + while (check_waiting != 0) { + check_waiting = 0; + /* + * Enter a loop to wake up all threads waiting + * for a process to complete: + */ + for (pthread = TAILQ_FIRST(&_waitingq); + pthread != NULL; pthread = pthread_next) { + pthread_next = TAILQ_NEXT(pthread, pqe); + if (pthread->state == PS_RUNNING) + handle_state_change(pthread); + } + } + /* Release the lock: */ signal_lock.access_lock = 0; } + + /* + * Check to see if the current thread performed a + * [sig|_]longjmp() out of a signal handler. + */ + if ((_thread_run->jmpflags & (JMPFLAGS_LONGJMP | + JMPFLAGS__LONGJMP)) != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + __longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + __siglongjmp(_thread_run->nested_jmp.sigjmp, + _thread_run->longjmp_val); + } } } @@ -353,7 +408,7 @@ _thread_sig_handle(int sig, ucontext_t * scp) * Perform any state changes due to signal * arrival: */ - _thread_sig_check_state(pthread, sig); + thread_sig_check_state(pthread, sig); return (pthread); } } @@ -363,9 +418,99 @@ _thread_sig_handle(int sig, ucontext_t * scp) return (NULL); } +static void +thread_sig_finish_longjmp(void *arg) +{ + /* + * Check to see if the current thread performed a [_]longjmp() out of a + * signal handler. + */ + if ((_thread_run->jmpflags & (JMPFLAGS_LONGJMP | JMPFLAGS__LONGJMP)) + != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + _thread_run->continuation = NULL; + __longjmp(_thread_run->nested_jmp.jmp, + _thread_run->longjmp_val); + } + /* + * Check to see if the current thread performed a siglongjmp + * out of a signal handler: + */ + else if ((_thread_run->jmpflags & JMPFLAGS_SIGLONGJMP) != 0) { + _thread_run->jmpflags = JMPFLAGS_NONE; + _thread_run->continuation = NULL; + __siglongjmp(_thread_run->nested_jmp.sigjmp, + _thread_run->longjmp_val); + } +} + +static void +handle_state_change(pthread_t pthread) +{ + /* + * We should only need to handle threads whose state was + * changed to running: + */ + if (pthread->state == PS_RUNNING) { + switch (pthread->oldstate) { + /* + * States which do not change when a signal is trapped: + */ + case PS_DEAD: + case PS_DEADLOCK: + case PS_RUNNING: + case PS_SIGTHREAD: + case PS_STATE_MAX: + break; + + /* + * States which need to return to critical sections + * before they can switch contexts: + */ + case PS_COND_WAIT: + case PS_FDLR_WAIT: + case PS_FDLW_WAIT: + case PS_FILE_WAIT: + case PS_JOIN: + case PS_MUTEX_WAIT: + /* Indicate that the thread was interrupted: */ + pthread->interrupted = 1; + /* + * Defer the [sig|_]longjmp until leaving the critical + * region: + */ + pthread->jmpflags |= JMPFLAGS_DEFERRED; + + /* Set the continuation routine: */ + pthread->continuation = thread_sig_finish_longjmp; + /* FALLTHROUGH */ + case PS_FDR_WAIT: + case PS_FDW_WAIT: + case PS_POLL_WAIT: + case PS_SELECT_WAIT: + case PS_SIGSUSPEND: + case PS_SIGWAIT: + case PS_SLEEP_WAIT: + case PS_SPINBLOCK: + case PS_SUSPENDED: + case PS_WAIT_WAIT: + if ((pthread->flags & PTHREAD_FLAGS_IN_WAITQ) != 0) { + PTHREAD_WAITQ_REMOVE(pthread); + if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ) + PTHREAD_WORKQ_REMOVE(pthread); + } + break; + } + + if ((pthread->flags & PTHREAD_FLAGS_IN_PRIOQ) == 0) + PTHREAD_PRIOQ_INSERT_TAIL(pthread); + } +} + + /* Perform thread specific actions in response to a signal: */ static void -_thread_sig_check_state(pthread_t pthread, int sig) +thread_sig_check_state(pthread_t pthread, int sig) { /* * Process according to thread state: @@ -402,7 +547,6 @@ _thread_sig_check_state(pthread_t pthread, int sig) sigaddset(&pthread->sigpend,sig); break; - /* * The wait state is a special case due to the handling of * SIGCHLD signals. @@ -483,12 +627,25 @@ _thread_sig_send(pthread_t pthread, int sig) } else if (pthread->state != PS_SIGWAIT && !sigismember(&pthread->sigmask, sig)) { /* Perform any state changes due to signal arrival: */ - _thread_sig_check_state(pthread, sig); + thread_sig_check_state(pthread, sig); - /* Call the installed signal handler: */ - _thread_sig_deliver(pthread, sig); - } - else { +#ifndef _NO_UNDISPATCH + if (_thread_run != pthread) { + /* + * Make a note to call the signal handler once + * the signaled thread is running. This is + * necessary in order to make sure that the + * signal is delivered on the correct stack. + */ + pthread->undispatched_signals++; + } else { +#endif + /* Call the installed signal handler. */ + _thread_sig_deliver(pthread, sig); +#ifndef _NO_UNDISPATCH + } +#endif + } else { /* Increment the pending signal count. */ sigaddset(&pthread->sigpend,sig); } @@ -553,6 +710,7 @@ _thread_sig_deliver(pthread_t pthread, int sig) { sigset_t mask; pthread_t pthread_saved; + jmp_buf jb, *saved_sighandler_jmp_buf; /* * Check that a custom handler is installed @@ -568,7 +726,7 @@ _thread_sig_deliver(pthread_t pthread, int sig) /* * Add the current signal and signal handler - * mask to the threads current signal mask: + * mask to the thread's current signal mask: */ SIGSETOR(pthread->sigmask, _thread_sigact[sig - 1].sa_mask); sigaddset(&pthread->sigmask, sig); @@ -577,16 +735,36 @@ _thread_sig_deliver(pthread_t pthread, int sig) if (_thread_run->sig_defer_count > 0) pthread->sig_defer_count++; - _thread_run = pthread; + /* Increment the number of nested signals being handled. */ + pthread->signal_nest_level++; /* - * Dispatch the signal via the custom signal - * handler: + * The jump buffer is allocated off the stack and the current + * jump buffer is saved. If the signal handler tries to + * [sig|_]longjmp(), our version of [sig|_]longjmp() will copy + * the user supplied jump buffer into + * _thread_run->nested_jmp.[sig]jmp and _longjmp() back to here. */ - (*(_thread_sigact[sig - 1].sa_handler))(sig); + saved_sighandler_jmp_buf = pthread->sighandler_jmp_buf; + pthread->sighandler_jmp_buf = &jb; + + _thread_run = pthread; + + if (_setjmp(jb) == 0) { + /* + * Dispatch the signal via the custom signal + * handler: + */ + (*(_thread_sigact[sig - 1].sa_handler))(sig); + } _thread_run = pthread_saved; + pthread->sighandler_jmp_buf = saved_sighandler_jmp_buf; + + /* Decrement the signal nest level. */ + pthread->signal_nest_level--; + /* Current thread inside critical region? */ if (_thread_run->sig_defer_count > 0) pthread->sig_defer_count--; |