]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/cloudabi/rwlock.rs
Rollup merge of #59232 - saleemjaffer:mir_place_refactor, r=oli-obk
[rust.git] / src / libstd / sys / cloudabi / rwlock.rs
1 use crate::cell::UnsafeCell;
2 use crate::mem;
3 use crate::sync::atomic::{AtomicU32, Ordering};
4 use crate::sys::cloudabi::abi;
5
6 extern "C" {
7     #[thread_local]
8     static __pthread_thread_id: abi::tid;
9 }
10
11 #[thread_local]
12 static mut RDLOCKS_ACQUIRED: u32 = 0;
13
14 pub struct RWLock {
15     lock: UnsafeCell<AtomicU32>,
16 }
17
18 pub unsafe fn raw(r: &RWLock) -> *mut AtomicU32 {
19     r.lock.get()
20 }
21
22 unsafe impl Send for RWLock {}
23 unsafe impl Sync for RWLock {}
24
25 const NEW: RWLock = RWLock {
26     lock: UnsafeCell::new(AtomicU32::new(abi::LOCK_UNLOCKED.0)),
27 };
28
29 impl RWLock {
30     pub const fn new() -> RWLock {
31         NEW
32     }
33
34     pub unsafe fn try_read(&self) -> bool {
35         let lock = self.lock.get();
36         let mut old = abi::LOCK_UNLOCKED.0;
37         while let Err(cur) =
38             (*lock).compare_exchange_weak(old, old + 1, Ordering::Acquire, Ordering::Relaxed)
39         {
40             if (cur & abi::LOCK_WRLOCKED.0) != 0 {
41                 // Another thread already has a write lock.
42                 assert_ne!(
43                     old & !abi::LOCK_KERNEL_MANAGED.0,
44                     __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
45                     "Attempted to acquire a read lock while holding a write lock"
46                 );
47                 return false;
48             } else if (old & abi::LOCK_KERNEL_MANAGED.0) != 0 && RDLOCKS_ACQUIRED == 0 {
49                 // Lock has threads waiting for the lock. Only acquire
50                 // the lock if we have already acquired read locks. In
51                 // that case, it is justified to acquire this lock to
52                 // prevent a deadlock.
53                 return false;
54             }
55             old = cur;
56         }
57
58         RDLOCKS_ACQUIRED += 1;
59         true
60     }
61
62     pub unsafe fn read(&self) {
63         if !self.try_read() {
64             // Call into the kernel to acquire a read lock.
65             let lock = self.lock.get();
66             let subscription = abi::subscription {
67                 type_: abi::eventtype::LOCK_RDLOCK,
68                 union: abi::subscription_union {
69                     lock: abi::subscription_lock {
70                         lock: lock as *mut abi::lock,
71                         lock_scope: abi::scope::PRIVATE,
72                     },
73                 },
74                 ..mem::zeroed()
75             };
76             let mut event: abi::event = mem::uninitialized();
77             let mut nevents: usize = mem::uninitialized();
78             let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
79             assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire read lock");
80             assert_eq!(
81                 event.error,
82                 abi::errno::SUCCESS,
83                 "Failed to acquire read lock"
84             );
85
86             RDLOCKS_ACQUIRED += 1;
87         }
88     }
89
90     pub unsafe fn read_unlock(&self) {
91         // Perform a read unlock. We can do this in userspace, except when
92         // other threads are blocked and we are performing the last unlock.
93         // In that case, call into the kernel.
94         //
95         // Other threads may attempt to increment the read lock count,
96         // meaning that the call into the kernel could be spurious. To
97         // prevent this from happening, upgrade to a write lock first. This
98         // allows us to call into the kernel, having the guarantee that the
99         // lock value will not change in the meantime.
100         assert!(RDLOCKS_ACQUIRED > 0, "Bad lock count");
101         let mut old = 1;
102         loop {
103             let lock = self.lock.get();
104             if old == 1 | abi::LOCK_KERNEL_MANAGED.0 {
105                 // Last read lock while threads are waiting. Attempt to upgrade
106                 // to a write lock before calling into the kernel to unlock.
107                 if let Err(cur) = (*lock).compare_exchange_weak(
108                     old,
109                     __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 | abi::LOCK_KERNEL_MANAGED.0,
110                     Ordering::Acquire,
111                     Ordering::Relaxed,
112                 ) {
113                     old = cur;
114                 } else {
115                     // Call into the kernel to unlock.
116                     let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
117                     assert_eq!(ret, abi::errno::SUCCESS, "Failed to write unlock a rwlock");
118                     break;
119                 }
120             } else {
121                 // No threads waiting or not the last read lock. Just decrement
122                 // the read lock count.
123                 assert_ne!(
124                     old & !abi::LOCK_KERNEL_MANAGED.0,
125                     0,
126                     "This rwlock is not locked"
127                 );
128                 assert_eq!(
129                     old & abi::LOCK_WRLOCKED.0,
130                     0,
131                     "Attempted to read-unlock a write-locked rwlock"
132                 );
133                 if let Err(cur) = (*lock).compare_exchange_weak(
134                     old,
135                     old - 1,
136                     Ordering::Acquire,
137                     Ordering::Relaxed,
138                 ) {
139                     old = cur;
140                 } else {
141                     break;
142                 }
143             }
144         }
145
146         RDLOCKS_ACQUIRED -= 1;
147     }
148
149     pub unsafe fn try_write(&self) -> bool {
150         // Attempt to acquire the lock.
151         let lock = self.lock.get();
152         if let Err(old) = (*lock).compare_exchange(
153             abi::LOCK_UNLOCKED.0,
154             __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
155             Ordering::Acquire,
156             Ordering::Relaxed,
157         ) {
158             // Failure. Crash upon recursive acquisition.
159             assert_ne!(
160                 old & !abi::LOCK_KERNEL_MANAGED.0,
161                 __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
162                 "Attempted to recursive write-lock a rwlock",
163             );
164             false
165         } else {
166             // Success.
167             true
168         }
169     }
170
171     pub unsafe fn write(&self) {
172         if !self.try_write() {
173             // Call into the kernel to acquire a write lock.
174             let lock = self.lock.get();
175             let subscription = abi::subscription {
176                 type_: abi::eventtype::LOCK_WRLOCK,
177                 union: abi::subscription_union {
178                     lock: abi::subscription_lock {
179                         lock: lock as *mut abi::lock,
180                         lock_scope: abi::scope::PRIVATE,
181                     },
182                 },
183                 ..mem::zeroed()
184             };
185             let mut event: abi::event = mem::uninitialized();
186             let mut nevents: usize = mem::uninitialized();
187             let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
188             assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire write lock");
189             assert_eq!(
190                 event.error,
191                 abi::errno::SUCCESS,
192                 "Failed to acquire write lock"
193             );
194         }
195     }
196
197     pub unsafe fn write_unlock(&self) {
198         let lock = self.lock.get();
199         assert_eq!(
200             (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
201             __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
202             "This rwlock is not write-locked by this thread"
203         );
204
205         if !(*lock)
206             .compare_exchange(
207                 __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
208                 abi::LOCK_UNLOCKED.0,
209                 Ordering::Release,
210                 Ordering::Relaxed,
211             )
212             .is_ok()
213         {
214             // Lock is managed by kernelspace. Call into the kernel
215             // to unblock waiting threads.
216             let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE);
217             assert_eq!(ret, abi::errno::SUCCESS, "Failed to write unlock a rwlock");
218         }
219     }
220
221     pub unsafe fn destroy(&self) {
222         let lock = self.lock.get();
223         assert_eq!(
224             (*lock).load(Ordering::Relaxed),
225             abi::LOCK_UNLOCKED.0,
226             "Attempted to destroy locked rwlock"
227         );
228     }
229 }