utils_atomics/locks.rs
1#[cfg(not(feature = "nightly"))]
2use core::marker::PhantomData;
3use core::{fmt::Debug, mem::ManuallyDrop};
4
5cfg_if::cfg_if! {
6 if #[cfg(feature = "std")] {
7 /// A synchronization primitive that can be used to coordinate threads.
8 ///
9 /// `Lock` is a type that represents a lock, which can be used to ensure that only one thread
10 /// can access a shared resource at a time.
11 ///
12 /// # Example
13 ///
14 /// ```
15 /// use utils_atomics::{Lock, lock};
16 ///
17 /// let (lock, lock_sub) = lock();
18 /// std::thread::spawn(move || {
19 /// // Do some work with the shared resource
20 /// lock.wake();
21 /// });
22 ///
23 /// // Do some work with the shared resource
24 /// lock_sub.wait();
25 /// ```
26 #[derive(Debug)]
27 #[repr(transparent)]
28 pub struct Lock (std::thread::Thread);
29
30 /// A helper type used for coordination with the `Lock`.
31 ///
32 /// `LockSub` is used in conjunction with a `Lock` to provide a way to wait for the lock to be
33 /// released.
34 #[derive(Debug)]
35 pub struct LockSub ((), #[cfg(not(feature = "nightly"))] PhantomData<*mut ()>);
36
37 impl Lock {
38 /// Transforms the `Lock` into a raw mutable pointer.
39 #[inline]
40 pub fn into_raw (self) -> *mut () {
41 static_assertions::assert_eq_align!(Lock, *mut ());
42 return unsafe { core::mem::transmute(self) }
43 }
44
45 /// Constructs a `Lock` from a raw mutable pointer.
46 ///
47 /// # Safety
48 ///
49 /// This function is unsafe because it assumes the provided pointer is valid and points to a
50 /// `Lock`.
51 #[inline]
52 pub unsafe fn from_raw (raw: *mut ()) -> Self {
53 static_assertions::assert_eq_align!(Lock, *mut ());
54 return Self(core::mem::transmute(raw))
55 }
56
57 /// Drops the `Lock` without waking up the waiting threads.
58 /// This method currently leaks memory when the `std` feature is disabled.
59 #[inline]
60 pub fn silent_drop (self) {
61 let mut this = ManuallyDrop::new(self);
62 unsafe { core::ptr::drop_in_place(&mut this.0) }
63 }
64 }
65
66 impl LockSub {
67 /// Blocks the current thread until the associated `Lock` is dropped.
68 ///
69 /// # Example
70 ///
71 /// ```
72 /// use utils_atomics::{Lock, lock};
73 ///
74 /// let (lock, lock_sub) = lock();
75 /// std::thread::spawn(move || {
76 /// // Do some work with the shared resource
77 /// lock.wake();
78 /// });
79 ///
80 /// // Do some work with the shared resource
81 /// lock_sub.wait();
82 /// ```
83 #[allow(clippy::unused_self)]
84 #[inline]
85 pub fn wait (self) {
86 std::thread::park();
87 }
88
89 /// Blocks the current thread for a specified duration or until the associated `Lock` is dropped,
90 /// whichever comes first.
91 ///
92 /// # Example
93 ///
94 /// ```
95 /// use utils_atomics::{Lock, lock};
96 /// use core::time::Duration;
97 /// use std::time::Instant;
98 ///
99 /// let (lock, lock_sub) = lock();
100 /// let handle = std::thread::spawn(move || {
101 /// // Do some work with the shared resource
102 /// std::thread::sleep(Duration::from_secs(3));
103 /// lock.wake();
104 /// });
105 ///
106 /// let start = Instant::now();
107 /// lock_sub.wait_timeout(Duration::from_secs(2));
108 /// assert!(start.elapsed() >= Duration::from_secs(2));
109 /// handle.join().unwrap();
110 /// ```
111 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
112 #[allow(clippy::unused_self)]
113 #[inline]
114 pub fn wait_timeout (self, dur: core::time::Duration) {
115 std::thread::park_timeout(dur);
116 }
117 }
118
119 impl Drop for Lock {
120 #[inline]
121 fn drop (&mut self) {
122 self.0.unpark();
123 }
124 }
125
126 /// Acquires a `Lock` and its corresponding `LockSub` for coordinating access to a shared resource.
127 ///
128 /// # Example
129 ///
130 /// ```
131 /// use utils_atomics::{Lock, lock};
132 ///
133 /// let (lock, lock_sub) = lock();
134 /// std::thread::spawn(move || {
135 /// // Do some work with the shared resource
136 /// lock.wake();
137 /// });
138 ///
139 /// // Do some work with the shared resource
140 /// lock_sub.wait();
141 /// ```
142 #[inline]
143 pub fn lock () -> (Lock, LockSub) {
144 return (Lock(std::thread::current()), LockSub((), #[cfg(not(feature = "nightly"))] PhantomData))
145 }
146 } else {
147 use alloc::sync::Arc;
148
149 /// A synchronization primitive that can be used to coordinate threads.
150 ///
151 /// `Lock` is a type that represents a lock, which can be used to ensure that only one thread
152 /// can access a shared resource at a time.
153 ///
154 /// # Example
155 ///
156 /// ```
157 /// use utils_atomics::{Lock, lock};
158 ///
159 /// let (lock, lock_sub) = lock();
160 /// std::thread::spawn(move || {
161 /// // Do some work with the shared resource
162 /// lock.wake();
163 /// });
164 ///
165 /// // Do some work with the shared resource
166 /// lock_sub.wait();
167 /// ```
168 #[derive(Debug)]
169 #[repr(transparent)]
170 pub struct Lock (alloc::sync::Arc<()>);
171
172 /// A helper type used for coordination with the `Lock`.
173 ///
174 /// `LockSub` is used in conjunction with a `Lock` to provide a way to wait for the lock to be
175 /// released.
176 #[derive(Debug)]
177 pub struct LockSub (alloc::sync::Arc<()>, #[cfg(not(feature = "nightly"))] PhantomData<*mut ()>);
178
179 impl Lock {
180 /// Transforms the `Lock` into a raw mutable pointer.
181 #[inline]
182 pub fn into_raw (self) -> *mut () {
183 let this = ManuallyDrop::new(self);
184 return unsafe { Arc::into_raw(core::ptr::read(&this.0)).cast_mut() }
185 }
186
187 /// Constructs a `Lock` from a raw mutable pointer.
188 ///
189 /// # Safety
190 ///
191 /// This function is unsafe because it assumes the provided pointer is valid and points to a
192 /// `Lock`.
193 #[inline]
194 pub unsafe fn from_raw (raw: *mut ()) -> Self {
195 return Self(Arc::from_raw(raw.cast_const()))
196 }
197
198 /// Drops the `Lock` without waking up the waiting threads.
199 /// This method currently leaks memory when the `std` feature is disabled.
200 #[inline]
201 pub fn silent_drop (self) {
202 core::mem::forget(self);
203 }
204 }
205
206 impl LockSub {
207 /// Blocks the current thread until the associated `Lock` is dropped.
208 ///
209 /// # Example
210 ///
211 /// ```
212 /// use utils_atomics::{Lock, lock};
213 ///
214 /// let (lock, lock_sub) = lock();
215 /// let handle = std::thread::spawn(move || {
216 /// // Do some work with the shared resource
217 /// lock.wake();
218 /// });
219 ///
220 /// // Do some work with the shared resource
221 /// lock_sub.wait();
222 /// handle.join().unwrap();
223 /// ```
224 #[inline]
225 pub fn wait (self) {
226 let mut this = self.0;
227 loop {
228 match alloc::sync::Arc::try_unwrap(this) {
229 Ok(_) => return,
230 Err(e) => this = e
231 }
232 core::hint::spin_loop()
233 }
234 }
235 }
236
237 /// Acquires a `Lock` and its corresponding `LockSub` for coordinating access to a shared resource.
238 ///
239 /// # Example
240 ///
241 /// ```
242 /// use utils_atomics::{Lock, lock};
243 ///
244 /// let (lock, lock_sub) = lock();
245 /// std::thread::spawn(move || {
246 /// // Do some work with the shared resource
247 /// lock.wake();
248 /// });
249 ///
250 /// // Do some work with the shared resource
251 /// lock_sub.wait();
252 /// ```
253 #[inline]
254 pub fn lock () -> (Lock, LockSub) {
255 let lock = alloc::sync::Arc::new(());
256 return (Lock(lock.clone()), LockSub(lock, #[cfg(not(feature = "nightly"))] PhantomData))
257 }
258
259 impl Drop for Lock {
260 #[inline]
261 fn drop (&mut self) {}
262 }
263 }
264}
265
266impl Lock {
267 /// Wakes up the waiting threads associated with the `Lock`.
268 ///
269 /// # Example
270 ///
271 /// ```
272 /// use utils_atomics::{Lock, lock};
273 ///
274 /// let (lock, lock_sub) = lock();
275 /// std::thread::spawn(move || {
276 /// // Do some work with the shared resource
277 /// lock.wake();
278 /// });
279 ///
280 /// // Do some work with the shared resource
281 /// lock_sub.wait();
282 /// ```
283 #[allow(clippy::unused_self)]
284 #[inline]
285 pub fn wake(self) {}
286}
287
288cfg_if::cfg_if! {
289 if #[cfg(feature = "nightly")] {
290 impl !Send for LockSub {}
291 } else {
292 unsafe impl Sync for LockSub {}
293 }
294}