11
11
use libc;
12
12
use cell:: UnsafeCell ;
13
13
14
- pub struct RWLock { inner : UnsafeCell < libc:: pthread_rwlock_t > }
14
+ pub struct RWLock {
15
+ inner : UnsafeCell < libc:: pthread_rwlock_t > ,
16
+ write_locked : UnsafeCell < bool > ,
17
+ }
15
18
16
19
unsafe impl Send for RWLock { }
17
20
unsafe impl Sync for RWLock { }
18
21
19
22
impl RWLock {
20
23
pub const fn new ( ) -> RWLock {
21
- RWLock { inner : UnsafeCell :: new ( libc:: PTHREAD_RWLOCK_INITIALIZER ) }
24
+ RWLock {
25
+ inner : UnsafeCell :: new ( libc:: PTHREAD_RWLOCK_INITIALIZER ) ,
26
+ write_locked : UnsafeCell :: new ( false ) ,
27
+ }
22
28
}
23
29
#[ inline]
24
30
pub unsafe fn read ( & self ) {
@@ -35,37 +41,74 @@ impl RWLock {
35
41
//
36
42
// We roughly maintain the deadlocking behavior by panicking to ensure
37
43
// that this lock acquisition does not succeed.
38
- if r == libc:: EDEADLK {
44
+ //
45
+ // We also check whether there this lock is already write locked. This
46
+ // is only possible if it was write locked by the current thread and
47
+ // the implementation allows recursive locking. The POSIX standard
48
+ // doesn't require recursivly locking a rwlock to deadlock, but we can't
49
+ // allow that because it could lead to aliasing issues.
50
+ if r == libc:: EDEADLK || * self . write_locked . get ( ) {
51
+ if r == 0 {
52
+ self . raw_unlock ( ) ;
53
+ }
39
54
panic ! ( "rwlock read lock would result in deadlock" ) ;
40
55
} else {
41
56
debug_assert_eq ! ( r, 0 ) ;
42
57
}
43
58
}
44
59
#[ inline]
45
60
pub unsafe fn try_read ( & self ) -> bool {
46
- libc:: pthread_rwlock_tryrdlock ( self . inner . get ( ) ) == 0
61
+ let r = libc:: pthread_rwlock_tryrdlock ( self . inner . get ( ) ) ;
62
+ if r == 0 && * self . write_locked . get ( ) {
63
+ self . raw_unlock ( ) ;
64
+ false
65
+ } else {
66
+ r == 0
67
+ }
47
68
}
48
69
#[ inline]
49
70
pub unsafe fn write ( & self ) {
50
71
let r = libc:: pthread_rwlock_wrlock ( self . inner . get ( ) ) ;
51
- // see comments above for why we check for EDEADLK
52
- if r == libc:: EDEADLK {
72
+ // see comments above for why we check for EDEADLK and write_locked
73
+ if r == libc:: EDEADLK || * self . write_locked . get ( ) {
74
+ if r == 0 {
75
+ self . raw_unlock ( ) ;
76
+ }
53
77
panic ! ( "rwlock write lock would result in deadlock" ) ;
54
78
} else {
55
79
debug_assert_eq ! ( r, 0 ) ;
56
80
}
81
+ * self . write_locked . get ( ) = true ;
57
82
}
58
83
#[ inline]
59
84
pub unsafe fn try_write ( & self ) -> bool {
60
- libc:: pthread_rwlock_trywrlock ( self . inner . get ( ) ) == 0
85
+ let r = libc:: pthread_rwlock_trywrlock ( self . inner . get ( ) ) ;
86
+ if r == 0 && * self . write_locked . get ( ) {
87
+ self . raw_unlock ( ) ;
88
+ false
89
+ } else if r == 0 {
90
+ * self . write_locked . get ( ) = true ;
91
+ true
92
+ } else {
93
+ false
94
+ }
61
95
}
62
96
#[ inline]
63
- pub unsafe fn read_unlock ( & self ) {
97
+ unsafe fn raw_unlock ( & self ) {
64
98
let r = libc:: pthread_rwlock_unlock ( self . inner . get ( ) ) ;
65
99
debug_assert_eq ! ( r, 0 ) ;
66
100
}
67
101
#[ inline]
68
- pub unsafe fn write_unlock ( & self ) { self . read_unlock ( ) }
102
+ pub unsafe fn read_unlock ( & self ) {
103
+ debug_assert ! ( !* self . write_locked. get( ) ) ;
104
+ self . raw_unlock ( ) ;
105
+ }
106
+ #[ inline]
107
+ pub unsafe fn write_unlock ( & self ) {
108
+ debug_assert ! ( * self . write_locked. get( ) ) ;
109
+ * self . write_locked . get ( ) = false ;
110
+ self . raw_unlock ( ) ;
111
+ }
69
112
#[ inline]
70
113
pub unsafe fn destroy ( & self ) {
71
114
let r = libc:: pthread_rwlock_destroy ( self . inner . get ( ) ) ;
0 commit comments