atomic_option/
lib.rs

1#![cfg_attr(test, deny(warnings))]
2#![deny(missing_docs)]
3
4//! # atomic-option
5//!
6//! An atomic, nullable, owned pointer.
7//!
8
9use std::mem;
10use std::marker::PhantomData;
11use std::sync::atomic::{AtomicUsize, Ordering};
12
13const NULL: usize = 0;
14
15/// An atomic version of `Option<Box<T>>`, useful for moving owned objects
16/// between threads in a wait-free manner.
17pub struct AtomicOption<T> {
18    // Contains the address of a Box<T>, or 0 to indicate None
19    inner: AtomicUsize,
20    phantom: PhantomData<Option<Box<T>>>
21}
22
23unsafe impl<T> Sync for AtomicOption<T> {}
24
25impl<T> AtomicOption<T> {
26    /// Create a new AtomicOption storing the specified data.
27    ///
28    /// ```
29    /// # use std::sync::atomic::Ordering;
30    /// # use atomic_option::AtomicOption;
31    /// let opt = AtomicOption::new(Box::new(7));
32    /// let value = opt.take(Ordering::SeqCst).unwrap();
33    /// assert_eq!(value, Box::new(7));
34    /// ```
35    #[inline]
36    pub fn new(data: Box<T>) -> AtomicOption<T> {
37        AtomicOption {
38            inner: AtomicUsize::new(into_raw(data)),
39            phantom: PhantomData
40        }
41    }
42
43    /// Create a new AtomicOption from a raw pointer.
44    pub unsafe fn from_raw(ptr: *mut T) -> AtomicOption<T> {
45        AtomicOption {
46            inner: AtomicUsize::new(ptr as usize),
47            phantom: PhantomData
48        }
49    }
50
51    /// Create a new AtomicOption storing None.
52    ///
53    /// ```
54    /// # use std::sync::atomic::Ordering;
55    /// # use atomic_option::AtomicOption;
56    /// let opt: AtomicOption<()> = AtomicOption::empty();
57    /// let value = opt.take(Ordering::SeqCst);
58    /// assert!(value.is_none());
59    /// ```
60    #[inline]
61    pub fn empty() -> AtomicOption<T> {
62        AtomicOption {
63            inner: AtomicUsize::new(NULL),
64            phantom: PhantomData
65        }
66    }
67
68    /// Take the value out of the AtomicOption, if there is one.
69    ///
70    /// ```
71    /// # use std::sync::atomic::Ordering;
72    /// # use atomic_option::AtomicOption;
73    /// let opt = AtomicOption::new(Box::new(178));
74    /// let first_take = opt.take(Ordering::SeqCst);
75    /// let second_take = opt.take(Ordering::SeqCst);
76    ///
77    /// assert_eq!(first_take, Some(Box::new(178)));
78    /// assert!(second_take.is_none());
79    /// ```
80    #[inline]
81    pub fn take(&self, ordering: Ordering) -> Option<Box<T>> {
82        self.replace(None, ordering)
83    }
84
85    /// Swap the value in the AtomicOption with a new one, returning the
86    /// old value if there was one.
87    ///
88    /// ```
89    /// # use std::sync::atomic::Ordering;
90    /// # use atomic_option::AtomicOption;
91    /// let opt = AtomicOption::new(Box::new(1236));
92    /// let old = opt.swap(Box::new(542), Ordering::SeqCst).unwrap();
93    /// assert_eq!(old, Box::new(1236));
94    ///
95    /// let new = opt.take(Ordering::SeqCst).unwrap();
96    /// assert_eq!(new, Box::new(542));
97    /// ```
98    #[inline]
99    pub fn swap(&self, new: Box<T>, ordering: Ordering) -> Option<Box<T>> {
100        self.replace(Some(new), ordering)
101    }
102
103    /// Replace the Option in the AtomicOption with a new one, returning the old option.
104    ///
105    /// ```
106    /// # use std::sync::atomic::Ordering;
107    /// # use atomic_option::AtomicOption;
108    /// let opt = AtomicOption::empty();
109    /// let old = opt.replace(Some(Box::new("hello")), Ordering::SeqCst);
110    /// assert!(old.is_none());
111    ///
112    /// let new = opt.take(Ordering::SeqCst).unwrap();
113    /// assert_eq!(new, Box::new("hello"));
114    /// ```
115    #[inline]
116    pub fn replace(&self, new: Option<Box<T>>, ordering: Ordering) -> Option<Box<T>> {
117        let raw_new = new.map(into_raw).unwrap_or(0);
118
119        match self.inner.swap(raw_new, ordering) {
120            NULL => None,
121            old => Some(unsafe { from_raw(old) })
122        }
123    }
124
125    /// Store the new value in the AtomicOption iff it currently contains a None.
126    ///
127    /// None is returned if the store succeeded, or Some is returned with the rejected
128    /// data if the store fails.
129    ///
130    /// This operation is implemented as a single atomic `compare_and_swap`.
131    ///
132    /// ```
133    /// # use std::sync::atomic::Ordering;
134    /// # use atomic_option::AtomicOption;
135    /// let opt = AtomicOption::empty();
136    /// let stored = opt.try_store(Box::new("some data"), Ordering::SeqCst);
137    /// assert!(stored.is_none());
138    ///
139    /// let stored2 = opt.try_store(Box::new("some more data"), Ordering::SeqCst);
140    /// assert_eq!(stored2, Some(Box::new("some more data")));
141    ///
142    /// let value = opt.take(Ordering::SeqCst).unwrap();
143    /// assert_eq!(value, Box::new("some data"));
144    /// ```
145    #[inline]
146    pub fn try_store(&self, new: Box<T>, ordering: Ordering) -> Option<Box<T>> {
147        let raw_new = into_raw(new);
148
149        match self.inner.compare_and_swap(NULL, raw_new, ordering) {
150            NULL => None,
151            _ => Some(unsafe { from_raw(raw_new) })
152        }
153    }
154
155    /// Execute a `compare_and_swap` loop until there is a value in the AtomicOption,
156    /// then return it.
157    ///
158    /// ```
159    /// # use std::sync::atomic::Ordering;
160    /// # use std::sync::Arc;
161    /// # use std::thread;
162    /// # use atomic_option::AtomicOption;
163    ///
164    /// // We'll use an AtomicOption as a lightweight channel transferring data via a spinlock.
165    /// let tx = Arc::new(AtomicOption::empty());
166    /// let rx = tx.clone();
167    ///
168    /// thread::spawn(move || {
169    ///     assert_eq!(*rx.spinlock(Ordering::Acquire), 7);
170    /// });
171    ///
172    /// tx.swap(Box::new(7), Ordering::Release);
173    /// ```
174    #[inline]
175    pub fn spinlock(&self, ordering: Ordering) -> Box<T> {
176        loop {
177            match self.replace(None, ordering) {
178                Some(v) => return v,
179                None => {}
180            }
181        }
182    }
183
184    /// Get the raw value stored in the AtomicOption.
185    ///
186    /// ## Safety
187    ///
188    /// It is almost *never* safe to read from this pointer.
189    pub fn load_raw(&self, ordering: Ordering) -> *const T {
190        self.inner.load(ordering) as *const T
191    }
192}
193
194impl<T> From<Option<Box<T>>> for AtomicOption<T> {
195    #[inline]
196    fn from(opt: Option<Box<T>>) -> AtomicOption<T> {
197        match opt {
198            Some(data) => AtomicOption::new(data),
199            None => AtomicOption::empty()
200        }
201    }
202}
203
204impl<T> Drop for AtomicOption<T> {
205    fn drop(&mut self) {
206        let _ = self.take(Ordering::SeqCst);
207    }
208}
209
210#[inline(always)]
211fn into_raw<T>(data: Box<T>) -> usize {
212    unsafe { mem::transmute(data) }
213}
214
215#[inline(always)]
216unsafe fn from_raw<T>(data: usize) -> Box<T> {
217    mem::transmute(data)
218}
219