1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use crate::{InnerAtomicFlag, FALSE, TRUE};
use core::{
    cell::UnsafeCell,
    mem::{needs_drop, MaybeUninit},
    sync::atomic::Ordering,
};

/// Inverse of a `OnceCell`. It initializes with a value, which then can be raced by other threads to take.
///
/// Once the value is taken, it can never be taken again.
pub struct TakeCell<T> {
    taken: InnerAtomicFlag,
    v: UnsafeCell<MaybeUninit<T>>,
}

impl<T> TakeCell<T> {
    /// Creates a new [`TakeCell`]
    #[inline]
    pub const fn new(v: T) -> Self {
        Self {
            taken: InnerAtomicFlag::new(FALSE),
            v: UnsafeCell::new(MaybeUninit::new(v)),
        }
    }

    /// Creates a [`TakeCell`] that has already been taken
    #[inline]
    pub const fn new_taken() -> Self {
        Self {
            taken: InnerAtomicFlag::new(TRUE),
            v: UnsafeCell::new(MaybeUninit::uninit()),
        }
    }

    /// Checks if the cell has alredy been taken
    #[inline]
    pub fn is_taken(&self) -> bool {
        self.taken.load(Ordering::Relaxed) == TRUE
    }

    /// Attempts to take the value from the cell, returning `None` if the value has already been taken
    #[inline]
    pub fn try_take(&self) -> Option<T> {
        if self
            .taken
            .compare_exchange(FALSE, TRUE, Ordering::AcqRel, Ordering::Acquire)
            .is_ok()
        {
            unsafe {
                let v = &*self.v.get();
                return Some(v.assume_init_read());
            }
        }
        None
    }

    /// Attempts to take the value from the cell through non-atomic operations, returning `None` if the value has already been taken
    ///
    /// # Safety
    /// This method is safe because the mutable reference indicates we are the only thread with access to the cell,
    /// so atomic operations aren't required.
    #[inline]
    pub fn try_take_mut(&mut self) -> Option<T> {
        let taken = self.taken.get_mut();
        if *taken == FALSE {
            *taken = TRUE;

            unsafe { return Some(self.v.get_mut().assume_init_read()) }
        }
        None
    }
}

impl<T> Drop for TakeCell<T> {
    #[inline]
    fn drop(&mut self) {
        if needs_drop::<T>() && *self.taken.get_mut() == FALSE {
            unsafe { self.v.get_mut().assume_init_drop() }
        }
    }
}

unsafe impl<T: Send> Send for TakeCell<T> {}
unsafe impl<T: Sync> Sync for TakeCell<T> {}

// Thanks ChatGPT!
#[cfg(test)]
mod tests {
    use super::TakeCell;

    #[test]
    fn test_normal_conditions() {
        let cell = TakeCell::new(42);
        assert_eq!(cell.is_taken(), false);
        assert_eq!(cell.try_take(), Some(42));
        assert_eq!(cell.is_taken(), true);
        assert_eq!(cell.try_take(), None);

        let mut cell = TakeCell::new(42);
        assert_eq!(cell.try_take_mut(), Some(42));
        assert_eq!(cell.try_take_mut(), None);
    }

    #[cfg(feature = "std")]
    #[test]
    fn test_stressed_conditions() {
        use alloc::vec::Vec;
        use std::{
            sync::{Arc, Barrier},
            thread,
        };

        let cell = Arc::new(TakeCell::new(42));
        let barrier = Arc::new(Barrier::new(10));

        let mut handles = Vec::new();

        for _ in 0..10 {
            let c = Arc::clone(&cell);
            let b = Arc::clone(&barrier);
            handles.push(thread::spawn(move || {
                b.wait();
                c.try_take()
            }));
        }

        for handle in handles {
            handle.join().unwrap();
        }

        assert_eq!(cell.is_taken(), true);
        assert_eq!(cell.try_take(), None);
    }
}