]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/cloudabi/mutex.rs
Rollup merge of #77339 - fusion-engineering-forks:tryfrom-nonzero-to-nonzero, r=dtolnay
[rust.git] / library / std / src / sys / cloudabi / mutex.rs
1 use crate::cell::Cell;
2 use crate::mem;
3 use crate::mem::MaybeUninit;
4 use crate::sync::atomic::{AtomicU32, Ordering};
5 use crate::sys::cloudabi::abi;
6 use crate::sys::rwlock::{self, RWLock};
7
8 extern "C" {
9     #[thread_local]
10     static __pthread_thread_id: abi::tid;
11 }
12
13 // Implement Mutex using an RWLock. This doesn't introduce any
14 // performance overhead in this environment, as the operations would be
15 // implemented identically.
16 pub struct Mutex(RWLock);
17
18 pub type MovableMutex = Mutex;
19
20 pub unsafe fn raw(m: &Mutex) -> &AtomicU32 {
21     rwlock::raw(&m.0)
22 }
23
24 impl Mutex {
25     pub const fn new() -> Mutex {
26         Mutex(RWLock::new())
27     }
28
29     pub unsafe fn init(&mut self) {
30         // This function should normally reinitialize the mutex after
31         // moving it to a different memory address. This implementation
32         // does not require adjustments after moving.
33     }
34
35     pub unsafe fn try_lock(&self) -> bool {
36         self.0.try_write()
37     }
38
39     pub unsafe fn lock(&self) {
40         self.0.write()
41     }
42
43     pub unsafe fn unlock(&self) {
44         self.0.write_unlock()
45     }
46
47     pub unsafe fn destroy(&self) {
48         self.0.destroy()
49     }
50 }
51
52 pub struct ReentrantMutex {
53     lock: AtomicU32,
54     recursion: Cell<u32>,
55 }
56
57 unsafe impl Send for ReentrantMutex {}
58 unsafe impl Sync for ReentrantMutex {}
59
60 impl ReentrantMutex {
61     pub const unsafe fn uninitialized() -> ReentrantMutex {
62         ReentrantMutex { lock: AtomicU32::new(abi::LOCK_UNLOCKED.0), recursion: Cell::new(0) }
63     }
64
65     pub unsafe fn init(&self) {}
66
67     pub unsafe fn try_lock(&self) -> bool {
68         // Attempt to acquire the lock.
69         if let Err(old) = self.lock.compare_exchange(
70             abi::LOCK_UNLOCKED.0,
71             __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
72             Ordering::Acquire,
73             Ordering::Relaxed,
74         ) {
75             // If we fail to acquire the lock, it may be the case
76             // that we've already acquired it and may need to recurse.
77             if old & !abi::LOCK_KERNEL_MANAGED.0 == __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 {
78                 self.recursion.set(self.recursion.get() + 1);
79                 true
80             } else {
81                 false
82             }
83         } else {
84             // Success.
85             assert_eq!(self.recursion.get(), 0, "Mutex has invalid recursion count");
86             true
87         }
88     }
89
90     pub unsafe fn lock(&self) {
91         if !self.try_lock() {
92             // Call into the kernel to acquire a write lock.
93             let lock = &self.lock as *const AtomicU32;
94             let subscription = abi::subscription {
95                 type_: abi::eventtype::LOCK_WRLOCK,
96                 union: abi::subscription_union {
97                     lock: abi::subscription_lock {
98                         lock: lock as *mut abi::lock,
99                         lock_scope: abi::scope::PRIVATE,
100                     },
101                 },
102                 ..mem::zeroed()
103             };
104             let mut event = MaybeUninit::<abi::event>::uninit();
105             let mut nevents = MaybeUninit::<usize>::uninit();
106             let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr());
107             assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire mutex");
108             let event = event.assume_init();
109             assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire mutex");
110         }
111     }
112
113     pub unsafe fn unlock(&self) {
114         assert_eq!(
115             self.lock.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
116             __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
117             "This mutex is locked by a different thread"
118         );
119
120         let r = self.recursion.get();
121         if r > 0 {
122             self.recursion.set(r - 1);
123         } else if !self
124             .lock
125             .compare_exchange(
126                 __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
127                 abi::LOCK_UNLOCKED.0,
128                 Ordering::Release,
129                 Ordering::Relaxed,
130             )
131             .is_ok()
132         {
133             // Lock is managed by kernelspace. Call into the kernel
134             // to unblock waiting threads.
135             let ret = abi::lock_unlock(
136                 &self.lock as *const AtomicU32 as *mut abi::lock,
137                 abi::scope::PRIVATE,
138             );
139             assert_eq!(ret, abi::errno::SUCCESS, "Failed to unlock a mutex");
140         }
141     }
142
143     pub unsafe fn destroy(&self) {
144         assert_eq!(
145             self.lock.load(Ordering::Relaxed),
146             abi::LOCK_UNLOCKED.0,
147             "Attempted to destroy locked mutex"
148         );
149         assert_eq!(self.recursion.get(), 0, "Recursion counter invalid");
150     }
151 }