stackarc/
lib.rs

1//! A little crate providing a type for creating a [`Arc`][`std::sync::Arc`] on the stack.
2
3use std::mem::MaybeUninit;
4use std::sync::{
5    atomic::{AtomicUsize, Ordering::*},
6    Arc,
7};
8
9use std::ptr::NonNull;
10
11/// A type with the same layout as the allocated value of [`Arc`] that it's initialized
12/// once you convert a mutable reference to it.
13/// 
14/// This type consist of the strong,weak count of the [`Arc`] and a [`MaybeUninit`] holding the
15/// data.The strong it's set to zero until you cast to the first [`Arc`] with [`to_arc`] or
16/// [`to_arc_with`] thus upgrading the count.
17/// 
18/// # Examples
19/// 
20/// The first use-case of this type is lazy-init an mutable static across threads and drop it when
21/// all ceases to use it.
22/// 
23/// ```
24/// use stackarc::ArcInner;
25/// use std::thread::{sleep, spawn};
26/// use std::time::Duration;
27/// 
28/// static mut A: ArcInner<String> = ArcInner::uninit();
29/// 
30/// fn main() {
31///     let x = spawn(|| unsafe { 
32///         let _a = A.to_arc_with(|| format!("foobar")); 
33///         sleep(Duration::from_secs(2)); 
34///     });
35/// 
36///     let y = spawn(|| unsafe {
37///         let _a = A.to_arc_with(|| format!("barfoo"));  
38///         sleep(Duration::from_secs(2)); 
39///     });
40/// 
41///     // wait one second to wait for the threads to initialize the value
42///     // which in turn wait another to maintain their reference alive
43///     // and don't drop the count until create another
44///     sleep(Duration::from_secs(1)); 
45///     let z = unsafe { A.to_arc_with(|| format!("baz")) };
46///  
47///     assert_ne!(*z, "baz");
48/// 
49///     drop(z);
50///     x.join().unwrap();
51///     y.join().unwrap();
52///     let z = unsafe { A.to_arc_with(|| format!("foo")) };
53///     
54///     assert_eq!(*z, "foo");
55/// }
56/// ```
57/// 
58/// [`to_arc`]: #method.to_arc
59/// [`to_arc_with`]: #method.to_arc_with
60#[repr(C)]
61pub struct ArcInner<T> {
62    strong: AtomicUsize,
63    weak: AtomicUsize,
64    data: MaybeUninit<T>
65}
66
67impl<T> ArcInner<T> {
68    /// Declares a new `ArcInner` in an uninit state,use [`to_arc_with`] or [`to_arc`] to initialize it.
69    /// 
70    /// [`to_arc`]: #method.to_arc
71    /// [`to_arc_with`]: #method.to_arc_with
72    #[inline]
73    pub const fn uninit() -> Self {
74            Self {
75                strong: AtomicUsize::new(0),
76                weak: AtomicUsize::new(1),
77                data: MaybeUninit::uninit(),
78            }
79    }
80
81    /// Returns an [`Arc`] after upgrading the counts and initialize the value calling `default` if
82    /// the strong is zero.
83    /// 
84    /// A good example is at the [type docs](#top).
85    // maybe rename to `as_arc_with` as we are converting between references but with a little overhead when it's uninitialized
86    #[inline]
87    pub fn to_arc_with<F: FnOnce() -> T>(&mut self, default: F) -> Arc<T> {
88        unsafe {
89            if self.strong.fetch_add(1, SeqCst) == 0 {
90                // we know that there's no other references that can be dropped
91                // once we reach here so a relaxed order it's fine
92                self.weak.store(2, Relaxed);
93                self.as_mut_ptr().write(default());
94            } else {
95                self.weak.fetch_add(1, SeqCst);
96            }
97
98            (&NonNull::from(self) as *const NonNull<Self> as *const Arc<T>).read()
99        }
100    }
101
102    /// Returns an [`Arc`] after upgrading the counts and initialize the value with `x` if the
103    /// strong is zero.
104    /// 
105    /// A good example is at the [type docs](#top).
106    #[inline]
107    pub fn to_arc(&mut self, x: T) -> Arc<T> {
108        self.to_arc_with(|| x)
109    }
110
111    /// Returns a raw mutable pointer to the inner data.
112    #[inline]
113    pub fn as_mut_ptr(&mut self) -> *mut T {
114        self.data.as_mut_ptr()
115    }
116
117    /// Extracts a raw pointer to the inner data.
118    #[inline]
119    pub fn as_ptr(&self) -> *const T {
120        self.data.as_ptr()
121    }
122}
123
124#[cfg(test)]
125mod test {
126    use super::*;
127
128    #[test]
129    fn a() {
130        let mut a = ArcInner::uninit();
131
132        {
133            let b = a.to_arc(1);
134
135            assert_eq!(1, *b);
136        }
137
138        let c = a.to_arc(0);
139        drop(a);
140
141        assert_eq!(0, *c);
142    }
143}