path: root/src/thread/pthread_rwlock_tryrdlock.c
diff options
authorRich Felker <>2011-08-03 10:21:32 -0400
committerRich Felker <>2011-08-03 10:21:32 -0400
commit50304f2eefb4c79ceaf4605203f3825a35d831c0 (patch)
tree85e109c5525ecd1f37c8df37c0ade861ec845764 /src/thread/pthread_rwlock_tryrdlock.c
parent8aeee8db21858becb45a8e9f6b5bc23109638bcb (diff)
overhaul rwlocks to address several issues
like mutexes and semaphores, rwlocks suffered from a race condition where the unlock operation could access the lock memory after another thread successfully obtained the lock (and possibly destroyed or unmapped the object). this has been fixed in the same way it was fixed for other lock types. in addition, the previous implementation favored writers over readers. in the absence of other considerations, that is the best behavior for rwlocks, and posix explicitly allows it. however posix also requires read locks to be recursive. if writers are favored, any attempt to obtain a read lock while a writer is waiting for the lock will fail, causing "recursive" read locks to deadlock. this can be avoided by keeping track of which threads already hold read locks, but doing so requires unbounded memory usage, and there must be a fallback case that favors readers in case memory allocation failed. and all of this must be synchronized. the cost, complexity, and risk of errors in getting it right is too great, so we simply favor readers. tracking of the owner of write locks has been removed, as it was not useful for anything. it could allow deadlock detection, but it's not clear to me that returning EDEADLK (which a buggy program is likely to ignore) is better than deadlocking; at least the latter behavior prevents further data corruption. a correct program cannot invoke this situation anyway. the reader count and write lock state, as well as the "last minute" waiter flag have all been combined into a single atomic lock. this means all state transitions for the lock are atomic compare-and-swap operations. this makes establishing correctness much easier and may improve performance. finally, some code duplication has been cleaned up. more is called for, especially the standard __timedwait idiom repeated in all locks.
Diffstat (limited to 'src/thread/pthread_rwlock_tryrdlock.c')
1 files changed, 7 insertions, 7 deletions
diff --git a/src/thread/pthread_rwlock_tryrdlock.c b/src/thread/pthread_rwlock_tryrdlock.c
index f860ec71..fa271fcc 100644
--- a/src/thread/pthread_rwlock_tryrdlock.c
+++ b/src/thread/pthread_rwlock_tryrdlock.c
@@ -2,12 +2,12 @@
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rw)
- a_inc(&rw->_rw_readers);
- if (rw->_rw_wrlock) {
- a_dec(&rw->_rw_readers);
- if (rw->_rw_waiters && !rw->_rw_readers)
- __wake(&rw->_rw_readers, 1, 0);
- return EBUSY;
- }
+ int val, cnt;
+ do {
+ val = rw->_rw_lock;
+ cnt = val & 0x7fffffff;
+ if (cnt == 0x7fffffff) return EBUSY;
+ if (cnt == 0x7ffffffe) return EAGAIN;
+ } while (a_cas(&rw->_rw_lock, val, val+1) != val);
return 0;