ach_once/
lib.rs

1#![no_std]
2use core::fmt;
3use core::mem::MaybeUninit;
4use core::ptr;
5use core::sync::atomic::Ordering::SeqCst;
6use interrupt::CriticalSection;
7use util::*;
8
9pub struct Once<T> {
10    val: MaybeUninit<T>,
11    state: AtomicMemoryState,
12}
13impl<T> Once<T> {
14    pub const fn new() -> Self {
15        Once {
16            val: MaybeUninit::uninit(),
17            state: AtomicMemoryState::new(MemoryState::Uninitialized),
18        }
19    }
20    pub const fn new_with(init: T) -> Self {
21        Once {
22            val: MaybeUninit::new(init),
23            state: AtomicMemoryState::new(MemoryState::Initialized),
24        }
25    }
26    fn ptr(&self) -> *mut T {
27        self.val.as_ptr() as *mut T
28    }
29    pub fn is_initialized(&self) -> bool {
30        let state = self.state.load(SeqCst);
31        state.is_initialized()
32    }
33    pub fn take(&mut self) -> Option<T> {
34        if self.is_initialized() {
35            let ret = unsafe { ptr::read(self.ptr()) };
36            self.state.store(MemoryState::Uninitialized.into(), SeqCst);
37            Some(ret)
38        } else {
39            None
40        }
41    }
42    pub fn into_inner(self) -> Option<T> {
43        if self.is_initialized() {
44            let ret = unsafe { ptr::read(self.ptr()) };
45            Some(ret)
46        } else {
47            None
48        }
49    }
50
51    /// Tries to get a reference to the value of the Cell.
52    ///
53    /// Returns Err if the cell is uninitialized or in critical section.
54    pub fn try_get(&self) -> Result<&T, Error<()>> {
55        let state = self.state.load(SeqCst);
56        if state.is_initialized() {
57            let ret = unsafe { self.val.assume_init_ref() };
58            Ok(ret)
59        } else {
60            Err(Error {
61                state,
62                input: (),
63                retry: state.is_initializing(),
64            })
65        }
66    }
67    /// Tries to get a reference to the value of the Cell.
68    ///
69    /// Returns Err if the cell is uninitialized.
70    ///
71    /// Notice: `Spin`
72    pub fn get(&self) -> Result<&T, Error<()>> {
73        retry(|_| self.try_get(), ())
74    }
75    pub fn get_mut(&mut self) -> Option<&mut T> {
76        if self.is_initialized() {
77            let ret = unsafe { self.val.assume_init_mut() };
78            Some(ret)
79        } else {
80            None
81        }
82    }
83
84    /// Sets the value of the Cell to the argument value.
85    ///
86    /// Returns Err if the value is initialized or in critical section.
87    pub fn try_set(&self, value: T) -> Result<(), Error<T>> {
88        let _cs = CriticalSection::new();
89        if let Err(state) = self.state.compare_exchange(
90            MemoryState::Uninitialized.into(),
91            MemoryState::Initializing.into(),
92            SeqCst,
93            SeqCst,
94        ) {
95            Err(Error {
96                state,
97                input: value,
98                retry: state.is_erasing(),
99            })
100        } else {
101            unsafe { ptr::write(self.ptr(), value) };
102            self.state.store(MemoryState::Initialized.into(), SeqCst);
103            Ok(())
104        }
105    }
106    /// Sets the value of the Cell to the argument value.
107    ///
108    /// Returns Err if the value is initialized.
109    /// 
110    /// Notice: `Spin`
111    pub fn set(&self,  value: T) -> Result<(), Error<T>> {
112        retry(|val|self.try_set(val), value)
113    }
114
115    /// Tries to get a reference to the value of the Cell.
116    ///
117    /// Returns Err if the cell is in critical section.
118    pub fn get_or_try_init(&self, value: T) -> Result<&T, Error<T>> {
119        let _cs = CriticalSection::new();
120        if let Err(_) = self.state.compare_exchange(
121            MemoryState::Uninitialized.into(),
122            MemoryState::Initializing.into(),
123            SeqCst,
124            SeqCst,
125        ) {
126            self.try_get().map_err(
127                |Error {
128                     state,
129                     input: _,
130                     retry,
131                 }| Error {
132                    state,
133                    input: value,
134                    retry,
135                },
136            )
137        } else {
138            unsafe { ptr::write(self.ptr(), value) };
139            self.state.store(MemoryState::Initialized.into(), SeqCst);
140            Ok(unsafe { self.val.assume_init_ref() })
141        }
142    }
143    /// Tries to get a reference to the value of the Cell.
144    ///
145    /// Notice: `Spin`
146    pub fn get_or_init(&self, mut value: T) -> &T {
147        loop {
148            match self.get_or_try_init(value) {
149                Ok(val) => return val,
150                Err(err) if err.retry => {
151                    value = err.input;
152                    spin_loop::spin();
153                    continue;
154                }
155                Err(_) => unreachable!(),
156            }
157        }
158    }
159}
160impl<T: fmt::Debug> fmt::Debug for Once<T> {
161    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162        fmt::Debug::fmt(&self.get(), f)
163    }
164}
165impl<T> Drop for Once<T> {
166    fn drop(&mut self) {
167        self.take();
168    }
169}