浏览代码

redesign robust mutex states to eliminate data races on type field

in order to implement ENOTRECOVERABLE, the implementation has
traditionally used a bit of the mutex type field to indicate that it's
recovered after EOWNERDEAD and will go into ENOTRECOVERABLE state if
pthread_mutex_consistent is not called before unlocking. while it's
only the thread that holds the lock that needs access to this
information (except possibly for the sake of pthread_mutex_consistent
choosing between EINVAL and EPERM for erroneous calls), the change to
the type field is formally a data race with all other threads that
perform any operation on the mutex. no individual bits race, and no
write races are possible, so things are "okay" in some sense, but it's
still not good.

this patch moves the recovery/consistency state to the mutex
owner/lock field which is rightfully mutable. bit 30, the same bit the
kernel uses with a zero owner to indicate that the previous owner died
holding the lock, is now used with a nonzero owner to indicate that
the mutex is held but has not yet been marked consistent. note that
the kernel ABI also reserves bit 29 not to appear in any tid, so the
sentinel value we use for ENOTRECOVERABLE, 0x7fffffff, does not clash
with any tid plus bit 30.
Rich Felker 6 年之前
父节点
当前提交
099b89d384

+ 7 - 3
src/thread/pthread_mutex_consistent.c

@@ -1,10 +1,14 @@
 #include "pthread_impl.h"
 #include "pthread_impl.h"
+#include "atomic.h"
 
 
 int pthread_mutex_consistent(pthread_mutex_t *m)
 int pthread_mutex_consistent(pthread_mutex_t *m)
 {
 {
-	if (!(m->_m_type & 8)) return EINVAL;
-	if ((m->_m_lock & 0x7fffffff) != __pthread_self()->tid)
+	int old = m->_m_lock;
+	int own = old & 0x3fffffff;
+	if (!(m->_m_type & 4) || !own || !(old & 0x40000000))
+		return EINVAL;
+	if (own != __pthread_self()->tid)
 		return EPERM;
 		return EPERM;
-	m->_m_type &= ~8U;
+	a_and(&m->_m_lock, ~0x40000000);
 	return 0;
 	return 0;
 }
 }

+ 4 - 2
src/thread/pthread_mutex_timedlock.c

