1#![doc = include_str!("../README.md")]
2#![cfg_attr(feature = "const-new", feature(const_fn_trait_bound))]
3
4use std::{
5 fmt::{Debug, Formatter},
6 marker::PhantomData,
7 sync::{
8 atomic::{AtomicUsize, Ordering},
9 Arc, Weak,
10 },
11};
12
13pub type ArcCell<T> = AtomicCell<Arc<T>>;
15pub type WeakCell<T> = AtomicCell<Weak<T>>;
17
18pub type OptionalArcCell<T> = AtomicCell<Option<Arc<T>>>;
20pub type OptionalWeakCell<T> = AtomicCell<Option<Weak<T>>>;
22
23pub struct AtomicCell<T: AtomicCellStorable> {
25 value: AtomicUsize,
26 _marker: PhantomData<T>,
27}
28
29impl<T: AtomicCellStorable> AtomicCell<T> {
30 pub fn new(value: T) -> Self {
32 AtomicCell {
33 value: AtomicUsize::new(value.into_value()),
34 _marker: PhantomData,
35 }
36 }
37
38 pub fn set(&self, value: T) -> T {
40 let old = self.internal_take();
41 self.internal_put(value);
42 old
43 }
44
45 fn internal_take(&self) -> T {
46 unsafe {
47 let mut current = self.value.load(Ordering::SeqCst);
48 T::from_value(loop {
49 match self.value.compare_exchange_weak(
51 current,
52 T::TAKEN_VALUE,
53 Ordering::SeqCst,
54 Ordering::SeqCst,
55 ) {
56 Ok(val) if val != T::TAKEN_VALUE => break val,
57 Ok(_) => current = T::TAKEN_VALUE, Err(new_val) => current = new_val, }
60
61 core::hint::spin_loop();
64 })
65 }
66 }
67
68 fn internal_put(&self, value: T) {
69 let _old = self.value.swap(value.into_value(), Ordering::SeqCst);
70 debug_assert_eq!(_old, T::TAKEN_VALUE);
71 }
72}
73
74impl<T: AtomicCellStorable> Drop for AtomicCell<T> {
75 fn drop(&mut self) {
76 unsafe {
77 let _ = T::from_value(self.value.load(Ordering::SeqCst));
78 }
79 }
80}
81
82impl<T: AtomicCellStorable + Clone> AtomicCell<T> {
83 pub fn get(&self) -> T {
85 let value = self.internal_take();
86 let copy = value.clone();
87 self.internal_put(value);
88 copy
89 }
90}
91
92impl<T: AtomicCellStorable + Clone> Clone for AtomicCell<T> {
93 fn clone(&self) -> AtomicCell<T> {
94 AtomicCell::new(self.get())
95 }
96}
97
98impl<T: AtomicCellStorable + Default> AtomicCell<T> {
99 pub fn take(&self) -> T {
101 let new_value = T::default();
103
104 let value = self.internal_take();
105 self.internal_put(new_value);
106
107 value
108 }
109}
110
111impl<T: AtomicCellStorable + Default> Default for AtomicCell<T> {
112 fn default() -> Self {
113 AtomicCell::new(T::default())
114 }
115}
116
117#[cfg(feature = "const-new")]
118impl<T: AtomicCellStorable + AtomicCellConstInit> AtomicCell<T> {
119 pub const fn const_new() -> Self {
120 AtomicCell {
121 value: AtomicUsize::new(T::DEFAULT_VALUE),
122 _marker: PhantomData,
123 }
124 }
125}
126
127impl<T> AtomicCell<Weak<T>> {
128 pub fn empty() -> Self {
130 AtomicCell::new(Weak::new())
131 }
132
133 pub fn upgrade(&self) -> Option<Arc<T>> {
135 self.get().upgrade()
136 }
137
138 pub fn store(&self, arc: &Arc<T>) {
140 self.set(Arc::downgrade(arc));
141 }
142}
143
144impl<T> AtomicCell<Option<Weak<T>>> {
145 pub fn upgrade(&self) -> Option<Arc<T>> {
147 self.get().and_then(|weak| weak.upgrade())
148 }
149
150 pub fn store(&self, arc: &Arc<T>) {
152 self.set(Some(Arc::downgrade(arc)));
153 }
154}
155
156impl<T: AtomicCellStorable + Clone + Debug> Debug for AtomicCell<T> {
157 fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
158 fmt.debug_tuple("AtomicCell").field(&self.get()).finish()
159 }
160}
161
162pub unsafe trait AtomicCellStorable {
168 const TAKEN_VALUE: usize;
170 fn into_value(self) -> usize;
172 unsafe fn from_value(value: usize) -> Self;
174}
175
176unsafe impl<T> AtomicCellStorable for Arc<T> {
177 const TAKEN_VALUE: usize = usize::MAX;
178
179 fn into_value(self) -> usize {
180 Arc::into_raw(self) as usize
181 }
182
183 unsafe fn from_value(value: usize) -> Self {
184 Arc::from_raw(value as *const T)
185 }
186}
187
188unsafe impl<T> AtomicCellStorable for Weak<T> {
189 const TAKEN_VALUE: usize = usize::MAX - 1;
191
192 fn into_value(self) -> usize {
193 Weak::into_raw(self) as usize
194 }
195
196 unsafe fn from_value(value: usize) -> Self {
197 Weak::from_raw(value as *const T)
198 }
199}
200
201const EMPTY_OPTION: usize = 0;
202
203unsafe impl<T> AtomicCellStorable for Option<Arc<T>> {
204 const TAKEN_VALUE: usize = <Arc<T> as AtomicCellStorable>::TAKEN_VALUE;
205
206 fn into_value(self) -> usize {
207 match self {
208 None => EMPTY_OPTION,
209 Some(arc) => Arc::into_raw(arc) as usize,
210 }
211 }
212
213 unsafe fn from_value(value: usize) -> Self {
214 match value {
215 EMPTY_OPTION => None,
216 value => Some(Arc::from_raw(value as *const T)),
217 }
218 }
219}
220
221unsafe impl<T> AtomicCellStorable for Option<Weak<T>> {
222 const TAKEN_VALUE: usize = <Weak<T> as AtomicCellStorable>::TAKEN_VALUE;
223
224 fn into_value(self) -> usize {
225 match self {
226 None => EMPTY_OPTION,
227 Some(arc) => Weak::into_raw(arc) as usize,
228 }
229 }
230
231 unsafe fn from_value(value: usize) -> Self {
232 match value {
233 EMPTY_OPTION => None,
234 value => Some(Weak::from_raw(value as *const T)),
235 }
236 }
237}
238
239pub unsafe trait AtomicCellConstInit {
240 const DEFAULT_VALUE: usize;
241}
242
243unsafe impl<T> AtomicCellConstInit for Option<Arc<T>> {
244 const DEFAULT_VALUE: usize = EMPTY_OPTION;
245}
246
247unsafe impl<T> AtomicCellConstInit for Option<Weak<T>> {
248 const DEFAULT_VALUE: usize = EMPTY_OPTION;
249}
250
251#[cfg(test)]
252mod tests {
253 use crate::{ArcCell, WeakCell};
254 use std::sync::{
255 atomic::{AtomicUsize, Ordering},
256 Arc,
257 };
258
259 #[test]
260 fn arc_cell() {
261 let data1 = Arc::new(5);
262 let data2 = Arc::new(6);
263
264 let cell = ArcCell::new(data1);
265 assert_eq!(*cell.get(), 5);
266 cell.set(data2);
267 assert_eq!(*cell.get(), 6);
268 }
269
270 #[test]
271 fn weak_cell() {
272 let data = Arc::new(5);
273
274 let cell = WeakCell::empty();
275 cell.store(&data);
276 assert_eq!(cell.upgrade(), Some(data.clone()));
277 drop(data);
278 assert_eq!(cell.upgrade(), None);
279 }
280
281 #[test]
282 fn cell_drops() {
283 static DROPS: AtomicUsize = AtomicUsize::new(0);
284 struct DropCount;
285 impl std::ops::Drop for DropCount {
286 fn drop(&mut self) {
287 DROPS.fetch_add(1, Ordering::SeqCst);
288 }
289 }
290 {
291 let _cell = ArcCell::new(Arc::new(DropCount));
292 }
293 assert_eq!(DROPS.load(Ordering::SeqCst), 1);
294 }
295}