]>
Commit | Line | Data |
---|---|---|
532ac7d7 XL |
1 | use crate::cell::UnsafeCell; |
2 | use crate::sync::atomic::{AtomicUsize, Ordering}; | |
923072b8 | 3 | use crate::sys_common::lazy_box::{LazyBox, LazyInit}; |
1a4d82fc | 4 | |
04454e1e | 5 | pub struct RwLock { |
3157f602 | 6 | inner: UnsafeCell<libc::pthread_rwlock_t>, |
a1dfa0c6 | 7 | write_locked: UnsafeCell<bool>, // guarded by the `inner` RwLock |
3157f602 XL |
8 | num_readers: AtomicUsize, |
9 | } | |
1a4d82fc | 10 | |
923072b8 | 11 | pub(crate) type MovableRwLock = LazyBox<RwLock>; |
17df50a5 | 12 | |
04454e1e FG |
13 | unsafe impl Send for RwLock {} |
14 | unsafe impl Sync for RwLock {} | |
c34b1796 | 15 | |
923072b8 FG |
16 | impl LazyInit for RwLock { |
17 | fn init() -> Box<Self> { | |
18 | Box::new(Self::new()) | |
19 | } | |
20 | } | |
21 | ||
04454e1e FG |
22 | impl RwLock { |
23 | pub const fn new() -> RwLock { | |
24 | RwLock { | |
3157f602 XL |
25 | inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), |
26 | write_locked: UnsafeCell::new(false), | |
27 | num_readers: AtomicUsize::new(0), | |
28 | } | |
62682a34 | 29 | } |
1a4d82fc JJ |
30 | #[inline] |
31 | pub unsafe fn read(&self) { | |
92a42be0 | 32 | let r = libc::pthread_rwlock_rdlock(self.inner.get()); |
d9579d0f | 33 | |
f9f354fc XL |
34 | // According to POSIX, when a thread tries to acquire this read lock |
35 | // while it already holds the write lock | |
36 | // (or vice versa, or tries to acquire the write lock twice), | |
37 | // "the call shall either deadlock or return [EDEADLK]" | |
38 | // (https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_wrlock.html, | |
39 | // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_rwlock_rdlock.html). | |
40 | // So, in principle, all we have to do here is check `r == 0` to be sure we properly | |
41 | // got the lock. | |
d9579d0f | 42 | // |
f9f354fc XL |
43 | // However, (at least) glibc before version 2.25 does not conform to this spec, |
44 | // and can return `r == 0` even when this thread already holds the write lock. | |
45 | // We thus check for this situation ourselves and panic when detecting that a thread | |
46 | // got the write lock more than once, or got a read and a write lock. | |
5bcae85e SL |
47 | if r == libc::EAGAIN { |
48 | panic!("rwlock maximum reader count exceeded"); | |
a1dfa0c6 | 49 | } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) { |
f9f354fc XL |
50 | // Above, we make sure to only access `write_locked` when `r == 0` to avoid |
51 | // data races. | |
3157f602 | 52 | if r == 0 { |
f9f354fc | 53 | // `pthread_rwlock_rdlock` succeeded when it should not have. |
3157f602 XL |
54 | self.raw_unlock(); |
55 | } | |
d9579d0f AL |
56 | panic!("rwlock read lock would result in deadlock"); |
57 | } else { | |
5e7ed085 FG |
58 | // POSIX does not make guarantees about all the errors that may be returned. |
59 | // See issue #94705 for more details. | |
60 | assert_eq!(r, 0, "unexpected error during rwlock read lock: {:?}", r); | |
3157f602 | 61 | self.num_readers.fetch_add(1, Ordering::Relaxed); |
d9579d0f | 62 | } |
1a4d82fc JJ |
63 | } |
64 | #[inline] | |
65 | pub unsafe fn try_read(&self) -> bool { | |
3157f602 XL |
66 | let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); |
67 | if r == 0 { | |
68 | if *self.write_locked.get() { | |
f9f354fc | 69 | // `pthread_rwlock_tryrdlock` succeeded when it should not have. |
3157f602 XL |
70 | self.raw_unlock(); |
71 | false | |
72 | } else { | |
73 | self.num_readers.fetch_add(1, Ordering::Relaxed); | |
74 | true | |
75 | } | |
76 | } else { | |
77 | false | |
78 | } | |
1a4d82fc JJ |
79 | } |
80 | #[inline] | |
81 | pub unsafe fn write(&self) { | |
92a42be0 | 82 | let r = libc::pthread_rwlock_wrlock(self.inner.get()); |
f9f354fc XL |
83 | // See comments above for why we check for EDEADLK and write_locked. For the same reason, |
84 | // we also need to check that there are no readers (tracked in `num_readers`). | |
dfeec247 | 85 | if r == libc::EDEADLK |
f9f354fc | 86 | || (r == 0 && *self.write_locked.get()) |
dfeec247 XL |
87 | || self.num_readers.load(Ordering::Relaxed) != 0 |
88 | { | |
f9f354fc XL |
89 | // Above, we make sure to only access `write_locked` when `r == 0` to avoid |
90 | // data races. | |
3157f602 | 91 | if r == 0 { |
f9f354fc | 92 | // `pthread_rwlock_wrlock` succeeded when it should not have. |
3157f602 XL |
93 | self.raw_unlock(); |
94 | } | |
d9579d0f AL |
95 | panic!("rwlock write lock would result in deadlock"); |
96 | } else { | |
f9f354fc XL |
97 | // According to POSIX, for a properly initialized rwlock this can only |
98 | // return EDEADLK or 0. We rely on that. | |
d9579d0f AL |
99 | debug_assert_eq!(r, 0); |
100 | } | |
3157f602 | 101 | *self.write_locked.get() = true; |
1a4d82fc JJ |
102 | } |
103 | #[inline] | |
104 | pub unsafe fn try_write(&self) -> bool { | |
3157f602 XL |
105 | let r = libc::pthread_rwlock_trywrlock(self.inner.get()); |
106 | if r == 0 { | |
107 | if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { | |
f9f354fc | 108 | // `pthread_rwlock_trywrlock` succeeded when it should not have. |
3157f602 XL |
109 | self.raw_unlock(); |
110 | false | |
111 | } else { | |
112 | *self.write_locked.get() = true; | |
113 | true | |
114 | } | |
115 | } else { | |
116 | false | |
117 | } | |
1a4d82fc JJ |
118 | } |
119 | #[inline] | |
3157f602 | 120 | unsafe fn raw_unlock(&self) { |
92a42be0 | 121 | let r = libc::pthread_rwlock_unlock(self.inner.get()); |
1a4d82fc JJ |
122 | debug_assert_eq!(r, 0); |
123 | } | |
124 | #[inline] | |
3157f602 XL |
125 | pub unsafe fn read_unlock(&self) { |
126 | debug_assert!(!*self.write_locked.get()); | |
127 | self.num_readers.fetch_sub(1, Ordering::Relaxed); | |
128 | self.raw_unlock(); | |
129 | } | |
130 | #[inline] | |
131 | pub unsafe fn write_unlock(&self) { | |
132 | debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0); | |
133 | debug_assert!(*self.write_locked.get()); | |
134 | *self.write_locked.get() = false; | |
135 | self.raw_unlock(); | |
136 | } | |
1a4d82fc | 137 | #[inline] |
923072b8 | 138 | unsafe fn destroy(&mut self) { |
92a42be0 | 139 | let r = libc::pthread_rwlock_destroy(self.inner.get()); |
85aaf69f SL |
140 | // On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a |
141 | // rwlock that was just initialized with | |
92a42be0 | 142 | // libc::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked) |
85aaf69f | 143 | // or pthread_rwlock_init() is called, this behaviour no longer occurs. |
d9579d0f AL |
144 | if cfg!(target_os = "dragonfly") { |
145 | debug_assert!(r == 0 || r == libc::EINVAL); | |
146 | } else { | |
147 | debug_assert_eq!(r, 0); | |
148 | } | |
85aaf69f | 149 | } |
1a4d82fc | 150 | } |
923072b8 FG |
151 | |
152 | impl Drop for RwLock { | |
153 | #[inline] | |
154 | fn drop(&mut self) { | |
155 | unsafe { self.destroy() }; | |
156 | } | |
157 | } |