@@ -16,10 +16,12 @@ int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec
 	while (spins-- && m->_m_lock && !m->_m_waiters) a_spin();
 	while (spins-- && m->_m_lock && !m->_m_waiters) a_spin();
 
 
 	while ((r=__pthread_mutex_trylock(m)) == EBUSY) {
 	while ((r=__pthread_mutex_trylock(m)) == EBUSY) {
-		if (!(r=m->_m_lock) || ((r&0x40000000) && (type&4)))
+		r = m->_m_lock;
+		int own = r & 0x3fffffff;
+		if (!own && (!r || (type&4)))
 			continue;
 			continue;
 		if ((type&3) == PTHREAD_MUTEX_ERRORCHECK
 		if ((type&3) == PTHREAD_MUTEX_ERRORCHECK
-		 && (r&0x7fffffff) == __pthread_self()->tid)
+		    && own == __pthread_self()->tid)
 			return EDEADLK;
 			return EDEADLK;
 
 
 		a_inc(&m->_m_waiters);
 		a_inc(&m->_m_waiters);

+ 5 - 5
src/thread/pthread_mutex_trylock.c

@@ -8,14 +8,14 @@ int __pthread_mutex_trylock_owner(pthread_mutex_t *m)
 	int tid = self->tid;
 	int tid = self->tid;
 
 
 	old = m->_m_lock;
 	old = m->_m_lock;
-	own = old & 0x7fffffff;
+	own = old & 0x3fffffff;
 	if (own == tid && (type&3) == PTHREAD_MUTEX_RECURSIVE) {
 	if (own == tid && (type&3) == PTHREAD_MUTEX_RECURSIVE) {
 		if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN;
 		if ((unsigned)m->_m_count >= INT_MAX) return EAGAIN;
 		m->_m_count++;
 		m->_m_count++;
 		return 0;
 		return 0;
 	}
 	}
-	if (own == 0x7fffffff) return ENOTRECOVERABLE;
-	if (own && (!(own & 0x40000000) || !(type & 4))) return EBUSY;
+	if (own == 0x3fffffff) return ENOTRECOVERABLE;
+	if (own || (old && !(type & 4))) return EBUSY;
 
 
 	if (m->_m_type & 128) {
 	if (m->_m_type & 128) {
 		if (!self->robust_list.off) {
 		if (!self->robust_list.off) {
@@ -25,6 +25,7 @@ int __pthread_mutex_trylock_owner(pthread_mutex_t *m)
 		if (m->_m_waiters) tid |= 0x80000000;
 		if (m->_m_waiters) tid |= 0x80000000;
 		self->robust_list.pending = &m->_m_next;
 		self->robust_list.pending = &m->_m_next;
 	}
 	}
+	tid |= old & 0x40000000;
 
 
 	if (a_cas(&m->_m_lock, old, tid) != old) {
 	if (a_cas(&m->_m_lock, old, tid) != old) {
 		self->robust_list.pending = 0;
 		self->robust_list.pending = 0;
@@ -39,9 +40,8 @@ int __pthread_mutex_trylock_owner(pthread_mutex_t *m)
 	self->robust_list.head = &m->_m_next;
 	self->robust_list.head = &m->_m_next;
 	self->robust_list.pending = 0;
 	self->robust_list.pending = 0;
 
 
-	if (own) {
+	if (old) {
 		m->_m_count = 0;
 		m->_m_count = 0;
-		m->_m_type |= 8;
 		return EOWNERDEAD;
 		return EOWNERDEAD;
 	}
 	}
 
 

+ 7 - 2
src/thread/pthread_mutex_unlock.c

@@ -7,13 +7,18 @@ int __pthread_mutex_unlock(pthread_mutex_t *m)
 	int cont;
 	int cont;
 	int type = m->_m_type & 15;
 	int type = m->_m_type & 15;
 	int priv = (m->_m_type & 128) ^ 128;
 	int priv = (m->_m_type & 128) ^ 128;
+	int new = 0;
 
 
 	if (type != PTHREAD_MUTEX_NORMAL) {
 	if (type != PTHREAD_MUTEX_NORMAL) {
 		self = __pthread_self();
 		self = __pthread_self();
-		if ((m->_m_lock&0x7fffffff) != self->tid)
+		int old = m->_m_lock;
+		int own = old & 0x3fffffff;
+		if (own != self->tid)
 			return EPERM;
 			return EPERM;
 		if ((type&3) == PTHREAD_MUTEX_RECURSIVE && m->_m_count)
 		if ((type&3) == PTHREAD_MUTEX_RECURSIVE && m->_m_count)
 			return m->_m_count--, 0;
 			return m->_m_count--, 0;
+		if ((type&4) && (old&0x40000000))
+			new = 0x7fffffff;
 		if (!priv) {
 		if (!priv) {
 			self->robust_list.pending = &m->_m_next;
 			self->robust_list.pending = &m->_m_next;
 			__vm_lock();
 			__vm_lock();
@@ -24,7 +29,7 @@ int __pthread_mutex_unlock(pthread_mutex_t *m)
 		if (next != &self->robust_list.head) *(volatile void *volatile *)
 		if (next != &self->robust_list.head) *(volatile void *volatile *)
 			((char *)next - sizeof(void *)) = prev;
 			((char *)next - sizeof(void *)) = prev;
 	}
 	}
-	cont = a_swap(&m->_m_lock, (type & 8) ? 0x7fffffff : 0);
+	cont = a_swap(&m->_m_lock, new);
 	if (type != PTHREAD_MUTEX_NORMAL && !priv) {
 	if (type != PTHREAD_MUTEX_NORMAL && !priv) {
 		self->robust_list.pending = 0;
 		self->robust_list.pending = 0;
 		__vm_unlock();
 		__vm_unlock();