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}