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}