Module Name: src Committed By: ad Date: Mon Jun 1 11:44:59 UTC 2020
Modified Files: src/lib/libpthread: pthread.c pthread_cond.c pthread_int.h pthread_mutex.c pthread_rwlock.c pthread_types.h Log Message: In the interests of reliability simplify waiter handling more and redo condvars to manage the list of waiters with atomic ops. To generate a diff of this commit: cvs rdiff -u -r1.170 -r1.171 src/lib/libpthread/pthread.c cvs rdiff -u -r1.69 -r1.70 src/lib/libpthread/pthread_cond.c cvs rdiff -u -r1.104 -r1.105 src/lib/libpthread/pthread_int.h cvs rdiff -u -r1.77 -r1.78 src/lib/libpthread/pthread_mutex.c cvs rdiff -u -r1.40 -r1.41 src/lib/libpthread/pthread_rwlock.c cvs rdiff -u -r1.23 -r1.24 src/lib/libpthread/pthread_types.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libpthread/pthread.c diff -u src/lib/libpthread/pthread.c:1.170 src/lib/libpthread/pthread.c:1.171 --- src/lib/libpthread/pthread.c:1.170 Sat May 16 22:53:37 2020 +++ src/lib/libpthread/pthread.c Mon Jun 1 11:44:59 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: pthread.c,v 1.170 2020/05/16 22:53:37 ad Exp $ */ +/* $NetBSD: pthread.c,v 1.171 2020/06/01 11:44:59 ad Exp $ */ /*- * Copyright (c) 2001, 2002, 2003, 2006, 2007, 2008, 2020 @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: pthread.c,v 1.170 2020/05/16 22:53:37 ad Exp $"); +__RCSID("$NetBSD: pthread.c,v 1.171 2020/06/01 11:44:59 ad Exp $"); #define __EXPOSE_STACK 1 @@ -294,12 +294,11 @@ pthread__initthread(pthread_t t) t->pt_self = t; t->pt_magic = PT_MAGIC; t->pt_willpark = 0; - t->pt_unpark = 0; + t->pt_waiters[0] = 0; t->pt_nwaiters = 0; t->pt_sleepobj = NULL; t->pt_signalled = 0; t->pt_havespecific = 0; - t->pt_early = NULL; t->pt_lwpctl = &pthread__dummy_lwpctl; memcpy(&t->pt_lockops, pthread__lock_ops, sizeof(t->pt_lockops)); @@ -609,51 +608,32 @@ pthread__clear_waiters(pthread_t self) { int rv; - /* Zero waiters or one waiter in error case (pthread_exit()). */ - if (self->pt_nwaiters == 0) { - if (self->pt_unpark != 0 && self->pt_willpark == 0) { - rv = (ssize_t)_lwp_unpark(self->pt_unpark, NULL); - self->pt_unpark = 0; - if (rv != 0 && errno != EALREADY && errno != EINTR && - errno != ESRCH) { - pthread__errorfunc(__FILE__, __LINE__, __func__, - "_lwp_unpark failed"); - } - } - return; - } + pthread__smt_wake(); - /* One waiter or two waiters (the second being a deferred wakeup). */ - if (self->pt_nwaiters == 1) { - if (self->pt_unpark != 0) { - /* Fall through to multiple waiters case. */ - self->pt_waiters[1] = self->pt_unpark; - self->pt_nwaiters = 2; - self->pt_unpark = 0; - } else if (self->pt_willpark) { - /* Defer to _lwp_park(). */ - self->pt_unpark = self->pt_waiters[0]; - self->pt_nwaiters = 0; - return; - } else { - /* Wake one now. */ - rv = (ssize_t)_lwp_unpark(self->pt_waiters[0], NULL); - self->pt_nwaiters = 0; - if (rv != 0 && errno != EALREADY && errno != EINTR && - errno != ESRCH) { - pthread__errorfunc(__FILE__, __LINE__, __func__, - "_lwp_unpark failed"); - } - return; + switch (self->pt_nwaiters) { + case 0: + break; + case 1: + if (self->pt_willpark) { + break; } - } - - /* Multiple waiters. */ - rv = _lwp_unpark_all(self->pt_waiters, self->pt_nwaiters, NULL); - self->pt_nwaiters = 0; - if (rv != 0 && errno != EINTR) { - pthread__errorfunc(__FILE__, __LINE__, __func__, - "_lwp_unpark_all failed"); + rv = _lwp_unpark(self->pt_waiters[0], NULL); + self->pt_waiters[0] = 0; + self->pt_nwaiters = 0; + if (rv != 0) { + pthread__errorfunc(__FILE__, __LINE__, __func__, + "_lwp_unpark failed"); + } + break; + default: + rv = _lwp_unpark_all(self->pt_waiters, self->pt_nwaiters, NULL); + self->pt_waiters[0] = 0; + self->pt_nwaiters = 0; + if (rv != 0) { + pthread__errorfunc(__FILE__, __LINE__, __func__, + "_lwp_unpark_all failed"); + } + break; } } @@ -1115,7 +1095,7 @@ pthread__assertfunc(const char *file, in function ? "\"" : ""); _sys_write(STDERR_FILENO, buf, (size_t)len); - (void)_lwp_kill(_lwp_self(), SIGABRT); + (void)raise(SIGABRT); _exit(1); } @@ -1163,16 +1143,12 @@ pthread__errorfunc(const char *file, int * http://www.sun.com/software/whitepapers/solaris9/multithread.pdf */ -#define OOPS(msg) \ - pthread__errorfunc(__FILE__, __LINE__, __func__, msg) - int pthread__park(pthread_t self, pthread_mutex_t *lock, pthread_queue_t *queue, const struct timespec *abstime, int cancelpt) { int rv, error; - void *obj; self->pt_willpark = 1; pthread_mutex_unlock(lock); @@ -1186,26 +1162,15 @@ pthread__park(pthread_t self, pthread_mu * It is fine to test the value of pt_sleepobj without * holding any locks, because: * - * o Only the blocking thread (this thread) ever sets them + * o Only the blocking thread (this thread) ever sets it * to a non-NULL value. * - * o Other threads may set them NULL, but if they do so they + * o Other threads may set it NULL, but if they do so they * must also make this thread return from _lwp_park. * * o _lwp_park, _lwp_unpark and _lwp_unpark_all are system * calls and all make use of spinlocks in the kernel. So - * these system calls act as full memory barriers, and will - * ensure that the calling CPU's store buffers are drained. - * In combination with the spinlock release before unpark, - * this means that modification of pt_sleepobj/onq by another - * thread will become globally visible before that thread - * schedules an unpark operation on this thread. - * - * Note: the test in the while() statement dodges the park op if - * we have already been awoken, unless there is another thread to - * awaken. This saves a syscall - if we were already awakened, - * the next call to _lwp_park() would need to return early in order - * to eat the previous wakeup. + * these system calls act as full memory barriers. */ rv = 0; do { @@ -1213,9 +1178,13 @@ pthread__park(pthread_t self, pthread_mu * If we deferred unparking a thread, arrange to * have _lwp_park() restart it before blocking. */ + pthread__assert(self->pt_nwaiters <= 1); + pthread__assert(self->pt_nwaiters != 0 || + self->pt_waiters[0] == 0); error = _lwp_park(CLOCK_REALTIME, TIMER_ABSTIME, - __UNCONST(abstime), self->pt_unpark, NULL, NULL); - self->pt_unpark = 0; + __UNCONST(abstime), self->pt_waiters[0], NULL, NULL); + self->pt_waiters[0] = 0; + self->pt_nwaiters = 0; if (error != 0) { switch (rv = errno) { case EINTR: @@ -1225,7 +1194,8 @@ pthread__park(pthread_t self, pthread_mu case ETIMEDOUT: break; default: - OOPS("_lwp_park failed"); + pthread__errorfunc(__FILE__, __LINE__, + __func__, "_lwp_park failed"); break; } } @@ -1233,24 +1203,6 @@ pthread__park(pthread_t self, pthread_mu if (cancelpt && self->pt_cancel) rv = EINTR; } while (self->pt_sleepobj != NULL && rv == 0); - - /* - * If we have been awoken early but are still on the queue, - * then remove ourself. Again, it's safe to do the test - * without holding any locks. - */ - if (__predict_false(self->pt_sleepobj != NULL)) { - pthread_mutex_lock(lock); - if ((obj = self->pt_sleepobj) != NULL) { - PTQ_REMOVE(queue, self, pt_sleep); - self->pt_sleepobj = NULL; - if (obj != NULL && self->pt_early != NULL) - (*self->pt_early)(obj); - } - pthread_mutex_unlock(lock); - } - self->pt_early = NULL; - return rv; } Index: src/lib/libpthread/pthread_cond.c diff -u src/lib/libpthread/pthread_cond.c:1.69 src/lib/libpthread/pthread_cond.c:1.70 --- src/lib/libpthread/pthread_cond.c:1.69 Sat May 16 22:53:37 2020 +++ src/lib/libpthread/pthread_cond.c Mon Jun 1 11:44:59 2020 @@ -1,7 +1,7 @@ -/* $NetBSD: pthread_cond.c,v 1.69 2020/05/16 22:53:37 ad Exp $ */ +/* $NetBSD: pthread_cond.c,v 1.70 2020/06/01 11:44:59 ad Exp $ */ /*- - * Copyright (c) 2001, 2006, 2007, 2008 The NetBSD Foundation, Inc. + * Copyright (c) 2001, 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -29,24 +29,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/* - * We assume that there will be no contention on pthread_cond_t::ptc_lock - * because functioning applications must call both the wait and wakeup - * functions while holding the same application provided mutex. The - * spinlock is present only to prevent libpthread causing the application - * to crash or malfunction as a result of corrupted data structures, in - * the event that the application is buggy. - * - * If there is contention on spinlock when real-time threads are in use, - * it could cause a deadlock due to priority inversion: the thread holding - * the spinlock may not get CPU time to make forward progress and release - * the spinlock to a higher priority thread that is waiting for it. - * Contention on the spinlock will only occur with buggy applications, - * so at the time of writing it's not considered a major bug in libpthread. - */ - #include <sys/cdefs.h> -__RCSID("$NetBSD: pthread_cond.c,v 1.69 2020/05/16 22:53:37 ad Exp $"); +__RCSID("$NetBSD: pthread_cond.c,v 1.70 2020/06/01 11:44:59 ad Exp $"); #include <stdlib.h> #include <errno.h> @@ -91,8 +75,7 @@ pthread_cond_init(pthread_cond_t *cond, (attr == NULL) || (attr->ptca_magic == _PT_CONDATTR_MAGIC)); cond->ptc_magic = _PT_COND_MAGIC; - pthread_lockinit(&cond->ptc_lock); - PTQ_INIT(&cond->ptc_waiters); + cond->ptc_waiters = NULL; cond->ptc_mutex = NULL; if (attr && attr->ptca_private) { cond->ptc_private = malloc(sizeof(clockid_t)); @@ -116,7 +99,7 @@ pthread_cond_destroy(pthread_cond_t *con pthread__error(EINVAL, "Invalid condition variable", cond->ptc_magic == _PT_COND_MAGIC); pthread__error(EBUSY, "Destroying condition variable in use", - cond->ptc_mutex == NULL); + cond->ptc_waiters == NULL); cond->ptc_magic = _PT_COND_DEAD; free(cond->ptc_private); @@ -128,7 +111,7 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) { - pthread_t self; + pthread_t self, next, waiters; int retval; clockid_t clkid = pthread_cond_getclock(cond); @@ -149,52 +132,58 @@ pthread_cond_timedwait(pthread_cond_t *c } /* Note this thread as waiting on the CV. */ - pthread__spinlock(self, &cond->ptc_lock); cond->ptc_mutex = mutex; - PTQ_INSERT_HEAD(&cond->ptc_waiters, self, pt_sleep); - self->pt_sleepobj = cond; - pthread__spinunlock(self, &cond->ptc_lock); + self->pt_condwait = 1; + for (waiters = cond->ptc_waiters;; waiters = next) { + self->pt_condnext = waiters; +#ifndef PTHREAD__ATOMIC_IS_MEMBAR + membar_producer(); +#endif + next = atomic_cas_ptr(&cond->ptc_waiters, waiters, self); + if (next == waiters) + break; + } + + /* Drop the interlock */ + self->pt_willpark = 1; + pthread_mutex_unlock(mutex); + self->pt_willpark = 0; do { - self->pt_willpark = 1; - pthread_mutex_unlock(mutex); - self->pt_willpark = 0; - do { - retval = _lwp_park(clkid, TIMER_ABSTIME, - __UNCONST(abstime), self->pt_unpark, NULL, NULL); - self->pt_unpark = 0; - } while (retval == -1 && errno == ESRCH); - pthread_mutex_lock(mutex); - - /* - * If we have cancelled then exit. POSIX dictates that - * the mutex must be held when we action the cancellation. - * - * If we absorbed a pthread_cond_signal() and cannot take - * the wakeup, we must ensure that another thread does. - * - * If awoke early, we may still be on the sleep queue and - * must remove ourself. - */ + pthread__assert(self->pt_nwaiters <= 1); + pthread__assert(self->pt_nwaiters != 0 || + self->pt_waiters[0] == 0); + retval = _lwp_park(clkid, TIMER_ABSTIME, __UNCONST(abstime), + self->pt_waiters[0], NULL, NULL); + self->pt_waiters[0] = 0; + self->pt_nwaiters = 0; if (__predict_false(retval != 0)) { - switch (errno) { - case EINTR: - case EALREADY: + if (errno == EINTR || errno == EALREADY || + errno == ESRCH) { retval = 0; - break; - default: + } else { retval = errno; - break; } } - if (__predict_false(self->pt_cancel | retval)) { - pthread_cond_signal(cond); - if (self->pt_cancel) { - pthread__cancelled(); - } - break; + } while (self->pt_condwait && !self->pt_cancel && !retval); + + /* + * If we have cancelled then exit. POSIX dictates that + * the mutex must be held when we action the cancellation. + * + * If we absorbed a pthread_cond_signal() and cannot take + * the wakeup, we must ensure that another thread does. + * + * If awoke early, we may still be on the waiter list and + * must remove ourself. + */ + pthread_mutex_lock(mutex); + if (__predict_false(self->pt_condwait | self->pt_cancel | retval)) { + pthread_cond_broadcast(cond); + if (self->pt_cancel) { + pthread__cancelled(); } - } while (self->pt_sleepobj != NULL); + } return retval; } @@ -208,121 +197,89 @@ pthread_cond_wait(pthread_cond_t *cond, return pthread_cond_timedwait(cond, mutex, NULL); } -static int __noinline -pthread__cond_wake_one(pthread_cond_t *cond) +int +pthread_cond_signal(pthread_cond_t *cond) { - pthread_t self, signaled; + pthread_t self, thread, next; pthread_mutex_t *mutex; - lwpid_t lid; - /* - * Pull the first thread off the queue. If the current thread - * is associated with the condition variable, remove it without - * awakening (error case in pthread_cond_timedwait()). - */ - self = pthread__self(); - pthread__spinlock(self, &cond->ptc_lock); - if (self->pt_sleepobj == cond) { - PTQ_REMOVE(&cond->ptc_waiters, self, pt_sleep); - self->pt_sleepobj = NULL; - } - signaled = PTQ_FIRST(&cond->ptc_waiters); - if (__predict_false(signaled == NULL)) { - cond->ptc_mutex = NULL; - pthread__spinunlock(self, &cond->ptc_lock); - return 0; - } + if (__predict_false(__uselibcstub)) + return __libc_cond_signal_stub(cond); + + pthread__error(EINVAL, "Invalid condition variable", + cond->ptc_magic == _PT_COND_MAGIC); + + /* Take ownership of one waiter. */ + self = pthread_self(); mutex = cond->ptc_mutex; - if (PTQ_NEXT(signaled, pt_sleep) == NULL) { - cond->ptc_mutex = NULL; - PTQ_INIT(&cond->ptc_waiters); - } else { - PTQ_REMOVE(&cond->ptc_waiters, signaled, pt_sleep); + for (thread = cond->ptc_waiters;; thread = next) { + if (thread == NULL) { + return 0; + } + membar_datadep_consumer(); /* for alpha */ + next = thread->pt_condnext; + next = atomic_cas_ptr(&cond->ptc_waiters, thread, next); + if (__predict_true(next == thread)) { + break; + } } - signaled->pt_sleepobj = NULL; - lid = signaled->pt_lid; - pthread__spinunlock(self, &cond->ptc_lock); - - /* - * For all valid uses of pthread_cond_signal(), the caller will - * hold the mutex that the target is using to synchronize with. - * To avoid the target awakening and immediately blocking on the - * mutex, transfer the thread to be awoken to the current thread's - * deferred wakeup list. The waiter will be set running when the - * caller (this thread) releases the mutex. - */ - if (__predict_false(self->pt_nwaiters == (size_t)pthread__unpark_max)) { + if (self->pt_nwaiters >= pthread__unpark_max) { pthread__clear_waiters(self); } - self->pt_waiters[self->pt_nwaiters++] = lid; + self->pt_waiters[self->pt_nwaiters++] = thread->pt_lid; + membar_sync(); + thread->pt_condwait = 0; + /* No longer safe to touch 'thread' */ pthread__mutex_deferwake(self, mutex); return 0; } int -pthread_cond_signal(pthread_cond_t *cond) +pthread_cond_broadcast(pthread_cond_t *cond) { + pthread_t self, thread, next; + pthread_mutex_t *mutex; if (__predict_false(__uselibcstub)) - return __libc_cond_signal_stub(cond); + return __libc_cond_broadcast_stub(cond); pthread__error(EINVAL, "Invalid condition variable", cond->ptc_magic == _PT_COND_MAGIC); - if (__predict_true(PTQ_EMPTY(&cond->ptc_waiters))) + if (cond->ptc_waiters == NULL) return 0; - return pthread__cond_wake_one(cond); -} -static int __noinline -pthread__cond_wake_all(pthread_cond_t *cond) -{ - pthread_t self, signaled; - pthread_mutex_t *mutex; - u_int max; + /* Take ownership of the current set of waiters. */ + self = pthread_self(); + mutex = cond->ptc_mutex; + thread = atomic_swap_ptr(&cond->ptc_waiters, NULL); + membar_datadep_consumer(); /* for alpha */ /* - * Try to defer waking threads (see pthread_cond_signal()). - * Only transfer waiters for which there is no pending wakeup. + * Pull waiters from the queue and add to our list. Use a memory + * barrier to ensure that we safely read the value of pt_condnext + * before 'thread' sees pt_condwait being cleared. */ - self = pthread__self(); - pthread__spinlock(self, &cond->ptc_lock); - max = pthread__unpark_max; - mutex = cond->ptc_mutex; - PTQ_FOREACH(signaled, &cond->ptc_waiters, pt_sleep) { - if (__predict_false(self->pt_nwaiters == max)) { + while (thread != NULL) { + if (self->pt_nwaiters >= pthread__unpark_max) { pthread__clear_waiters(self); } - signaled->pt_sleepobj = NULL; - self->pt_waiters[self->pt_nwaiters++] = signaled->pt_lid; + next = thread->pt_condnext; + self->pt_waiters[self->pt_nwaiters++] = thread->pt_lid; + membar_sync(); + thread->pt_condwait = 0; + /* No longer safe to touch 'thread' */ + thread = next; } - PTQ_INIT(&cond->ptc_waiters); - cond->ptc_mutex = NULL; - pthread__spinunlock(self, &cond->ptc_lock); pthread__mutex_deferwake(self, mutex); - return 0; } int -pthread_cond_broadcast(pthread_cond_t *cond) -{ - if (__predict_false(__uselibcstub)) - return __libc_cond_broadcast_stub(cond); - - pthread__error(EINVAL, "Invalid condition variable", - cond->ptc_magic == _PT_COND_MAGIC); - - if (__predict_true(PTQ_EMPTY(&cond->ptc_waiters))) - return 0; - return pthread__cond_wake_all(cond); -} - -int _pthread_cond_has_waiters_np(pthread_cond_t *cond) { - return !PTQ_EMPTY(&cond->ptc_waiters); + return cond->ptc_waiters != NULL; } int Index: src/lib/libpthread/pthread_int.h diff -u src/lib/libpthread/pthread_int.h:1.104 src/lib/libpthread/pthread_int.h:1.105 --- src/lib/libpthread/pthread_int.h:1.104 Sat May 16 22:53:37 2020 +++ src/lib/libpthread/pthread_int.h Mon Jun 1 11:44:59 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_int.h,v 1.104 2020/05/16 22:53:37 ad Exp $ */ +/* $NetBSD: pthread_int.h,v 1.105 2020/06/01 11:44:59 ad Exp $ */ /*- * Copyright (c) 2001, 2002, 2003, 2006, 2007, 2008, 2020 @@ -105,7 +105,6 @@ struct __pthread_st { void *pt_exitval; /* Read by pthread_join() */ char *pt_name; /* Thread's name, set by the app. */ int pt_willpark; /* About to park */ - lwpid_t pt_unpark; /* Unpark this when parking */ struct pthread_lock_ops pt_lockops;/* Cached to avoid PIC overhead */ void *(*pt_func)(void *);/* Function to call at start. */ void *pt_arg; /* Argument to pass at start. */ @@ -142,10 +141,11 @@ struct __pthread_st { volatile int pt_rwlocked; /* Handed rwlock successfully */ volatile int pt_signalled; /* Received pthread_cond_signal() */ volatile int pt_mutexwait; /* Waiting to acquire mutex */ + volatile int pt_condwait; /* Waiting to acquire mutex */ void * volatile pt_mutexnext; /* Next thread in chain */ + void * volatile pt_condnext; /* Next thread in chain */ void * volatile pt_sleepobj; /* Object slept on */ PTQ_ENTRY(__pthread_st) pt_sleep; - void (*pt_early)(void *); /* Thread-specific data. Large so it sits close to the end. */ int pt_havespecific __aligned(COHERENCY_UNIT); Index: src/lib/libpthread/pthread_mutex.c diff -u src/lib/libpthread/pthread_mutex.c:1.77 src/lib/libpthread/pthread_mutex.c:1.78 --- src/lib/libpthread/pthread_mutex.c:1.77 Sat May 16 22:53:37 2020 +++ src/lib/libpthread/pthread_mutex.c Mon Jun 1 11:44:59 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: pthread_mutex.c,v 1.77 2020/05/16 22:53:37 ad Exp $ */ +/* $NetBSD: pthread_mutex.c,v 1.78 2020/06/01 11:44:59 ad Exp $ */ /*- * Copyright (c) 2001, 2003, 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc. @@ -47,7 +47,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: pthread_mutex.c,v 1.77 2020/05/16 22:53:37 ad Exp $"); +__RCSID("$NetBSD: pthread_mutex.c,v 1.78 2020/06/01 11:44:59 ad Exp $"); #include <sys/types.h> #include <sys/lwpctl.h> @@ -358,26 +358,24 @@ pthread__mutex_lock_slow(pthread_mutex_t * it is unsafe to re-enter the thread onto the waiters * list. */ -#ifndef PTHREAD__ATOMIC_IS_MEMBAR - membar_sync(); -#endif - while (self->pt_mutexwait) { + do { + pthread__assert(self->pt_nwaiters <= 1); + pthread__assert(self->pt_nwaiters != 0 || + self->pt_waiters[0] == 0); error = _lwp_park(CLOCK_REALTIME, TIMER_ABSTIME, - __UNCONST(ts), self->pt_unpark, NULL, NULL); - self->pt_unpark = 0; - if (__predict_true(error != -1)) { - continue; - } - if (errno == ETIMEDOUT && self->pt_mutexwait) { - /*Remove self from waiters list*/ + __UNCONST(ts), self->pt_waiters[0], NULL, NULL); + self->pt_waiters[0] = 0; + self->pt_nwaiters = 0; + if (error < 0 && errno == ETIMEDOUT) { + /* Remove self from waiters list */ pthread__mutex_wakeup(self, ptm); - /*priority protect*/ + /* Priority protect */ if (MUTEX_PROTECT(owner)) (void)_sched_protect(-1); errno = serrno; return ETIMEDOUT; } - } + } while (self->pt_mutexwait); owner = ptm->ptm_owner; } } @@ -485,7 +483,6 @@ pthread_mutex_unlock(pthread_mutex_t *pt } } } - pthread__smt_wake(); /* * Finally, wake any waiters and return. @@ -515,7 +512,6 @@ pthread__mutex_wakeup(pthread_t self, pt /* Take ownership of the current set of waiters. */ thread = atomic_swap_ptr(&ptm->ptm_waiters, NULL); membar_datadep_consumer(); /* for alpha */ - pthread__smt_wake(); /* * Pull waiters from the queue and add to our list. Use a memory @@ -523,23 +519,17 @@ pthread__mutex_wakeup(pthread_t self, pt * before 'thread' sees pt_mutexwait being cleared. */ while (thread != NULL) { - if (self->pt_nwaiters < pthread__unpark_max) { - next = thread->pt_mutexnext; - if (thread != self) { - self->pt_waiters[self->pt_nwaiters++] = - thread->pt_lid; - membar_sync(); - } - thread->pt_mutexwait = 0; - /* No longer safe to touch 'thread' */ - thread = next; - continue; + if (self->pt_nwaiters >= pthread__unpark_max) { + pthread__clear_waiters(self); } - pthread__clear_waiters(self); - } - if (self->pt_nwaiters > 0) { - pthread__clear_waiters(self); + next = thread->pt_mutexnext; + self->pt_waiters[self->pt_nwaiters++] = thread->pt_lid; + membar_sync(); + thread->pt_mutexwait = 0; + /* No longer safe to touch 'thread' */ + thread = next; } + pthread__clear_waiters(self); } int Index: src/lib/libpthread/pthread_rwlock.c diff -u src/lib/libpthread/pthread_rwlock.c:1.40 src/lib/libpthread/pthread_rwlock.c:1.41 --- src/lib/libpthread/pthread_rwlock.c:1.40 Sat May 16 22:53:37 2020 +++ src/lib/libpthread/pthread_rwlock.c Mon Jun 1 11:44:59 2020 @@ -1,7 +1,7 @@ -/* $NetBSD: pthread_rwlock.c,v 1.40 2020/05/16 22:53:37 ad Exp $ */ +/* $NetBSD: pthread_rwlock.c,v 1.41 2020/06/01 11:44:59 ad Exp $ */ /*- - * Copyright (c) 2002, 2006, 2007, 2008 The NetBSD Foundation, Inc. + * Copyright (c) 2002, 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: pthread_rwlock.c,v 1.40 2020/05/16 22:53:37 ad Exp $"); +__RCSID("$NetBSD: pthread_rwlock.c,v 1.41 2020/06/01 11:44:59 ad Exp $"); #include <sys/types.h> #include <sys/lwpctl.h> @@ -56,7 +56,8 @@ __RCSID("$NetBSD: pthread_rwlock.c,v 1.4 static int pthread__rwlock_wrlock(pthread_rwlock_t *, const struct timespec *); static int pthread__rwlock_rdlock(pthread_rwlock_t *, const struct timespec *); -static void pthread__rwlock_early(void *); +static void pthread__rwlock_early(pthread_t, pthread_rwlock_t *, + pthread_mutex_t *); int _pthread_rwlock_held_np(pthread_rwlock_t *); int _pthread_rwlock_rdheld_np(pthread_rwlock_t *); @@ -218,10 +219,13 @@ pthread__rwlock_rdlock(pthread_rwlock_t ptr->ptr_nreaders++; self->pt_rwlocked = _RW_WANT_READ; self->pt_sleepobj = &ptr->ptr_rblocked; - self->pt_early = pthread__rwlock_early; error = pthread__park(self, interlock, &ptr->ptr_rblocked, ts, 0); + if (self->pt_sleepobj != NULL) { + pthread__rwlock_early(self, ptr, interlock); + } + /* Did we get the lock? */ if (self->pt_rwlocked == _RW_LOCKED) { #ifndef PTHREAD__ATOMIC_IS_MEMBAR @@ -339,10 +343,13 @@ pthread__rwlock_wrlock(pthread_rwlock_t PTQ_INSERT_TAIL(&ptr->ptr_wblocked, self, pt_sleep); self->pt_rwlocked = _RW_WANT_WRITE; self->pt_sleepobj = &ptr->ptr_wblocked; - self->pt_early = pthread__rwlock_early; error = pthread__park(self, interlock, &ptr->ptr_wblocked, ts, 0); + if (self->pt_sleepobj != NULL) { + pthread__rwlock_early(self, ptr, interlock); + } + /* Did we get the lock? */ if (self->pt_rwlocked == _RW_LOCKED) { #ifndef PTHREAD__ATOMIC_IS_MEMBAR @@ -358,7 +365,6 @@ pthread__rwlock_wrlock(pthread_rwlock_t } } - int pthread_rwlock_trywrlock(pthread_rwlock_t *ptr) { @@ -563,32 +569,19 @@ pthread_rwlock_unlock(pthread_rwlock_t * * removed from the waiters lists. */ static void -pthread__rwlock_early(void *obj) +pthread__rwlock_early(pthread_t self, pthread_rwlock_t *ptr, + pthread_mutex_t *interlock) { - uintptr_t owner, set, new, next; - pthread_rwlock_t *ptr; - pthread_t self; - u_int off; + uintptr_t owner, set, newval, next; + pthread_queue_t *queue; - self = pthread__self(); - - switch (self->pt_rwlocked) { - case _RW_WANT_READ: - off = offsetof(pthread_rwlock_t, ptr_rblocked); - break; - case _RW_WANT_WRITE: - off = offsetof(pthread_rwlock_t, ptr_wblocked); - break; - default: - pthread__errorfunc(__FILE__, __LINE__, __func__, - "bad value of pt_rwlocked"); - off = 0; - /* NOTREACHED */ - break; + pthread_mutex_lock(interlock); + if ((queue = self->pt_sleepobj) == NULL) { + pthread_mutex_unlock(interlock); + return; } - - /* LINTED mind your own business */ - ptr = (pthread_rwlock_t *)((uint8_t *)obj - off); + PTQ_REMOVE(queue, self, pt_sleep); + self->pt_sleepobj = NULL; owner = (uintptr_t)ptr->ptr_owner; if ((owner & RW_THREAD) == 0) { @@ -604,11 +597,12 @@ pthread__rwlock_early(void *obj) set = 0; for (;; owner = next) { - new = (owner & ~(RW_HAS_WAITERS | RW_WRITE_WANTED)) | set; - next = rw_cas(ptr, owner, new); + newval = (owner & ~(RW_HAS_WAITERS | RW_WRITE_WANTED)) | set; + next = rw_cas(ptr, owner, newval); if (owner == next) break; } + pthread_mutex_unlock(interlock); } int Index: src/lib/libpthread/pthread_types.h diff -u src/lib/libpthread/pthread_types.h:1.23 src/lib/libpthread/pthread_types.h:1.24 --- src/lib/libpthread/pthread_types.h:1.23 Sat Sep 9 23:21:45 2017 +++ src/lib/libpthread/pthread_types.h Mon Jun 1 11:44:59 2020 @@ -1,7 +1,7 @@ -/* $NetBSD: pthread_types.h,v 1.23 2017/09/09 23:21:45 kamil Exp $ */ +/* $NetBSD: pthread_types.h,v 1.24 2020/06/01 11:44:59 ad Exp $ */ /*- - * Copyright (c) 2001, 2008 The NetBSD Foundation, Inc. + * Copyright (c) 2001, 2008, 2020 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -172,7 +172,8 @@ struct __pthread_cond_st { /* Protects the queue of waiters */ __pthread_spin_t ptc_lock; - pthread_queue_t ptc_waiters; + pthread_t volatile ptc_waiters; + void *ptc_spare; pthread_mutex_t *ptc_mutex; /* Current mutex */ void *ptc_private; @@ -183,7 +184,8 @@ struct __pthread_cond_st { #define _PTHREAD_COND_INITIALIZER { _PT_COND_MAGIC, \ __SIMPLELOCK_UNLOCKED, \ - {NULL, NULL}, \ + NULL, \ + NULL, \ NULL, \ NULL \ }