lazy_exclusive/
lib.rs

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