@@ -22,27 +22,26 @@ impl RWLock {
2222 pub unsafe fn read ( & self ) {
2323 let r = libc:: pthread_rwlock_rdlock ( self . inner . get ( ) ) ;
2424
25- // According to the pthread_rwlock_rdlock spec, this function **may**
26- // fail with EDEADLK if a deadlock is detected. On the other hand
27- // pthread mutexes will *never* return EDEADLK if they are initialized
28- // as the "fast" kind (which ours always are). As a result, a deadlock
29- // situation may actually return from the call to pthread_rwlock_rdlock
30- // instead of blocking forever (as mutexes and Windows rwlocks do). Note
31- // that not all unix implementations, however, will return EDEADLK for
32- // their rwlocks .
25+ // According to POSIX, when a thread tries to acquire this read lock
26+ // while it already holds the write lock
27+ // (or vice versa, or tries to acquire the write lock twice),
28+ // " the call shall either deadlock or return [EDEADLK]"
29+ // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html,
30+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html).
31+ // So, in principle, all we have to do here is check `r == 0` to be sure we properly
32+ // got the lock .
3333 //
34- // We roughly maintain the deadlocking behavior by panicking to ensure
35- // that this lock acquisition does not succeed.
36- //
37- // We also check whether this lock is already write locked. This
38- // is only possible if it was write locked by the current thread and
39- // the implementation allows recursive locking. The POSIX standard
40- // doesn't require recursively locking a rwlock to deadlock, but we can't
41- // allow that because it could lead to aliasing issues.
34+ // However, (at least) glibc before version 2.25 does not conform to this spec,
35+ // and can return `r == 0` even when this thread already holds the write lock.
36+ // We thus check for this situation ourselves and panic when detecting that a thread
37+ // got the write lock more than once, or got a read and a write lock.
4238 if r == libc:: EAGAIN {
4339 panic ! ( "rwlock maximum reader count exceeded" ) ;
4440 } else if r == libc:: EDEADLK || ( r == 0 && * self . write_locked . get ( ) ) {
41+ // Above, we make sure to only access `write_locked` when `r == 0` to avoid
42+ // data races.
4543 if r == 0 {
44+ // `pthread_rwlock_rdlock` succeeded when it should not have.
4645 self . raw_unlock ( ) ;
4746 }
4847 panic ! ( "rwlock read lock would result in deadlock" ) ;
@@ -56,6 +55,7 @@ impl RWLock {
5655 let r = libc:: pthread_rwlock_tryrdlock ( self . inner . get ( ) ) ;
5756 if r == 0 {
5857 if * self . write_locked . get ( ) {
58+ // `pthread_rwlock_tryrdlock` succeeded when it should not have.
5959 self . raw_unlock ( ) ;
6060 false
6161 } else {
@@ -69,18 +69,21 @@ impl RWLock {
6969 #[ inline]
7070 pub unsafe fn write ( & self ) {
7171 let r = libc:: pthread_rwlock_wrlock ( self . inner . get ( ) ) ;
72- // See comments above for why we check for EDEADLK and write_locked. We
73- // also need to check that num_readers is 0 .
72+ // See comments above for why we check for EDEADLK and write_locked. For the same reason,
73+ // we also need to check that there are no readers (tracked in `num_readers`) .
7474 if r == libc:: EDEADLK
75- || * self . write_locked . get ( )
75+ || ( r == 0 && * self . write_locked . get ( ) )
7676 || self . num_readers . load ( Ordering :: Relaxed ) != 0
7777 {
78+ // Above, we make sure to only access `write_locked` when `r == 0` to avoid
79+ // data races.
7880 if r == 0 {
81+ // `pthread_rwlock_wrlock` succeeded when it should not have.
7982 self . raw_unlock ( ) ;
8083 }
8184 panic ! ( "rwlock write lock would result in deadlock" ) ;
8285 } else {
83- debug_assert_eq ! ( r, 0 ) ;
86+ assert_eq ! ( r, 0 ) ;
8487 }
8588 * self . write_locked . get ( ) = true ;
8689 }
@@ -89,6 +92,7 @@ impl RWLock {
8992 let r = libc:: pthread_rwlock_trywrlock ( self . inner . get ( ) ) ;
9093 if r == 0 {
9194 if * self . write_locked . get ( ) || self . num_readers . load ( Ordering :: Relaxed ) != 0 {
95+ // `pthread_rwlock_trywrlock` succeeded when it should not have.
9296 self . raw_unlock ( ) ;
9397 false
9498 } else {
0 commit comments