corcovado/
lazycell.rs

1// Original work Copyright (c) 2014 The Rust Project Developers
2// Modified work Copyright (c) 2016-2018 Nikita Pekin and the lazycell contributors
3// Modified work Copyright (c) 2022 Raphael Amorim and the rio contributors
4// See the README.md file at the top-level directory of this distribution.
5//
6// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
7// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
9// option. This file may not be copied, modified, or distributed
10// except according to those terms.
11
12//! This crate provides a `LazyCell` struct which acts as a lazily filled
13//! `Cell`.
14//!
15//! With a `RefCell`, the inner contents cannot be borrowed for the lifetime of
16//! the entire object, but only of the borrows returned. A `LazyCell` is a
17//! variation on `RefCell` which allows borrows to be tied to the lifetime of
18//! the outer object.
19//!
20//! `AtomicLazyCell` is a variant that uses an atomic variable to manage
21//! coordination in a thread-safe fashion. The limitation of an `AtomicLazyCell`
22//! is that after it is initialized, it can't be modified.
23
24use std::cell::UnsafeCell;
25use std::mem;
26use std::sync::atomic::{AtomicUsize, Ordering};
27
28/// A lazily filled `Cell`, with mutable contents.
29///
30/// A `LazyCell` is completely frozen once filled, **unless** you have `&mut`
31/// access to it, in which case `LazyCell::borrow_mut` may be used to mutate the
32/// contents.
33#[derive(Debug, Default)]
34pub struct LazyCell<T> {
35    inner: UnsafeCell<Option<T>>,
36}
37
38impl<T> LazyCell<T> {
39    /// Creates a new, empty, `LazyCell`.
40    pub fn new() -> LazyCell<T> {
41        LazyCell {
42            inner: UnsafeCell::new(None),
43        }
44    }
45
46    /// Put a value into this cell.
47    ///
48    /// This function will return `Err(value)` is the cell is already full.
49    pub fn fill(&self, value: T) -> Result<(), T> {
50        let slot = unsafe { &mut *self.inner.get() };
51        if slot.is_some() {
52            return Err(value);
53        }
54        *slot = Some(value);
55
56        Ok(())
57    }
58
59    /// Put a value into this cell.
60    ///
61    /// Note that this function is infallible but requires `&mut self`. By
62    /// requiring `&mut self` we're guaranteed that no active borrows to this
63    /// cell can exist so we can always fill in the value. This may not always
64    /// be usable, however, as `&mut self` may not be possible to borrow.
65    ///
66    /// # Return value
67    ///
68    /// This function returns the previous value, if any.
69    #[allow(unused)]
70    pub fn replace(&mut self, value: T) -> Option<T> {
71        unsafe { &mut *self.inner.get() }.replace(value)
72    }
73
74    /// Test whether this cell has been previously filled.
75    #[allow(unused)]
76    pub fn filled(&self) -> bool {
77        self.borrow().is_some()
78    }
79
80    /// Borrows the contents of this lazy cell for the duration of the cell
81    /// itself.
82    ///
83    /// This function will return `Some` if the cell has been previously
84    /// initialized, and `None` if it has not yet been initialized.
85    #[allow(unused)]
86    pub fn borrow(&self) -> Option<&T> {
87        unsafe { &*self.inner.get() }.as_ref()
88    }
89
90    /// Borrows the contents of this lazy cell mutably for the duration of the cell
91    /// itself.
92    ///
93    /// This function will return `Some` if the cell has been previously
94    /// initialized, and `None` if it has not yet been initialized.
95    #[allow(unused)]
96    pub fn borrow_mut(&mut self) -> Option<&mut T> {
97        unsafe { &mut *self.inner.get() }.as_mut()
98    }
99
100    /// Borrows the contents of this lazy cell for the duration of the cell
101    /// itself.
102    ///
103    /// If the cell has not yet been filled, the cell is first filled using the
104    /// function provided.
105    ///
106    /// # Panics
107    ///
108    /// Panics if the cell becomes filled as a side effect of `f`.
109    #[allow(unused)]
110    pub fn borrow_with<F: FnOnce() -> T>(&self, f: F) -> &T {
111        if let Some(value) = self.borrow() {
112            return value;
113        }
114        let value = f();
115        if self.fill(value).is_err() {
116            panic!("borrow_with: cell was filled by closure")
117        }
118        self.borrow().unwrap()
119    }
120
121    /// Borrows the contents of this `LazyCell` mutably for the duration of the
122    /// cell itself.
123    ///
124    /// If the cell has not yet been filled, the cell is first filled using the
125    /// function provided.
126    ///
127    /// # Panics
128    ///
129    /// Panics if the cell becomes filled as a side effect of `f`.
130    #[allow(unused)]
131    pub fn borrow_mut_with<F: FnOnce() -> T>(&mut self, f: F) -> &mut T {
132        if !self.filled() {
133            let value = f();
134            if self.fill(value).is_err() {
135                panic!("borrow_mut_with: cell was filled by closure")
136            }
137        }
138
139        self.borrow_mut().unwrap()
140    }
141
142    /// Same as `borrow_with`, but allows the initializing function to fail.
143    ///
144    /// # Panics
145    ///
146    /// Panics if the cell becomes filled as a side effect of `f`.
147    #[allow(unused)]
148    pub fn try_borrow_with<E, F>(&self, f: F) -> Result<&T, E>
149    where
150        F: FnOnce() -> Result<T, E>,
151    {
152        if let Some(value) = self.borrow() {
153            return Ok(value);
154        }
155        let value = f()?;
156        if self.fill(value).is_err() {
157            panic!("try_borrow_with: cell was filled by closure")
158        }
159        Ok(self.borrow().unwrap())
160    }
161
162    /// Same as `borrow_mut_with`, but allows the initializing function to fail.
163    ///
164    /// # Panics
165    ///
166    /// Panics if the cell becomes filled as a side effect of `f`.
167    #[allow(unused)]
168    pub fn try_borrow_mut_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
169    where
170        F: FnOnce() -> Result<T, E>,
171    {
172        if self.filled() {
173            return Ok(self.borrow_mut().unwrap());
174        }
175        let value = f()?;
176        if self.fill(value).is_err() {
177            panic!("try_borrow_mut_with: cell was filled by closure")
178        }
179        Ok(self.borrow_mut().unwrap())
180    }
181
182    /// Consumes this `LazyCell`, returning the underlying value.
183    #[allow(unused)]
184    pub fn into_inner(self) -> Option<T> {
185        // Rust 1.25 changed UnsafeCell::into_inner() from unsafe to safe
186        // function. This unsafe can be removed when supporting Rust older than
187        // 1.25 is not needed.
188        #[allow(unused_unsafe)]
189        unsafe {
190            self.inner.into_inner()
191        }
192    }
193}
194
195impl<T: Copy> LazyCell<T> {
196    /// Returns a copy of the contents of the lazy cell.
197    ///
198    /// This function will return `Some` if the cell has been previously initialized,
199    /// and `None` if it has not yet been initialized.
200    #[allow(unused)]
201    pub fn get(&self) -> Option<T> {
202        unsafe { *self.inner.get() }
203    }
204}
205
206// Tracks the AtomicLazyCell inner state
207const NONE: usize = 0;
208const LOCK: usize = 1;
209const SOME: usize = 2;
210
211/// A lazily filled and thread-safe `Cell`, with frozen contents.
212#[derive(Debug, Default)]
213pub struct AtomicLazyCell<T> {
214    inner: UnsafeCell<Option<T>>,
215    state: AtomicUsize,
216}
217
218impl<T> AtomicLazyCell<T> {
219    /// Creates a new, empty, `AtomicLazyCell`.
220    pub fn new() -> AtomicLazyCell<T> {
221        Self {
222            inner: UnsafeCell::new(None),
223            state: AtomicUsize::new(NONE),
224        }
225    }
226
227    /// Put a value into this cell.
228    ///
229    /// This function will return `Err(value)` is the cell is already full.
230    pub fn fill(&self, t: T) -> Result<(), T> {
231        #[allow(deprecated)]
232        if NONE != self.state.compare_and_swap(NONE, LOCK, Ordering::Acquire) {
233            return Err(t);
234        }
235
236        unsafe { *self.inner.get() = Some(t) };
237
238        #[allow(deprecated)]
239        if LOCK != self.state.compare_and_swap(LOCK, SOME, Ordering::Release) {
240            panic!("unable to release lock");
241        }
242
243        Ok(())
244    }
245
246    /// Put a value into this cell.
247    ///
248    /// Note that this function is infallible but requires `&mut self`. By
249    /// requiring `&mut self` we're guaranteed that no active borrows to this
250    /// cell can exist so we can always fill in the value. This may not always
251    /// be usable, however, as `&mut self` may not be possible to borrow.
252    ///
253    /// # Return value
254    ///
255    /// This function returns the previous value, if any.
256    #[allow(unused)]
257    pub fn replace(&mut self, value: T) -> Option<T> {
258        match mem::replace(self.state.get_mut(), SOME) {
259            NONE | SOME => {}
260            _ => panic!("cell in inconsistent state"),
261        }
262        unsafe { &mut *self.inner.get() }.replace(value)
263    }
264
265    /// Test whether this cell has been previously filled.
266    #[allow(unused)]
267    pub fn filled(&self) -> bool {
268        self.state.load(Ordering::Acquire) == SOME
269    }
270
271    /// Borrows the contents of this lazy cell for the duration of the cell
272    /// itself.
273    ///
274    /// This function will return `Some` if the cell has been previously
275    /// initialized, and `None` if it has not yet been initialized.
276    pub fn borrow(&self) -> Option<&T> {
277        match self.state.load(Ordering::Acquire) {
278            SOME => unsafe { &*self.inner.get() }.as_ref(),
279            _ => None,
280        }
281    }
282
283    /// Consumes this `LazyCell`, returning the underlying value.
284    #[allow(unused)]
285    pub fn into_inner(self) -> Option<T> {
286        // Rust 1.25 changed UnsafeCell::into_inner() from unsafe to safe
287        // function. This unsafe can be removed when supporting Rust older than
288        // 1.25 is not needed.
289        #[allow(unused_unsafe)]
290        unsafe {
291            self.inner.into_inner()
292        }
293    }
294}
295
296impl<T: Copy> AtomicLazyCell<T> {
297    /// Returns a copy of the contents of the lazy cell.
298    ///
299    /// This function will return `Some` if the cell has been previously initialized,
300    /// and `None` if it has not yet been initialized.
301    #[allow(unused)]
302    pub fn get(&self) -> Option<T> {
303        match self.state.load(Ordering::Acquire) {
304            SOME => unsafe { *self.inner.get() },
305            _ => None,
306        }
307    }
308}
309
310unsafe impl<T: Sync + Send> Sync for AtomicLazyCell<T> {}
311
312unsafe impl<T: Send> Send for AtomicLazyCell<T> {}
313
314#[cfg(test)]
315mod tests {
316    use super::{AtomicLazyCell, LazyCell};
317
318    #[test]
319    fn test_borrow_from_empty() {
320        let lazycell: LazyCell<usize> = LazyCell::new();
321
322        let value = lazycell.borrow();
323        assert_eq!(value, None);
324
325        let value = lazycell.get();
326        assert_eq!(value, None);
327    }
328
329    #[test]
330    fn test_fill_and_borrow() {
331        let lazycell = LazyCell::new();
332
333        assert!(!lazycell.filled());
334        lazycell.fill(1).unwrap();
335        assert!(lazycell.filled());
336
337        let value = lazycell.borrow();
338        assert_eq!(value, Some(&1));
339
340        let value = lazycell.get();
341        assert_eq!(value, Some(1));
342    }
343
344    #[test]
345    fn test_borrow_mut() {
346        let mut lazycell = LazyCell::new();
347        assert!(lazycell.borrow_mut().is_none());
348
349        lazycell.fill(1).unwrap();
350        assert_eq!(lazycell.borrow_mut(), Some(&mut 1));
351
352        *lazycell.borrow_mut().unwrap() = 2;
353        assert_eq!(lazycell.borrow_mut(), Some(&mut 2));
354
355        // official way to reset the cell
356        lazycell = LazyCell::new();
357        assert!(lazycell.borrow_mut().is_none());
358    }
359
360    #[test]
361    fn test_already_filled_error() {
362        let lazycell = LazyCell::new();
363
364        lazycell.fill(1).unwrap();
365        assert_eq!(lazycell.fill(1), Err(1));
366    }
367
368    #[test]
369    fn test_borrow_with() {
370        let lazycell = LazyCell::new();
371
372        let value = lazycell.borrow_with(|| 1);
373        assert_eq!(&1, value);
374    }
375
376    #[test]
377    fn test_borrow_with_already_filled() {
378        let lazycell = LazyCell::new();
379        lazycell.fill(1).unwrap();
380
381        let value = lazycell.borrow_with(|| 1);
382        assert_eq!(&1, value);
383    }
384
385    #[test]
386    fn test_borrow_with_not_called_when_filled() {
387        let lazycell = LazyCell::new();
388
389        lazycell.fill(1).unwrap();
390
391        let value = lazycell.borrow_with(|| 2);
392        assert_eq!(&1, value);
393    }
394
395    #[test]
396    #[should_panic]
397    fn test_borrow_with_sound_with_reentrancy() {
398        // Kudos to dbaupp for discovering this issue
399        // https://www.reddit.com/r/rust/comments/5vs9rt/lazycell_a_rust_library_providing_a_lazilyfilled/de527xm/
400        let lazycell: LazyCell<Box<i32>> = LazyCell::new();
401
402        let mut reference: Option<&i32> = None;
403
404        lazycell.borrow_with(|| {
405            let _ = lazycell.fill(Box::new(1));
406            reference = lazycell.borrow().map(|r| &**r);
407            Box::new(2)
408        });
409    }
410
411    #[test]
412    fn test_borrow_mut_with() {
413        let mut lazycell = LazyCell::new();
414
415        {
416            let value = lazycell.borrow_mut_with(|| 1);
417            assert_eq!(&mut 1, value);
418            *value = 2;
419        }
420        assert_eq!(&2, lazycell.borrow().unwrap());
421    }
422
423    #[test]
424    fn test_borrow_mut_with_already_filled() {
425        let mut lazycell = LazyCell::new();
426        lazycell.fill(1).unwrap();
427
428        let value = lazycell.borrow_mut_with(|| 1);
429        assert_eq!(&1, value);
430    }
431
432    #[test]
433    fn test_borrow_mut_with_not_called_when_filled() {
434        let mut lazycell = LazyCell::new();
435
436        lazycell.fill(1).unwrap();
437
438        let value = lazycell.borrow_mut_with(|| 2);
439        assert_eq!(&1, value);
440    }
441
442    #[test]
443    fn test_try_borrow_with_ok() {
444        let lazycell = LazyCell::new();
445        let result = lazycell.try_borrow_with::<(), _>(|| Ok(1));
446        assert_eq!(result, Ok(&1));
447    }
448
449    #[test]
450    fn test_try_borrow_with_err() {
451        let lazycell = LazyCell::<()>::new();
452        let result = lazycell.try_borrow_with(|| Err(1));
453        assert_eq!(result, Err(1));
454    }
455
456    #[test]
457    fn test_try_borrow_with_already_filled() {
458        let lazycell = LazyCell::new();
459        lazycell.fill(1).unwrap();
460        let result = lazycell.try_borrow_with::<(), _>(|| unreachable!());
461        assert_eq!(result, Ok(&1));
462    }
463
464    #[test]
465    #[should_panic]
466    fn test_try_borrow_with_sound_with_reentrancy() {
467        let lazycell: LazyCell<Box<i32>> = LazyCell::new();
468
469        let mut reference: Option<&i32> = None;
470
471        let _ = lazycell.try_borrow_with::<(), _>(|| {
472            let _ = lazycell.fill(Box::new(1));
473            reference = lazycell.borrow().map(|r| &**r);
474            Ok(Box::new(2))
475        });
476    }
477
478    #[test]
479    fn test_try_borrow_mut_with_ok() {
480        let mut lazycell = LazyCell::new();
481        {
482            let result = lazycell.try_borrow_mut_with::<(), _>(|| Ok(1));
483            assert_eq!(result, Ok(&mut 1));
484            *result.unwrap() = 2;
485        }
486        assert_eq!(&mut 2, lazycell.borrow().unwrap());
487    }
488
489    #[test]
490    fn test_try_borrow_mut_with_err() {
491        let mut lazycell = LazyCell::<()>::new();
492        let result = lazycell.try_borrow_mut_with(|| Err(1));
493        assert_eq!(result, Err(1));
494    }
495
496    #[test]
497    fn test_try_borrow_mut_with_already_filled() {
498        let mut lazycell = LazyCell::new();
499        lazycell.fill(1).unwrap();
500        let result = lazycell.try_borrow_mut_with::<(), _>(|| unreachable!());
501        assert_eq!(result, Ok(&mut 1));
502    }
503
504    #[test]
505    fn test_into_inner() {
506        let lazycell = LazyCell::new();
507
508        lazycell.fill(1).unwrap();
509        let value = lazycell.into_inner();
510        assert_eq!(value, Some(1));
511    }
512
513    #[test]
514    fn test_atomic_borrow_from_empty() {
515        let lazycell: AtomicLazyCell<usize> = AtomicLazyCell::new();
516
517        let value = lazycell.borrow();
518        assert_eq!(value, None);
519
520        let value = lazycell.get();
521        assert_eq!(value, None);
522    }
523
524    #[test]
525    fn test_atomic_fill_and_borrow() {
526        let lazycell = AtomicLazyCell::new();
527
528        assert!(!lazycell.filled());
529        lazycell.fill(1).unwrap();
530        assert!(lazycell.filled());
531
532        let value = lazycell.borrow();
533        assert_eq!(value, Some(&1));
534
535        let value = lazycell.get();
536        assert_eq!(value, Some(1));
537    }
538
539    #[test]
540    fn test_atomic_already_filled_panic() {
541        let lazycell = AtomicLazyCell::new();
542
543        lazycell.fill(1).unwrap();
544        assert_eq!(1, lazycell.fill(1).unwrap_err());
545    }
546
547    #[test]
548    fn test_atomic_into_inner() {
549        let lazycell = AtomicLazyCell::new();
550
551        lazycell.fill(1).unwrap();
552        let value = lazycell.into_inner();
553        assert_eq!(value, Some(1));
554    }
555
556    #[test]
557    fn normal_replace() {
558        let mut cell = LazyCell::new();
559        assert_eq!(cell.fill(1), Ok(()));
560        assert_eq!(cell.replace(2), Some(1));
561        assert_eq!(cell.replace(3), Some(2));
562        assert_eq!(cell.borrow(), Some(&3));
563
564        let mut cell = LazyCell::new();
565        assert_eq!(cell.replace(2), None);
566    }
567
568    #[test]
569    fn atomic_replace() {
570        let mut cell = AtomicLazyCell::new();
571        assert_eq!(cell.fill(1), Ok(()));
572        assert_eq!(cell.replace(2), Some(1));
573        assert_eq!(cell.replace(3), Some(2));
574        assert_eq!(cell.borrow(), Some(&3));
575    }
576}