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}