async_fifo/
slot.rs

1//! # `Slot<T>`
2//! 
3//! You can atomically swap the contents of this slot from any thread.
4//! Think of it as `Mutex<Option<Box<T>>>` without any actual locking.
5
6use core::sync::atomic::Ordering::SeqCst;
7use core::sync::atomic::AtomicPtr;
8
9use alloc::boxed::Box;
10
11use crate::try_swap_ptr;
12
13/// Atomically Swappable `Option<Box<T>>`
14pub struct Slot<T> {
15    inner: AtomicPtr<T>,
16}
17
18impl<T> Slot<T> {
19    const EMPTY: *mut T = 1 as *mut T;
20    const LOCKED: *mut T = 2 as *mut T;
21
22    pub const NONE: Self = Self {
23        inner: AtomicPtr::new(Self::EMPTY)
24    };
25
26    /// Creates a new `Slot` with an initial item inside.
27    ///
28    /// To create an empty slot, use `Slot::default()` or `Slot::NONE`.
29    pub fn new(item: Box<T>) -> Self {
30        let slot = Self::default();
31        assert!(slot.try_insert(item).is_ok());
32        slot
33    }
34
35    /// Tries to push an item into this slot, failing if it's occupied or locked.
36    pub fn try_insert(&self, item: Box<T>) -> Result<(), Box<T>> {
37        let item_ptr = Box::leak(item);
38        match try_swap_ptr(&self.inner, Self::EMPTY, item_ptr) {
39            true => Ok(()),
40            false => Err(unsafe { Box::from_raw(item_ptr) }),
41        }
42    }
43
44    /// Forcibly pushes an item into this slot, returning the previous content
45    ///
46    /// This discards the "locked" flag.
47    pub fn insert(&self, item: Box<T>) -> Option<Box<T>> {
48        let item_ptr = Box::leak(item);
49        match self.inner.swap(item_ptr, SeqCst) {
50            p if p == (Self::EMPTY) || (p == Self::LOCKED) => None,
51            other => Some(unsafe { Box::from_raw(other) }),
52        }
53    }
54
55    /// Tries to extract the contained item.
56    ///
57    /// Setting `lock` to `true` will prevent pushing into this slot again.
58    pub fn try_take(&self, lock: bool) -> Option<Box<T>> {
59        let item_ptr = match self.inner.load(SeqCst) {
60            p if p == (Self::EMPTY) || (p == Self::LOCKED) => None,
61            other => Some(other),
62        }?;
63
64        let next = match lock {
65            true => Self::LOCKED,
66            false => Self::EMPTY,
67        };
68
69        match try_swap_ptr(&self.inner, item_ptr, next) {
70            true => Some(unsafe { Box::from_raw(item_ptr) }),
71            false => None,
72        }
73    }
74
75    pub fn is_locked(&self) -> bool {
76        self.inner.load(SeqCst) == Self::LOCKED
77    }
78
79    /// Forcibly unlocks this slot.
80    ///
81    /// Does nothing if the slot isn't locked
82    pub fn unlock(&self) -> bool {
83        try_swap_ptr(&self.inner, Self::LOCKED, Self::EMPTY)
84    }
85}
86
87impl<T> Default for Slot<T> {
88    fn default() -> Self {
89        Self::NONE
90    }
91}
92
93impl<T> Drop for Slot<T> {
94    fn drop(&mut self) {
95        core::mem::drop(self.try_take(true));
96    }
97}