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