cubecl_environment/sync/
base.rs1#[cfg(not(feature = "std"))]
2use spin::{Mutex as MutexImported, MutexGuard, Once as OnceImported, RwLock as RwLockImported};
3#[cfg(feature = "std")]
4use std::sync::{
5 Mutex as MutexImported, MutexGuard, OnceLock as OnceImported, RwLock as RwLockImported,
6};
7
8#[cfg(not(feature = "std"))]
9pub use spin::{LazyLock, RwLockReadGuard, RwLockWriteGuard};
10#[cfg(feature = "std")]
11pub use std::sync::{LazyLock, RwLockReadGuard, RwLockWriteGuard};
12
13pub use spin::Once;
18
19#[cfg(target_has_atomic = "ptr")]
20pub use alloc::sync::Arc;
21#[cfg(not(target_has_atomic = "ptr"))]
22pub use portable_atomic_util::Arc;
23
24#[cfg(target_has_atomic = "ptr")]
25pub use core::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicUsize, Ordering};
26#[cfg(not(target_has_atomic = "ptr"))]
27pub use portable_atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicUsize, Ordering};
28
29#[derive(Debug, Default)]
42pub struct Mutex<T: ?Sized> {
43 inner: MutexImported<T>,
44}
45
46impl<T> Mutex<T> {
47 #[inline(always)]
49 pub const fn new(value: T) -> Self {
50 Self {
51 inner: MutexImported::new(value),
52 }
53 }
54
55 #[inline(always)]
58 pub fn into_inner(self) -> T {
59 #[cfg(not(feature = "std"))]
60 {
61 self.inner.into_inner()
62 }
63
64 #[cfg(feature = "std")]
65 {
66 self.inner
67 .into_inner()
68 .unwrap_or_else(|poisoned| poisoned.into_inner())
69 }
70 }
71}
72
73impl<T: ?Sized> Mutex<T> {
74 #[inline(always)]
81 pub fn lock(&self) -> MutexGuard<'_, T> {
82 #[cfg(not(feature = "std"))]
83 {
84 self.inner.lock()
85 }
86
87 #[cfg(feature = "std")]
88 {
89 self.inner
90 .lock()
91 .unwrap_or_else(|poisoned| poisoned.into_inner())
92 }
93 }
94}
95
96#[derive(Debug)]
101pub struct RwLock<T: ?Sized> {
102 inner: RwLockImported<T>,
103}
104
105impl<T> RwLock<T> {
106 #[inline(always)]
108 pub const fn new(value: T) -> Self {
109 Self {
110 inner: RwLockImported::new(value),
111 }
112 }
113}
114
115impl<T: ?Sized> RwLock<T> {
116 #[inline(always)]
121 pub fn read(&self) -> RwLockReadGuard<'_, T> {
122 #[cfg(not(feature = "std"))]
123 {
124 self.inner.read()
125 }
126 #[cfg(feature = "std")]
127 {
128 self.inner
129 .read()
130 .unwrap_or_else(|poisoned| poisoned.into_inner())
131 }
132 }
133
134 #[inline(always)]
139 pub fn write(&self) -> RwLockWriteGuard<'_, T> {
140 #[cfg(not(feature = "std"))]
141 {
142 self.inner.write()
143 }
144
145 #[cfg(feature = "std")]
146 {
147 self.inner
148 .write()
149 .unwrap_or_else(|poisoned| poisoned.into_inner())
150 }
151 }
152}
153
154#[allow(dead_code)]
160#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
161pub struct ThreadId(core::num::NonZeroU64);
162
163pub struct SyncOnceCell<T>(OnceImported<T>);
167
168impl<T> Default for SyncOnceCell<T> {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174impl<T> SyncOnceCell<T> {
175 #[inline(always)]
177 pub fn new() -> Self {
178 Self(OnceImported::new())
179 }
180
181 #[inline(always)]
183 pub fn initialized(value: T) -> Self {
184 #[cfg(not(feature = "std"))]
185 {
186 let cell = OnceImported::initialized(value);
187 Self(cell)
188 }
189
190 #[cfg(feature = "std")]
191 {
192 let cell = OnceImported::new();
193 let _ = cell.set(value);
196
197 Self(cell)
198 }
199 }
200
201 #[inline(always)]
204 pub fn get_or_init<F>(&self, f: F) -> &T
205 where
206 F: FnOnce() -> T,
207 {
208 #[cfg(not(feature = "std"))]
209 {
210 self.0.call_once(f)
211 }
212
213 #[cfg(feature = "std")]
214 {
215 self.0.get_or_init(f)
216 }
217 }
218}
219
220#[cfg(all(test, feature = "std"))]
221mod tests {
222 use super::*;
223
224 #[test]
227 fn poisoned_mutex_recovers() {
228 let mutex = Mutex::new(0u32);
229
230 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
231 let _guard = mutex.lock();
232 panic!("poison the lock");
233 }))
234 .unwrap_err();
235
236 *mutex.lock() += 1;
237 assert_eq!(*mutex.lock(), 1);
238 }
239
240 #[test]
241 fn poisoned_rwlock_recovers() {
242 let lock = RwLock::new(0u32);
243
244 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
245 let _guard = lock.write();
246 panic!("poison the lock");
247 }))
248 .unwrap_err();
249
250 assert_eq!(*lock.read(), 0);
251 }
252
253 #[test]
254 fn a_mutex_locks_a_trait_object() {
255 let mutex: Arc<Mutex<dyn Iterator<Item = u32> + Send>> = Arc::new(Mutex::new(0..3));
256
257 assert_eq!(mutex.lock().next(), Some(0));
258 assert_eq!(mutex.lock().next(), Some(1));
259 }
260
261 #[test]
262 fn an_rwlock_locks_a_trait_object() {
263 let lock: Arc<RwLock<dyn Iterator<Item = u32> + Send + Sync>> = Arc::new(RwLock::new(0..3));
264
265 assert_eq!(lock.write().next(), Some(0));
266 assert_eq!(lock.read().size_hint(), (2, Some(2)));
267 }
268}