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 \
}