Implement continuations to correctly handle [sig|_]longjmp() inside of a
signal handler. Explicitly check for jumps to anywhere other than the current stack, since such jumps are undefined according to POSIX. While we're at it, convert thread cancellation to use continuations, since it's cleaner than the original cancellation code. Avoid delivering a signal to a thread twice. This was a pre-existing bug, but was likely unexposed until these other changes were made. Defer signals generated by pthread_kill() so that they can be delivered on the appropriate stack. deischen claims that this is unnecessary, which is likely true, but without this change, pthread_kill() can cause undefined priority queue states and/or PANICs in [sig|_]longjmp(), so I'm leaving this in for now. To compile this code out and exercise the bug, define the _NO_UNDISPATCH cpp macro. Defining _PTHREADS_INVARIANTS as well will cause earlier crashes. PR: kern/14685 Collaboration with: deischen
This commit is contained in:
parent
1c12990b99
commit
0a3fa43c7e
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=56277
@ -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 \
|
||||
|
@ -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()
|
||||
@ -462,6 +467,12 @@ union pthread_wait_data {
|
||||
spinlock_t *spinlock;
|
||||
};
|
||||
|
||||
/*
|
||||
* Define a continuation routine that can be used to perform a
|
||||
* transfer of control:
|
||||
*/
|
||||
typedef void (*thread_continuation_t) (void *);
|
||||
|
||||
/*
|
||||
* Thread structure.
|
||||
*/
|
||||
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -237,6 +238,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
&(*cond)->lock, __FILE__, __LINE__);
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
199
lib/libc_r/uthread/uthread_jmp.c
Normal file
199
lib/libc_r/uthread/uthread_jmp.c
Normal file
@ -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
|
@ -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) {
|
||||
|
@ -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. */
|
||||
|
@ -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: */
|
||||
|
@ -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))))
|
||||
|
@ -31,7 +31,6 @@
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <errno.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
@ -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--;
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -237,6 +238,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
&(*cond)->lock, __FILE__, __LINE__);
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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. */
|
||||
|
@ -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: */
|
||||
|
@ -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))))
|
||||
|
@ -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()
|
||||
@ -462,6 +467,12 @@ union pthread_wait_data {
|
||||
spinlock_t *spinlock;
|
||||
};
|
||||
|
||||
/*
|
||||
* Define a continuation routine that can be used to perform a
|
||||
* transfer of control:
|
||||
*/
|
||||
typedef void (*thread_continuation_t) (void *);
|
||||
|
||||
/*
|
||||
* Thread structure.
|
||||
*/
|
||||
@ -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 */
|
||||
|
@ -31,7 +31,6 @@
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <errno.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
@ -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--;
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -237,6 +238,12 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
&(*cond)->lock, __FILE__, __LINE__);
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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) {
|
||||
|
@ -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. */
|
||||
|
@ -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: */
|
||||
|
@ -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))))
|
||||
|
@ -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()
|
||||
@ -462,6 +467,12 @@ union pthread_wait_data {
|
||||
spinlock_t *spinlock;
|
||||
};
|
||||
|
||||
/*
|
||||
* Define a continuation routine that can be used to perform a
|
||||
* transfer of control:
|
||||
*/
|
||||
typedef void (*thread_continuation_t) (void *);
|
||||
|
||||
/*
|
||||
* Thread structure.
|
||||
*/
|
||||
@ -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 */
|
||||
|
@ -31,7 +31,6 @@
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <errno.h>
|
||||
#ifdef _THREAD_SAFE
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
@ -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--;
|
||||
|
Loading…
Reference in New Issue
Block a user