diff options
author | jasone <jasone@FreeBSD.org> | 2000-01-19 07:04:50 +0000 |
---|---|---|
committer | jasone <jasone@FreeBSD.org> | 2000-01-19 07:04:50 +0000 |
commit | 0b9957ff21dc2a9c577ff23b99d36d1787633701 (patch) | |
tree | fd1e0fc8602718af3b54f1661587a1462b98ccdd /lib/libpthread/thread/thr_cond.c | |
parent | 2c6582da15d1ca764e0434cfacf0ab1cc7fe11f0 (diff) | |
download | FreeBSD-src-0b9957ff21dc2a9c577ff23b99d36d1787633701.zip FreeBSD-src-0b9957ff21dc2a9c577ff23b99d36d1787633701.tar.gz |
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
Diffstat (limited to 'lib/libpthread/thread/thr_cond.c')
-rw-r--r-- | lib/libpthread/thread/thr_cond.c | 32 |
1 files changed, 20 insertions, 12 deletions
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(); } |