lazy_exclusive/
lib.rs

1#![no_std]
2
3use core::{
4    cell::UnsafeCell,
5    fmt::{self, Debug},
6    ops::{Deref, DerefMut},
7};
8#[cfg(feature = "use-locks")]
9use lock::Lock;
10
11#[cfg(feature = "use-locks")]
12mod lock;
13
14#[derive(Clone, Copy, PartialEq, Eq, Debug)]
15#[non_exhaustive]
16pub enum State {
17    Unlocked,
18    Locked,
19    Poisoned,
20}
21
22/// A Cell for [`State`]. Used for const access to its data
23pub struct StateCell {
24    inner: UnsafeCell<State>,
25}
26
27impl StateCell {
28    pub const fn new(data: State) -> Self {
29        Self {
30            inner: UnsafeCell::new(data),
31        }
32    }
33
34    pub const fn get(&self) -> State {
35        // SAFETY: self.inner.get() is never an invalid pointer
36        unsafe { *self.inner.get() }
37    }
38
39    pub fn set(&self, data: State) {
40        let _ = unsafe { core::mem::replace(&mut *self.inner.get(), data) };
41    }
42}
43
44/// A container type like [`LazyLock`].
45/// Allows mutable access, but only one reference at a time.
46/// ```rust
47/// use lazy_exclusive::LazyExclusive;
48///
49/// static LAZY: LazyExclusive<i32> = LazyExclusive::new(123);
50/// let lock = LAZY.get().unwrap();
51/// assert_eq!(*lock, 123);
52/// assert!(LAZY.is_locked());
53/// ```
54///
55/// [`LazyLock`]: std::sync::LazyLock
56pub struct LazyExclusive<T> {
57    state: StateCell,
58    data: UnsafeCell<T>,
59    #[cfg(feature = "use-locks")]
60    lock: Lock,
61}
62
63unsafe impl<T> Send for LazyExclusive<T> {}
64unsafe impl<T> Sync for LazyExclusive<T> {}
65
66pub struct Mut<'a, T> {
67    source: &'a LazyExclusive<T>,
68}
69
70impl<T> Mut<'_, T> {
71    const fn inner(&mut self) -> &mut T {
72        unsafe {
73            self.source
74                .data
75                .get()
76                .as_mut()
77                .expect("source.data is never a null pointer")
78        }
79    }
80}
81
82impl<T> Drop for Mut<'_, T> {
83    fn drop(&mut self) {
84        self.source.state.set(State::Unlocked);
85        #[cfg(feature = "use-locks")]
86        {
87            self.source.lock.unlock();
88
89            #[cfg(feature = "std")]
90            {
91                extern crate std;
92                if std::thread::panicking() {
93                    self.source.state.set(State::Poisoned);
94                }
95            }
96        }
97    }
98}
99
100impl<T> Deref for Mut<'_, T> {
101    type Target = T;
102
103    fn deref(&self) -> &Self::Target {
104        unsafe {
105            self.source
106                .data
107                .get()
108                .as_ref()
109                .expect("source.data is never a null pointer")
110        }
111    }
112}
113
114impl<T> AsRef<T> for Mut<'_, T> {
115    fn as_ref(&self) -> &T {
116        self
117    }
118}
119
120impl<T> AsMut<T> for Mut<'_, T> {
121    fn as_mut(&mut self) -> &mut T {
122        self
123    }
124}
125
126impl<T> DerefMut for Mut<'_, T> {
127    fn deref_mut(&mut self) -> &mut Self::Target {
128        self.inner()
129    }
130}
131
132impl<T> LazyExclusive<T> {
133    pub const fn new(data: T) -> Self {
134        let data = UnsafeCell::new(data);
135        let state = StateCell::new(State::Unlocked);
136
137        #[cfg(not(feature = "use-locks"))]
138        return Self { state, data };
139        #[cfg(feature = "use-locks")]
140        Self {
141            state,
142            data,
143            lock: Lock::new(),
144        }
145    }
146
147    /// Get a handle to the inner data. Returns [`None`] if a handle already exists
148    pub fn get(&self) -> Option<Mut<'_, T>> {
149        match self.state.get() {
150            State::Unlocked => {
151                self.state.set(State::Locked);
152                #[cfg(feature = "use-locks")]
153                self.lock.lock();
154                Some(Mut { source: self })
155            }
156            _ => None,
157        }
158    }
159
160    /// Set the inner value to [`new_value`]. Panics if the data is already locked
161    pub fn swap(&self, new_value: T) {
162        assert_eq!(self.state.get(), State::Unlocked);
163        unsafe {
164            let t = self.data.get().as_mut().unwrap();
165            *t = new_value;
166            self.state.set(State::Unlocked);
167
168            #[cfg(feature = "use-locks")]
169            self.lock.reset();
170        }
171    }
172
173    pub const fn get_state(&self) -> State {
174        self.state.get()
175    }
176
177    /// Wait for the data to unlock and return a new handle
178    #[cfg(feature = "use-locks")]
179    pub fn wait(&self) -> Mut<'_, T> {
180        self.lock.lock();
181        assert_eq!(self.state.get(), State::Unlocked, "The data was poisoned");
182        self.state.set(State::Locked);
183        Mut { source: self }
184    }
185
186    pub fn into_inner(self) -> T {
187        match self.state.get() {
188            State::Unlocked => self.data.into_inner(),
189            State::Locked => panic!("locked"),
190            State::Poisoned => panic!("poisoned"),
191        }
192    }
193
194    pub const fn is_unlocked(&self) -> bool {
195        matches!(self.state.get(), State::Unlocked)
196    }
197
198    pub const fn is_locked(&self) -> bool {
199        matches!(self.state.get(), State::Locked)
200    }
201
202    pub const fn is_poisoned(&self) -> bool {
203        matches!(self.state.get(), State::Poisoned)
204    }
205}
206
207impl<T: Debug> Debug for LazyExclusive<T> {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        let data: &dyn Debug = match self.state.get() {
210            State::Unlocked => unsafe { self.data.get().as_mut().expect("Should never fail") },
211            State::Locked => &"<locked>",
212            State::Poisoned => &"<poisoned>",
213        };
214
215        f.debug_struct("LazyExclusive")
216            .field("state", &self.state.get())
217            .field("data", data)
218            .finish()
219    }
220}
221
222impl<T> From<T> for LazyExclusive<T> {
223    fn from(value: T) -> Self {
224        Self::new(value)
225    }
226}
227
228impl<T: Clone> Clone for LazyExclusive<T> {
229    fn clone(&self) -> Self {
230        let data = match self.state.get() {
231            State::Unlocked => unsafe { self.data.get().as_ref().expect("Should never fail") },
232            State::Locked => panic!("locked"),
233            State::Poisoned => panic!("poisoned"),
234        };
235
236        Self::new(data.clone())
237    }
238}
239
240impl<T: Default> Default for LazyExclusive<T> {
241    fn default() -> Self {
242        Self::new(T::default())
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use crate::{LazyExclusive, State};
249
250    #[test]
251    fn basic() {
252        let shared = LazyExclusive::new(230);
253        let mut1 = shared.get();
254        assert!(mut1.is_some());
255        assert!(shared.get().is_none());
256
257        let mut1 = mut1.unwrap();
258        let inner = *mut1;
259        assert_eq!(inner, 230);
260    }
261
262    #[test]
263    fn static_test() {
264        static SHARED: LazyExclusive<i32> = LazyExclusive::new(1231);
265        let pointer = SHARED.get().unwrap();
266        assert_eq!(*pointer, 1231);
267    }
268
269    #[cfg(all(feature = "use-locks", feature = "std"))]
270    #[test]
271    fn lock_test() {
272        extern crate std;
273        use crate::State;
274        use std::time::{Duration, Instant};
275
276        let start = Instant::now();
277        let five_seconds = Duration::from_secs(5);
278        static SHARED: LazyExclusive<i32> = LazyExclusive::new(120);
279        let mut lock = SHARED.get().unwrap();
280
281        std::thread::spawn(move || {
282            *lock *= 2;
283            std::thread::sleep(Duration::new(5, 0));
284        });
285
286        assert_eq!(SHARED.get_state(), State::Locked);
287        let new_lock = SHARED.wait();
288        assert_eq!(*new_lock, 120 * 2);
289        assert!(start.elapsed() >= five_seconds);
290    }
291
292    #[test]
293    fn reset() {
294        let lazy = LazyExclusive::new(120);
295        lazy.swap(10);
296        assert_eq!(*lazy.get().unwrap(), 10);
297        assert_eq!(lazy.get_state(), State::Unlocked);
298    }
299
300    #[test]
301    fn clone() {
302        let lazy = LazyExclusive::new(120);
303        let clone = lazy.clone();
304
305        assert_eq!(lazy.into_inner(), clone.into_inner());
306    }
307}