Skip to main content

once_arc/
init_once_arc.rs

1use std::fmt;
2use std::sync::atomic::Ordering;
3use std::sync::{Arc, Mutex, PoisonError};
4
5use crate::OnceArc;
6
7/// A thread-safe container that provides synchronized one-time initialization
8/// of an `Arc<T>` via a closure.
9///
10/// This builds on [`OnceArc`] by adding `init` and `try_init` methods.
11/// When multiple threads call `init` concurrently on an empty cell, exactly
12/// one thread will execute the closure — the others block on an internal
13/// mutex and then observe the already-initialized value. If the closure
14/// completes without panicking, its return value becomes the stored `Arc<T>`.
15/// If it panics, the mutex is poisoned and subsequent calls return an error.
16///
17/// Once set, all reads go through the fast path: a single atomic load,
18/// identical to [`OnceArc::get`] — no mutex, no CAS.
19///
20/// # Examples
21///
22/// ```
23/// use std::sync::Arc;
24/// use std::sync::atomic::Ordering;
25/// use once_arc::InitOnceArc;
26///
27/// let cell: InitOnceArc<String> = InitOnceArc::new();
28///
29/// // First call runs the initializer
30/// assert_eq!(cell.init(|| "hello".to_string().into()).unwrap(), true);
31///
32/// // Subsequent calls return Ok(false) without running the closure
33/// assert_eq!(cell.init(|| "world".to_string().into()).unwrap(), false);
34/// assert_eq!(cell.get(Ordering::Acquire).unwrap(), "hello");
35/// ```
36pub struct InitOnceArc<T> {
37  inner: OnceArc<T>,
38  init_mutex: Mutex<()>,
39}
40
41// SAFETY: Same reasoning as OnceArc — T is behind an Arc
42// and only accessible via shared reference. The Mutex is Send + Sync.
43unsafe impl<T: Send + Sync> Send for InitOnceArc<T> {}
44unsafe impl<T: Send + Sync> Sync for InitOnceArc<T> {}
45
46impl<T> InitOnceArc<T> {
47  /// Creates a new empty `InitOnceArc`.
48  ///
49  /// # Examples
50  ///
51  /// ```
52  /// use once_arc::InitOnceArc;
53  ///
54  /// let cell: InitOnceArc<i32> = InitOnceArc::new();
55  /// ```
56  pub const fn new() -> Self {
57    Self {
58      inner: OnceArc::new(),
59      init_mutex: Mutex::new(()),
60    }
61  }
62
63  /// Attempts to store the value. Returns `Ok(())` on success,
64  /// `Err(Ok(value))` if a value was already stored, or `Err(Err(_))` if the
65  /// mutex is poisoned.
66  ///
67  /// If an init attempt is already ongoing, this `store` will wait for it,
68  /// before trying to store.
69  ///
70  /// # Examples
71  ///
72  /// ```
73  /// use std::sync::Arc;
74  /// use once_arc::InitOnceArc;
75  ///
76  /// let cell: InitOnceArc<i32> = InitOnceArc::new();
77  /// assert!(cell.store(Arc::new(42)).is_ok());
78  ///
79  /// let err = cell.store(Arc::new(99)).unwrap_err().unwrap();
80  /// assert_eq!(*err, 99);
81  /// ```
82  pub fn store(&self, value: Arc<T>) -> Result<(), Result<Arc<T>, PoisonError<()>>> {
83    let _guard = self.init_mutex.lock().map_err(|_| Err(PoisonError::new(())))?;
84    self.inner.store(value, Ordering::SeqCst).map_err(Ok)
85  }
86
87  /// Initializes the cell with the value produced by `f`, if not yet set.
88  /// Returns `Ok(true)` if the value was initialized, `Ok(false)` if it was
89  /// already set, or `Err` if the mutex is poisoned.
90  ///
91  /// Only one initializer will be run per [`InitOnceArc`] and if the initializer doesn't panic,
92  /// its return value is guaranteed to become the stored `Arc<T>`.
93  /// If it panics, the mutex is poisoned and subsequent calls will return an error.
94  ///
95  /// While the initializer is running all readers will see the cell as empty.
96  ///
97  /// # Examples
98  ///
99  /// ```
100  /// use std::sync::Arc;
101  /// use once_arc::InitOnceArc;
102  ///
103  /// let cell: InitOnceArc<i32> = InitOnceArc::new();
104  /// assert_eq!(cell.init(|| Arc::new(42)).unwrap(), true);
105  /// assert_eq!(cell.init(|| Arc::new(99)).unwrap(), false); // already initialized
106  /// ```
107  pub fn init(&self, f: impl FnOnce() -> Arc<T>) -> Result<bool, PoisonError<()>> {
108    // Slow path: acquire mutex, double-check, initialize
109    let _guard = self.init_mutex.lock().map_err(|_| PoisonError::new(()))?;
110    if self.inner.is_set(Ordering::SeqCst) {
111      return Ok(false);
112    }
113
114    let arc = f();
115    self
116      .inner
117      .store(arc, Ordering::SeqCst)
118      .unwrap_or_else(|_| unreachable!("store failed while holding init mutex"));
119    Ok(true)
120  }
121
122  /// Initializes the cell with the value produced by `f`, if not yet set.
123  /// If `f` returns `Err`, the cell remains empty and the error is propagated.
124  /// Returns `Ok(true)` if initialized, `Ok(false)` if already set,
125  /// `Err(Ok(e))` if `f` failed, or `Err(Err(_))` if the mutex is poisoned.
126  ///
127  /// Only one initializer will be run per [`InitOnceArc`] at a time and if the initializer doesn't panic or return an error,
128  /// its return value is guaranteed to become the stored `Arc<T>`.
129  /// If it panics, the mutex is poisoned and subsequent calls will return an error.
130  ///
131  /// While the initializer is running all readers will see the cell as empty.
132  ///
133  /// # Examples
134  ///
135  /// ```
136  /// use std::sync::Arc;
137  /// use once_arc::InitOnceArc;
138  ///
139  /// let cell: InitOnceArc<i32> = InitOnceArc::new();
140  ///
141  /// let err = cell.try_init(|| Err("oops")).unwrap_err().unwrap();
142  /// assert_eq!(err, "oops");
143  ///
144  /// assert_eq!(cell.try_init(|| Ok::<_, &str>(Arc::new(42))).unwrap(), true);
145  /// assert_eq!(cell.try_init(|| Ok::<_, &str>(Arc::new(99))).unwrap(), false);
146  /// ```
147  pub fn try_init<E>(&self, f: impl FnOnce() -> Result<Arc<T>, E>) -> Result<bool, Result<E, PoisonError<()>>> {
148    // Slow path
149    let _guard = self.init_mutex.lock().map_err(|_| Err(PoisonError::new(())))?;
150    if self.inner.is_set(Ordering::SeqCst) {
151      return Ok(false);
152    }
153
154    let value = match f() {
155      Ok(v) => v,
156      Err(e) => return Err(Ok(e)),
157    };
158    self
159      .inner
160      .store(value, Ordering::SeqCst)
161      .unwrap_or_else(|_| unreachable!("store failed while holding init mutex"));
162    Ok(true)
163  }
164
165  /// Returns a reference to the stored value, or `None` if not yet initialized.
166  ///
167  /// This is the fast path — a single atomic load, no mutex.
168  ///
169  /// # Examples
170  ///
171  /// ```
172  /// use std::sync::Arc;
173  /// use std::sync::atomic::Ordering;
174  /// use once_arc::InitOnceArc;
175  ///
176  /// let cell: InitOnceArc<i32> = InitOnceArc::new();
177  /// assert!(cell.get(Ordering::Acquire).is_none());
178  ///
179  /// cell.init(|| Arc::new(42)).unwrap();
180  /// assert_eq!(cell.get(Ordering::Acquire), Some(&42));
181  /// ```
182  pub fn get(&self, ordering: Ordering) -> Option<&T> {
183    self.inner.get(ordering)
184  }
185
186  /// Loads the stored value as a cloned `Arc<T>`, or `None` if not yet
187  /// initialized.
188  ///
189  /// # Examples
190  ///
191  /// ```
192  /// use std::sync::Arc;
193  /// use std::sync::atomic::Ordering;
194  /// use once_arc::InitOnceArc;
195  ///
196  /// let cell: InitOnceArc<i32> = InitOnceArc::new();
197  /// cell.init(|| Arc::new(42)).unwrap();
198  /// let arc = cell.load(Ordering::Acquire).unwrap();
199  /// assert_eq!(*arc, 42);
200  /// ```
201  pub fn load(&self, ordering: Ordering) -> Option<Arc<T>> {
202    self.inner.load(ordering)
203  }
204
205  /// Returns `true` if the value has been initialized.
206  ///
207  /// # Examples
208  ///
209  /// ```
210  /// use std::sync::Arc;
211  /// use std::sync::atomic::Ordering;
212  /// use once_arc::InitOnceArc;
213  ///
214  /// let cell: InitOnceArc<i32> = InitOnceArc::new();
215  /// assert!(!cell.is_set(Ordering::Relaxed));
216  /// cell.init(|| Arc::new(1)).unwrap();
217  /// assert!(cell.is_set(Ordering::Relaxed));
218  /// ```
219  pub fn is_set(&self, ordering: Ordering) -> bool {
220    self.inner.is_set(ordering)
221  }
222
223  /// Consumes `self` and returns the stored `Arc<T>`, if any.
224  ///
225  /// # Examples
226  ///
227  /// ```
228  /// use std::sync::Arc;
229  /// use once_arc::InitOnceArc;
230  ///
231  /// let cell: InitOnceArc<i32> = InitOnceArc::new();
232  /// cell.init(|| Arc::new(42)).unwrap();
233  /// let arc = cell.into_inner().unwrap();
234  /// assert_eq!(*arc, 42);
235  /// ```
236  pub fn into_inner(self) -> Option<Arc<T>> {
237    self.inner.into_inner()
238  }
239
240  /// Returns a mutable reference to the stored value, or `None` if not yet set.
241  ///
242  /// Since this requires `&mut self`, no atomic operations or mutex are needed.
243  ///
244  /// # Examples
245  ///
246  /// ```
247  /// use std::sync::Arc;
248  /// use std::sync::atomic::Ordering;
249  /// use once_arc::InitOnceArc;
250  ///
251  /// let mut cell: InitOnceArc<i32> = InitOnceArc::new();
252  /// cell.init(|| Arc::new(10)).unwrap();
253  /// *cell.get_mut().unwrap() = 20;
254  /// assert_eq!(cell.get(Ordering::Acquire), Some(&20));
255  /// ```
256  pub fn get_mut(&mut self) -> Option<&mut T> {
257    self.inner.get_mut()
258  }
259}
260
261impl<T> Default for InitOnceArc<T> {
262  fn default() -> Self {
263    Self::new()
264  }
265}
266
267impl<T: fmt::Debug> fmt::Debug for InitOnceArc<T> {
268  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269    f.debug_struct("InitOnceArc")
270      .field("value", &self.inner.get(Ordering::SeqCst))
271      .finish()
272  }
273}
274
275#[cfg(test)]
276mod tests {
277  use super::*;
278  use std::sync::Arc;
279  use std::sync::atomic::Ordering;
280
281  #[test]
282  fn once_arc_init() {
283    let cell: InitOnceArc<i32> = InitOnceArc::new();
284    assert!(cell.init(|| Arc::new(42)).unwrap());
285    assert!(!cell.init(|| Arc::new(99)).unwrap());
286    assert_eq!(cell.get(Ordering::Acquire), Some(&42));
287  }
288
289  #[test]
290  fn once_arc_get_empty() {
291    let cell: InitOnceArc<i32> = InitOnceArc::new();
292    assert!(cell.get(Ordering::Acquire).is_none());
293  }
294
295  #[test]
296  fn once_arc_try_init_err_then_ok() {
297    let cell: InitOnceArc<i32> = InitOnceArc::new();
298    let err = cell.try_init(|| Err("fail")).unwrap_err().unwrap();
299    assert_eq!(err, "fail");
300    assert!(cell.get(Ordering::Acquire).is_none());
301
302    assert!(cell.try_init(|| Ok::<_, &str>(Arc::new(42))).unwrap());
303    assert_eq!(cell.get(Ordering::Acquire), Some(&42));
304    assert!(!cell.try_init(|| Ok::<_, &str>(Arc::new(99))).unwrap());
305  }
306
307  #[test]
308  fn once_arc_load_and_is_set() {
309    let cell: InitOnceArc<i32> = InitOnceArc::new();
310    assert!(!cell.is_set(Ordering::Relaxed));
311    assert!(cell.load(Ordering::Acquire).is_none());
312
313    cell.init(|| Arc::new(7)).unwrap();
314    assert!(cell.is_set(Ordering::Relaxed));
315    assert_eq!(*cell.load(Ordering::Acquire).unwrap(), 7);
316  }
317
318  #[test]
319  fn once_arc_into_inner() {
320    let cell: InitOnceArc<i32> = InitOnceArc::new();
321    cell.init(|| Arc::new(42)).unwrap();
322    let arc = cell.into_inner().unwrap();
323    assert_eq!(*arc, 42);
324  }
325
326  #[test]
327  fn once_arc_into_inner_empty() {
328    let cell: InitOnceArc<i32> = InitOnceArc::new();
329    assert!(cell.into_inner().is_none());
330  }
331
332  #[test]
333  fn once_arc_concurrent_init() {
334    use std::sync::Barrier;
335    use std::sync::atomic::AtomicUsize;
336    use std::thread;
337
338    let cell = Arc::new(InitOnceArc::<i32>::new());
339    let init_count = Arc::new(AtomicUsize::new(0));
340    let barrier = Arc::new(Barrier::new(10));
341    let mut handles = Vec::new();
342
343    for _ in 0..10 {
344      let cell = cell.clone();
345      let init_count = init_count.clone();
346      let barrier = barrier.clone();
347      handles.push(thread::spawn(move || {
348        barrier.wait();
349        cell
350          .init(|| {
351            init_count.fetch_add(1, Ordering::Relaxed);
352            Arc::new(42)
353          })
354          .unwrap()
355      }));
356    }
357
358    let results: Vec<bool> = handles.into_iter().map(|h| h.join().unwrap()).collect();
359    assert_eq!(results.iter().filter(|&&b| b).count(), 1);
360    assert_eq!(cell.get(Ordering::Acquire), Some(&42));
361    // Exactly one thread ran the initializer
362    assert_eq!(init_count.load(Ordering::Relaxed), 1);
363  }
364
365  #[test]
366  fn once_arc_debug_fmt() {
367    let cell: InitOnceArc<i32> = InitOnceArc::new();
368    cell.init(|| Arc::new(42)).unwrap();
369    let dbg = format!("{:?}", cell);
370    assert!(dbg.contains("42"));
371  }
372
373  /// Poisons the mutex by panicking inside `init` on another thread.
374  fn poisoned_cell() -> Arc<InitOnceArc<i32>> {
375    use std::thread;
376    let cell = Arc::new(InitOnceArc::<i32>::new());
377    let c = cell.clone();
378    let _ = thread::spawn(move || {
379      let _ = c.init(|| panic!("deliberate poison"));
380    })
381    .join();
382    cell
383  }
384
385  #[test]
386  fn store_returns_poison_error() {
387    let cell = poisoned_cell();
388    let err = cell.store(Arc::new(1)).unwrap_err();
389    assert!(err.is_err()); // Err(PoisonError)
390  }
391
392  #[test]
393  fn init_returns_poison_error() {
394    let cell = poisoned_cell();
395    assert!(cell.init(|| Arc::new(1)).is_err());
396  }
397
398  #[test]
399  fn try_init_returns_poison_error() {
400    let cell = poisoned_cell();
401    let err = cell.try_init(|| Ok::<_, &str>(Arc::new(1))).unwrap_err();
402    assert!(err.is_err()); // Err(PoisonError)
403  }
404}