ping_pong_cell/
lib.rs

1#![no_std]
2use core::cell::UnsafeCell;
3use core::sync::atomic::Ordering::{Acquire, Release};
4use core::sync::atomic::{spin_loop_hint, AtomicBool};
5
6/// An Atomic Cell game for up to two players.
7#[derive(Debug)]
8pub struct PingPongCell<T> {
9    is_working: AtomicBool,
10    value: UnsafeCell<Option<T>>,
11}
12
13unsafe impl<T: Send> Send for PingPongCell<T> {}
14unsafe impl<T: Sync> Sync for PingPongCell<T> {}
15
16impl<T> PingPongCell<T> {
17    /// Create a new PingPongCell
18    pub fn new(value: Option<T>) -> Self {
19        PingPongCell {
20            is_working: AtomicBool::new(false),
21            value: UnsafeCell::new(value),
22        }
23    }
24
25    /// If there is a value currently inside, take it.
26    #[inline]
27    pub fn take(&self) -> Option<T> {
28        self.transact(|state| state.take())
29    }
30
31    /// Replace the value inside unconditionally.
32    #[inline]
33    pub fn put(&self, value: T) {
34        self.transact(|state| *state = Some(value));
35    }
36
37    /// Puts a value inside only if there is not one currently.
38    pub fn put_if_empty(&self, value: T) -> Result<(), T> {
39        self.transact(|state| {
40            if state.is_some() {
41                Err(value)
42            } else {
43                *state = Some(value);
44                Ok(())
45            }
46        })
47    }
48
49    /// Runs a closure with a mutable reference to the state
50    pub fn transact<F, R>(&self, fun: F) -> R
51    where
52        F: FnOnce(&mut Option<T>) -> R,
53    {
54        while self.is_working.compare_and_swap(false, true, Acquire) {
55            spin_loop_hint();
56        }
57        let ret = unsafe { fun(&mut *self.value.get()) };
58        self.is_working.store(false, Release);
59        ret
60    }
61}
62
63impl<T: Clone> PingPongCell<T> {
64    /// `put_empty()`, but clones the current contents on failure
65    pub fn put_empty_clone(&self, value: T) -> Result<(), (T, T)> {
66        self.transact(|state| {
67            if let Some(ref old) = state {
68                Err((value, old.clone()))
69            } else {
70                *state = Some(value);
71                Ok(())
72            }
73        })
74    }
75
76    /// Clones the contents, if any.
77    pub fn clone_inner(&self) -> Option<T> {
78        self.transact(|state| state.clone())
79    }
80}
81
82impl<T: Eq> PingPongCell<T> {
83    /// A single CAS operation
84    pub fn compare_and_swap(&self, expected: &T, new: T) -> Result<(), T> {
85        self.transact(|state| match state {
86            Some(ref mut val) if val == expected => {
87                *val = new;
88                Ok(())
89            }
90            _ => Err(new),
91        })
92    }
93}
94
95impl<T: Clone + Eq> PingPongCell<T> {
96    /// A single CAS operation, cloning the current value on failure
97    pub fn compare_swap_clone(&self, expected: &T, new: T) -> Result<(), (T, Option<T>)> {
98        self.transact(|state| match state {
99            Some(val) if val == expected => {
100                *state = Some(new);
101                Ok(())
102            }
103            Some(val) => Err((new, Some(val.clone()))),
104            None => Err((new, None)),
105        })
106    }
107}