@@ -22,27 +22,26 @@ impl RWLock {
22
22
pub unsafe fn read ( & self ) {
23
23
let r = libc:: pthread_rwlock_rdlock ( self . inner . get ( ) ) ;
24
24
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 .
33
33
//
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.
42
38
if r == libc:: EAGAIN {
43
39
panic ! ( "rwlock maximum reader count exceeded" ) ;
44
40
} 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.
45
43
if r == 0 {
44
+ // `pthread_rwlock_rdlock` succeeded when it should not have.
46
45
self . raw_unlock ( ) ;
47
46
}
48
47
panic ! ( "rwlock read lock would result in deadlock" ) ;
@@ -56,6 +55,7 @@ impl RWLock {
56
55
let r = libc:: pthread_rwlock_tryrdlock ( self . inner . get ( ) ) ;
57
56
if r == 0 {
58
57
if * self . write_locked . get ( ) {
58
+ // `pthread_rwlock_tryrdlock` succeeded when it should not have.
59
59
self . raw_unlock ( ) ;
60
60
false
61
61
} else {
@@ -69,18 +69,21 @@ impl RWLock {
69
69
#[ inline]
70
70
pub unsafe fn write ( & self ) {
71
71
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`) .
74
74
if r == libc:: EDEADLK
75
- || * self . write_locked . get ( )
75
+ || ( r == 0 && * self . write_locked . get ( ) )
76
76
|| self . num_readers . load ( Ordering :: Relaxed ) != 0
77
77
{
78
+ // Above, we make sure to only access `write_locked` when `r == 0` to avoid
79
+ // data races.
78
80
if r == 0 {
81
+ // `pthread_rwlock_wrlock` succeeded when it should not have.
79
82
self . raw_unlock ( ) ;
80
83
}
81
84
panic ! ( "rwlock write lock would result in deadlock" ) ;
82
85
} else {
83
- debug_assert_eq ! ( r, 0 ) ;
86
+ assert_eq ! ( r, 0 ) ;
84
87
}
85
88
* self . write_locked . get ( ) = true ;
86
89
}
@@ -89,6 +92,7 @@ impl RWLock {
89
92
let r = libc:: pthread_rwlock_trywrlock ( self . inner . get ( ) ) ;
90
93
if r == 0 {
91
94
if * self . write_locked . get ( ) || self . num_readers . load ( Ordering :: Relaxed ) != 0 {
95
+ // `pthread_rwlock_trywrlock` succeeded when it should not have.
92
96
self . raw_unlock ( ) ;
93
97
false
94
98
} else {
0 commit comments