]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys/unix/locks/pthread_rwlock.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / library / std / src / sys / unix / locks / pthread_rwlock.rs
CommitLineData
532ac7d7
XL
1use crate::cell::UnsafeCell;
2use crate::sync::atomic::{AtomicUsize, Ordering};
923072b8 3use crate::sys_common::lazy_box::{LazyBox, LazyInit};
1a4d82fc 4
04454e1e 5pub 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 11pub(crate) type MovableRwLock = LazyBox<RwLock>;
17df50a5 12
04454e1e
FG
13unsafe impl Send for RwLock {}
14unsafe impl Sync for RwLock {}
c34b1796 15
923072b8
FG
16impl LazyInit for RwLock {
17 fn init() -> Box<Self> {
18 Box::new(Self::new())
19 }
20}
21
04454e1e
FG
22impl 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
152impl Drop for RwLock {
153 #[inline]
154 fn drop(&mut self) {
155 unsafe { self.destroy() };
156 }
157}