about summary refs log tree commit diff
path: root/src/thread/pthread_rwlock_unlock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/thread/pthread_rwlock_unlock.c')
-rw-r--r--src/thread/pthread_rwlock_unlock.c23
1 files changed, 12 insertions, 11 deletions
diff --git a/src/thread/pthread_rwlock_unlock.c b/src/thread/pthread_rwlock_unlock.c
index 060e3fe1..5edca634 100644
--- a/src/thread/pthread_rwlock_unlock.c
+++ b/src/thread/pthread_rwlock_unlock.c
@@ -2,16 +2,17 @@
 
 int pthread_rwlock_unlock(pthread_rwlock_t *rw)
 {
-	struct pthread *self = pthread_self();
-	if (rw->_rw_owner == self->tid) {
-		rw->_rw_owner = 0;
-		a_store(&rw->_rw_wrlock, 0);
-		if (rw->_rw_waiters)
-			__wake(&rw->_rw_wrlock, -1, 0);
-		return 0;
-	}
-	a_dec(&rw->_rw_readers);
-	if (rw->_rw_waiters && !rw->_rw_readers)
-		__wake(&rw->_rw_readers, 1, 0);
+	int val, cnt, waiters, new;
+
+	do {
+		val = rw->_rw_lock;
+		cnt = val & 0x7fffffff;
+		waiters = rw->_rw_waiters;
+		new = (cnt == 0x7fffffff || cnt == 1) ? 0 : val-1;
+	} while (a_cas(&rw->_rw_lock, val, new) != val);
+
+	if (!new && (waiters || val<0))
+		__wake(&rw->_rw_lock, 1, 0);
+
 	return 0;
 }