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}