diff options
-rw-r--r-- | lib/libthr/thread/thr_init.c | 26 | ||||
-rw-r--r-- | lib/libthr/thread/thr_kern.c | 13 | ||||
-rw-r--r-- | lib/libthr/thread/thr_private.h | 3 |
3 files changed, 31 insertions, 11 deletions
diff --git a/lib/libthr/thread/thr_init.c b/lib/libthr/thread/thr_init.c index c404651..671bd6f 100644 --- a/lib/libthr/thread/thr_init.c +++ b/lib/libthr/thread/thr_init.c @@ -66,6 +66,14 @@ #include "thr_private.h" +/* + * Early implementations of sigtimedwait interpreted the signal + * set incorrectly. + */ +#define SIGTIMEDWAIT_SET_IS_INVERTED(osreldate) \ + ((500100 <= (osreldate) && (osreldate) <= 500113) || \ + (osreldate) == 501000 || (osreldate) == 501100) + extern void _thread_init_hack(void); /* @@ -168,6 +176,7 @@ _thread_init(void) size_t len; int mib[2]; sigset_t set; + int osreldate; int error; struct clockinfo clockinfo; @@ -324,6 +333,23 @@ _thread_init(void) SIGADDSET(set, SIGTHR); __sys_sigprocmask(SIG_BLOCK, &set, 0); + /* + * Precompute the signal set used by _thread_suspend to wait + * for SIGTHR. + */ + mib[0] = CTL_KERN; + mib[1] = KERN_OSRELDATE; + len = sizeof(osreldate); + if (sysctl(mib, 2, &osreldate, &len, NULL, 0) == 0 && + SIGTIMEDWAIT_SET_IS_INVERTED(osreldate)) { + /* Kernel bug requires an inverted signal set. */ + SIGFILLSET(_thread_suspend_sigset); + SIGDELSET(_thread_suspend_sigset, SIGTHR); + } else { + SIGEMPTYSET(_thread_suspend_sigset); + SIGADDSET(_thread_suspend_sigset, SIGTHR); + } + /* Get the kernel clockrate: */ mib[0] = CTL_KERN; mib[1] = KERN_CLOCKRATE; diff --git a/lib/libthr/thread/thr_kern.c b/lib/libthr/thread/thr_kern.c index 1c9a8ce..f45cae5 100644 --- a/lib/libthr/thread/thr_kern.c +++ b/lib/libthr/thread/thr_kern.c @@ -179,16 +179,9 @@ _thread_suspend(pthread_t pthread, struct timespec *abstime) struct timespec remaining; struct timespec *ts; siginfo_t info; - sigset_t set; int error; /* - * Catch SIGTHR. - */ - SIGFILLSET(set); - SIGDELSET(set, SIGTHR); - - /* * Compute the remainder of the run time. */ if (abstime) { @@ -204,9 +197,7 @@ _thread_suspend(pthread_t pthread, struct timespec *abstime) } else ts = NULL; - error = sigtimedwait(&set, &info, ts); - if (error == -1) - error = errno; - + error = sigtimedwait(&_thread_suspend_sigset, &info, ts); + error = (error == -1) ? errno : 0; return (error); } diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h index 9989342..b273890 100644 --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -692,6 +692,9 @@ SCLASS pthread_cond_t _gc_cond */ SCLASS struct sigaction _thread_sigact[NSIG]; +/* Precomputed signal set for _thread_suspend. */ +SCLASS sigset_t _thread_suspend_sigset; + /* Tracks the number of threads blocked while waiting for a spinlock. */ SCLASS volatile int _spinblock_count #ifdef GLOBAL_PTHREAD_PRIVATE |