]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/cloudabi/mutex.rs
Auto merge of #78409 - pietroalbini:build-manifest-checksum-cache, r=Mark-Simulacrum
[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             // SAFE: The caller must to ensure that `event` and `nevents` are initialized.
107             let ret =
108                 unsafe { abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr()) };
109             assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire mutex");
110             let event = event.assume_init();
111             assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire mutex");
112         }
113     }
114
115     pub unsafe fn unlock(&self) {
116         assert_eq!(
117             self.lock.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
118             __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
119             "This mutex is locked by a different thread"
120         );
121
122         let r = self.recursion.get();
123         if r > 0 {
124             self.recursion.set(r - 1);
125         } else if !self
126             .lock
127             .compare_exchange(
128                 __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
129                 abi::LOCK_UNLOCKED.0,
130                 Ordering::Release,
131                 Ordering::Relaxed,
132             )
133             .is_ok()
134         {
135             // Lock is managed by kernelspace. Call into the kernel
136             // to unblock waiting threads.
137             let ret = abi::lock_unlock(
138                 &self.lock as *const AtomicU32 as *mut abi::lock,
139                 abi::scope::PRIVATE,
140             );
141             assert_eq!(ret, abi::errno::SUCCESS, "Failed to unlock a mutex");
142         }
143     }
144
145     pub unsafe fn destroy(&self) {
146         assert_eq!(
147             self.lock.load(Ordering::Relaxed),
148             abi::LOCK_UNLOCKED.0,
149             "Attempted to destroy locked mutex"
150         );
151         assert_eq!(self.recursion.get(), 0, "Recursion counter invalid");
152     }
153 }