stackarc 0.1.0

A little crate implementing an unallocated Arc.
Documentation
//! A little crate providing a type for creating a [`Arc`][`std::sync::Arc`] on the stack.

use std::mem::MaybeUninit;
use std::sync::{
    atomic::{AtomicUsize, Ordering::*},
    Arc,
};

use std::ptr::NonNull;

/// A type with the same layout as the allocated value of [`Arc`] that it's initialized
/// once you convert a mutable reference to it.
/// 
/// This type consist of the strong,weak count of the [`Arc`] and a [`MaybeUninit`] holding the
/// data.The strong it's set to zero until you cast to the first [`Arc`] with [`to_arc`] or
/// [`to_arc_with`] thus upgrading the count.
/// 
/// # Examples
/// 
/// The first use-case of this type is lazy-init an mutable static across threads and drop it when
/// all ceases to use it.
/// 
/// ```
/// use stackarc::ArcInner;
/// use std::thread::{sleep, spawn};
/// use std::time::Duration;
/// 
/// static mut A: ArcInner<String> = ArcInner::uninit();
/// 
/// fn main() {
///     let x = spawn(|| unsafe { 
///         let _a = A.to_arc_with(|| format!("foobar")); 
///         sleep(Duration::from_secs(2)); 
///     });
/// 
///     let y = spawn(|| unsafe {
///         let _a = A.to_arc_with(|| format!("barfoo"));  
///         sleep(Duration::from_secs(2)); 
///     });
/// 
///     // wait one second to wait for the threads to initialize the value
///     // which in turn wait another to maintain their reference alive
///     // and don't drop the count until create another
///     sleep(Duration::from_secs(1)); 
///     let z = unsafe { A.to_arc_with(|| format!("baz")) };
///  
///     assert_ne!(*z, "baz");
/// 
///     drop(z);
///     x.join().unwrap();
///     y.join().unwrap();
///     let z = unsafe { A.to_arc_with(|| format!("foo")) };
///     
///     assert_eq!(*z, "foo");
/// }
/// ```
/// 
/// [`to_arc`]: #method.to_arc
/// [`to_arc_with`]: #method.to_arc_with
#[repr(C)]
pub struct ArcInner<T> {
    strong: AtomicUsize,
    weak: AtomicUsize,
    data: MaybeUninit<T>
}

impl<T> ArcInner<T> {
    /// Declares a new `ArcInner` in an uninit state,use [`to_arc_with`] or [`to_arc`] to initialize it.
    /// 
    /// [`to_arc`]: #method.to_arc
    /// [`to_arc_with`]: #method.to_arc_with
    #[inline]
    pub const fn uninit() -> Self {
            Self {
                strong: AtomicUsize::new(0),
                weak: AtomicUsize::new(1),
                data: MaybeUninit::uninit(),
            }
    }

    /// Returns an [`Arc`] after upgrading the counts and initialize the value calling `default` if
    /// the strong is zero.
    /// 
    /// A good example is at the [type docs](#top).
    // maybe rename to `as_arc_with` as we are converting between references but with a little overhead when it's uninitialized
    #[inline]
    pub fn to_arc_with<F: FnOnce() -> T>(&mut self, default: F) -> Arc<T> {
        unsafe {
            if self.strong.fetch_add(1, SeqCst) == 0 {
                // we know that there's no other references that can be dropped
                // once we reach here so a relaxed order it's fine
                self.weak.store(2, Relaxed);
                self.as_mut_ptr().write(default());
            } else {
                self.weak.fetch_add(1, SeqCst);
            }

            (&NonNull::from(self) as *const NonNull<Self> as *const Arc<T>).read()
        }
    }

    /// Returns an [`Arc`] after upgrading the counts and initialize the value with `x` if the
    /// strong is zero.
    /// 
    /// A good example is at the [type docs](#top).
    #[inline]
    pub fn to_arc(&mut self, x: T) -> Arc<T> {
        self.to_arc_with(|| x)
    }

    /// Returns a raw mutable pointer to the inner data.
    #[inline]
    pub fn as_mut_ptr(&mut self) -> *mut T {
        self.data.as_mut_ptr()
    }

    /// Extracts a raw pointer to the inner data.
    #[inline]
    pub fn as_ptr(&self) -> *const T {
        self.data.as_ptr()
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn a() {
        let mut a = ArcInner::uninit();

        {
            let b = a.to_arc(1);

            assert_eq!(1, *b);
        }

        let c = a.to_arc(0);
        drop(a);

        assert_eq!(0, *c);
    }
}