Skip to main content

once_arc/
once_arc.rs

1use std::fmt;
2use std::ptr;
3use std::sync::Arc;
4use std::sync::atomic::{AtomicPtr, Ordering};
5
6/// A thread-safe container that can be atomically initialized once with an `Arc<T>`.
7///
8/// This is conceptually similar to `Atomic<Option<Arc<T>>>`, but with a critical restriction:
9/// the value can only be set once. This restriction is what makes the implementation both
10/// sound and extremely fast — `load` is a single atomic read with no reference count
11/// manipulation.
12///
13/// # Examples
14///
15/// ```
16/// use std::sync::Arc;
17/// use std::sync::atomic::Ordering;
18/// use once_arc::OnceArc;
19///
20/// let slot: OnceArc<i32> = OnceArc::new();
21/// assert!(slot.get(Ordering::Acquire).is_none());
22///
23/// slot.store(Arc::new(42), Ordering::Release).unwrap();
24///
25/// // get() returns &T — just a single atomic load
26/// assert_eq!(slot.get(Ordering::Acquire), Some(&42));
27///
28/// // load() returns a cloned Arc
29/// let arc = slot.load(Ordering::Acquire).unwrap();
30/// assert_eq!(*arc, 42);
31///
32/// // Second store fails and returns the value back.
33/// let err = slot.store(Arc::new(99), Ordering::Release).unwrap_err();
34/// assert_eq!(*err, 99);
35/// ```
36pub struct OnceArc<T> {
37  ptr: AtomicPtr<T>,
38}
39
40// SAFETY: The inner `T` is behind an `Arc` and only accessible via shared reference.
41// `Send` and `Sync` require `T: Send + Sync` to match `Arc<T>`'s bounds.
42unsafe impl<T: Send + Sync> Send for OnceArc<T> {}
43unsafe impl<T: Send + Sync> Sync for OnceArc<T> {}
44
45impl<T> OnceArc<T> {
46  /// Creates a new empty `OnceArc`.
47  ///
48  /// # Examples
49  ///
50  /// ```
51  /// use once_arc::OnceArc;
52  ///
53  /// let slot: OnceArc<i32> = OnceArc::new();
54  /// ```
55  pub const fn new() -> Self {
56    Self {
57      ptr: AtomicPtr::new(ptr::null_mut()),
58    }
59  }
60
61  /// Attempts to store the value. Returns `Ok(())` if this is the first call,
62  /// or `Err(value)` if a value was already stored.
63  ///
64  /// `ordering` describes the required ordering for the
65  /// read-modify-write operation that takes place if the None-check succeeds.
66  /// Using [`Acquire`](std::sync::atomic::Ordering::Acquire) as ordering makes the store part
67  /// of this operation [`Relaxed`](std::sync::atomic::Ordering::Relaxed), and using [`Release`](std::sync::atomic::Ordering::Release) makes the load [`Relaxed`](std::sync::atomic::Ordering::Relaxed).
68  ///
69  /// # Examples
70  ///
71  /// ```
72  /// use std::sync::Arc;
73  /// use std::sync::atomic::Ordering;
74  /// use once_arc::OnceArc;
75  ///
76  /// let slot: OnceArc<i32> = OnceArc::new();
77  /// assert!(slot.store(Arc::new(42), Ordering::Release).is_ok());
78  ///
79  /// // Second store fails
80  /// let err = slot.store(Arc::new(99), Ordering::Release).unwrap_err();
81  /// assert_eq!(*err, 99);
82  /// ```
83  pub fn store(&self, value: Arc<T>, ordering: Ordering) -> Result<(), Arc<T>> {
84    let raw = Arc::into_raw(value) as *mut T;
85    match self
86      .ptr
87      .compare_exchange(ptr::null_mut(), raw, ordering, Ordering::Relaxed)
88    {
89      Ok(_) => Ok(()),
90      Err(_) => {
91        // SAFETY: We just created this raw pointer from Arc::into_raw above,
92        // and the CAS failed so nobody else owns it.
93        let value = unsafe { Arc::from_raw(raw) };
94        Err(value)
95      }
96    }
97  }
98
99  /// Returns a reference to the stored value, or `None` if not yet set.
100  ///
101  /// This is extremely fast: a single atomic load with no reference count
102  /// manipulation. The returned reference is valid for as long as `&self` is
103  /// valid, because the stored `Arc` is never removed until `self` is dropped.
104  ///
105  /// # Examples
106  ///
107  /// ```
108  /// use std::sync::Arc;
109  /// use std::sync::atomic::Ordering;
110  /// use once_arc::OnceArc;
111  ///
112  /// let slot: OnceArc<i32> = OnceArc::new();
113  /// assert_eq!(slot.get(Ordering::Acquire), None);
114  ///
115  /// slot.store(Arc::new(42), Ordering::Release).unwrap();
116  /// assert_eq!(slot.get(Ordering::Acquire), Some(&42));
117  /// ```
118  pub fn get(&self, ordering: Ordering) -> Option<&T> {
119    let ptr = self.ptr.load(ordering);
120    if ptr.is_null() {
121      None
122    } else {
123      // SAFETY: Once set, the pointer is never changed or freed until drop.
124      // The &T lifetime is tied to &self, and drop requires &mut self,
125      // so the data is guaranteed alive for the duration of the borrow.
126      Some(unsafe { &*ptr })
127    }
128  }
129
130  /// Loads the stored value as a cloned `Arc<T>`. This increments the reference
131  /// count, so the caller gets an independent handle to the underlying data.
132  ///
133  /// Returns `None` if the value has not been set yet.
134  ///
135  /// # Examples
136  ///
137  /// ```
138  /// use std::sync::Arc;
139  /// use std::sync::atomic::Ordering;
140  /// use once_arc::OnceArc;
141  ///
142  /// let slot = OnceArc::from(Arc::new(42));
143  /// let arc = slot.load(Ordering::Acquire).unwrap();
144  /// assert_eq!(*arc, 42);
145  /// ```
146  pub fn load(&self, ordering: Ordering) -> Option<Arc<T>> {
147    let ptr = self.ptr.load(ordering);
148    if ptr.is_null() {
149      None
150    } else {
151      // SAFETY: The pointer is valid and the Arc is alive (same reasoning as get).
152      // increment_strong_count keeps the existing Arc alive while we create a new one.
153      unsafe { Arc::increment_strong_count(ptr) };
154      Some(unsafe { Arc::from_raw(ptr) })
155    }
156  }
157
158  /// Returns `true` if a value has been stored.
159  ///
160  /// # Examples
161  ///
162  /// ```
163  /// use std::sync::Arc;
164  /// use std::sync::atomic::Ordering;
165  /// use once_arc::OnceArc;
166  ///
167  /// let slot: OnceArc<i32> = OnceArc::new();
168  /// assert!(!slot.is_set(Ordering::Relaxed));
169  ///
170  /// slot.store(Arc::new(1), Ordering::Release).unwrap();
171  /// assert!(slot.is_set(Ordering::Relaxed));
172  /// ```
173  pub fn is_set(&self, ordering: Ordering) -> bool {
174    !self.ptr.load(ordering).is_null()
175  }
176
177  /// Consumes `self` and returns the stored `Arc<T>`, if any.
178  ///
179  /// # Examples
180  ///
181  /// ```
182  /// use std::sync::Arc;
183  /// use std::sync::atomic::Ordering;
184  /// use once_arc::OnceArc;
185  ///
186  /// let slot = OnceArc::from(Arc::new(42));
187  /// let arc = slot.into_inner().unwrap();
188  /// assert_eq!(*arc, 42);
189  /// ```
190  pub fn into_inner(mut self) -> Option<Arc<T>> {
191    let ptr = *self.ptr.get_mut();
192    std::mem::forget(self); // skip Drop since we're taking ownership of the Arc
193    if ptr.is_null() {
194      None
195    } else {
196      // SAFETY: We have exclusive ownership via `self` (by value).
197      Some(unsafe { Arc::from_raw(ptr) })
198    }
199  }
200
201  /// Returns a mutable reference to the stored value, or `None` if not yet set.
202  ///
203  /// Since this requires `&mut self`, no atomic operations are needed and this
204  /// is guaranteed to be the only accessor.
205  ///
206  /// # Examples
207  ///
208  /// ```
209  /// use std::sync::Arc;
210  /// use std::sync::atomic::Ordering;
211  /// use once_arc::OnceArc;
212  ///
213  /// let mut slot = OnceArc::from(Arc::new(10));
214  /// *slot.get_mut().unwrap() = 20;
215  /// assert_eq!(slot.get(Ordering::Acquire), Some(&20));
216  /// ```
217  pub fn get_mut(&mut self) -> Option<&mut T> {
218    let ptr = *self.ptr.get_mut();
219    if ptr.is_null() {
220      None
221    } else {
222      // SAFETY: We have exclusive access via &mut self. The pointer was created
223      // by Arc::into_raw and the data is valid until drop. &mut self guarantees
224      // no other references exist.
225      Some(unsafe { &mut *ptr })
226    }
227  }
228}
229
230impl<T> Default for OnceArc<T> {
231  fn default() -> Self {
232    Self::new()
233  }
234}
235
236impl<T: fmt::Debug> fmt::Debug for OnceArc<T> {
237  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238    f.debug_struct("OnceArc")
239      .field("value", &self.get(Ordering::SeqCst))
240      .finish()
241  }
242}
243
244impl<T> Drop for OnceArc<T> {
245  fn drop(&mut self) {
246    let ptr = *self.ptr.get_mut();
247    if !ptr.is_null() {
248      // SAFETY: We have &mut self, so no other references exist.
249      // The pointer was created by Arc::into_raw in store().
250      unsafe { drop(Arc::from_raw(ptr)) };
251    }
252  }
253}
254
255impl<T> From<Arc<T>> for OnceArc<T> {
256  fn from(value: Arc<T>) -> Self {
257    Self {
258      ptr: AtomicPtr::new(Arc::into_raw(value) as *mut T),
259    }
260  }
261}
262
263impl<T> From<Option<Arc<T>>> for OnceArc<T> {
264  fn from(value: Option<Arc<T>>) -> Self {
265    match value {
266      Some(arc) => Self::from(arc),
267      None => Self::new(),
268    }
269  }
270}
271
272#[cfg(test)]
273mod tests {
274  use super::*;
275  use std::sync::Arc;
276  use std::sync::atomic::Ordering;
277
278  #[test]
279  fn empty_loads_none() {
280    let slot: OnceArc<i32> = OnceArc::new();
281    assert!(slot.get(Ordering::Acquire).is_none());
282    assert!(slot.load(Ordering::Acquire).is_none());
283    assert!(!slot.is_set(Ordering::Relaxed));
284  }
285
286  #[test]
287  fn set_once_and_load() {
288    let slot: OnceArc<i32> = OnceArc::new();
289    slot.store(Arc::new(42), Ordering::Release).unwrap();
290    assert_eq!(*slot.get(Ordering::Acquire).unwrap(), 42);
291    assert!(slot.is_set(Ordering::Relaxed));
292  }
293
294  #[test]
295  fn set_twice_fails() {
296    let slot: OnceArc<i32> = OnceArc::new();
297    slot.store(Arc::new(1), Ordering::Release).unwrap();
298    let err = slot.store(Arc::new(2), Ordering::Release).unwrap_err();
299    assert_eq!(*err, 2);
300    assert_eq!(*slot.get(Ordering::Acquire).unwrap(), 1);
301  }
302
303  #[test]
304  fn load_returns_arc() {
305    let slot: OnceArc<&str> = OnceArc::new();
306    let original = Arc::new("hello");
307    slot.store(original.clone(), Ordering::Release).unwrap();
308
309    let loaded = slot.load(Ordering::Acquire).unwrap();
310    assert!(Arc::ptr_eq(&original, &loaded));
311    assert_eq!(Arc::strong_count(&original), 3); // original + slot + loaded
312  }
313
314  #[test]
315  fn into_inner_returns_arc() {
316    let slot: OnceArc<i32> = OnceArc::new();
317    let original = Arc::new(100);
318    slot.store(original.clone(), Ordering::Release).unwrap();
319
320    let inner = slot.into_inner().unwrap();
321    assert!(Arc::ptr_eq(&original, &inner));
322    assert_eq!(Arc::strong_count(&original), 2); // original + inner (slot consumed)
323  }
324
325  #[test]
326  fn into_inner_empty() {
327    let slot: OnceArc<i32> = OnceArc::new();
328    assert!(slot.into_inner().is_none());
329  }
330
331  #[test]
332  fn drop_decrements_refcount() {
333    let arc = Arc::new(42);
334    assert_eq!(Arc::strong_count(&arc), 1);
335    {
336      let slot: OnceArc<i32> = OnceArc::new();
337      slot.store(arc.clone(), Ordering::Release).unwrap();
338      assert_eq!(Arc::strong_count(&arc), 2);
339    }
340    assert_eq!(Arc::strong_count(&arc), 1);
341  }
342
343  #[test]
344  fn from_arc() {
345    let slot = OnceArc::from(Arc::new(7));
346    assert_eq!(*slot.get(Ordering::Acquire).unwrap(), 7);
347  }
348
349  #[test]
350  fn from_none() {
351    let slot = OnceArc::<i32>::from(None);
352    assert!(slot.get(Ordering::Acquire).is_none());
353  }
354
355  #[test]
356  fn from_some() {
357    let slot = OnceArc::from(Some(Arc::new(55)));
358    assert_eq!(*slot.get(Ordering::Acquire).unwrap(), 55);
359  }
360
361  #[test]
362  fn debug_fmt() {
363    let slot: OnceArc<i32> = OnceArc::new();
364    slot.store(Arc::new(42), Ordering::Release).unwrap();
365    let dbg = format!("{:?}", slot);
366    assert!(dbg.contains("42"));
367  }
368
369  #[test]
370  fn concurrent_set_exactly_one_wins() {
371    use std::sync::Barrier;
372    use std::thread;
373
374    let slot = Arc::new(OnceArc::<i32>::new());
375    let barrier = Arc::new(Barrier::new(10));
376    let mut handles = Vec::new();
377
378    for i in 0..10 {
379      let slot = slot.clone();
380      let barrier = barrier.clone();
381      handles.push(thread::spawn(move || {
382        barrier.wait();
383        slot.store(Arc::new(i), Ordering::Release).is_ok()
384      }));
385    }
386
387    let successes: Vec<bool> = handles.into_iter().map(|h| h.join().unwrap()).collect();
388    assert_eq!(successes.iter().filter(|&&s| s).count(), 1);
389    assert!(slot.is_set(Ordering::Relaxed));
390  }
391
392  #[test]
393  fn concurrent_loads_after_set() {
394    use std::sync::Barrier;
395    use std::thread;
396
397    let slot = Arc::new(OnceArc::from(Arc::new(99)));
398    let barrier = Arc::new(Barrier::new(10));
399    let mut handles = Vec::new();
400
401    for _ in 0..10 {
402      let slot = slot.clone();
403      let barrier = barrier.clone();
404      handles.push(thread::spawn(move || {
405        barrier.wait();
406        *slot.get(Ordering::Acquire).unwrap()
407      }));
408    }
409
410    for h in handles {
411      assert_eq!(h.join().unwrap(), 99);
412    }
413  }
414
415  #[test]
416  fn get_mut_empty() {
417    let mut slot: OnceArc<i32> = OnceArc::new();
418    assert!(slot.get_mut().is_none());
419  }
420
421  #[test]
422  fn get_mut_modifies_value() {
423    let mut slot: OnceArc<i32> = OnceArc::new();
424    slot.store(Arc::new(10), Ordering::Release).unwrap();
425    *slot.get_mut().unwrap() = 20;
426    assert_eq!(*slot.get(Ordering::Acquire).unwrap(), 20);
427  }
428}