1ef40d7f6Sopenharmony_ci// There's a lot of scary concurrent code in this module, but it is copied from
2ef40d7f6Sopenharmony_ci// `std::sync::Once` with two changes:
3ef40d7f6Sopenharmony_ci//   * no poisoning
4ef40d7f6Sopenharmony_ci//   * init function can fail
5ef40d7f6Sopenharmony_ci
6ef40d7f6Sopenharmony_ciuse std::{
7ef40d7f6Sopenharmony_ci    cell::{Cell, UnsafeCell},
8ef40d7f6Sopenharmony_ci    marker::PhantomData,
9ef40d7f6Sopenharmony_ci    panic::{RefUnwindSafe, UnwindSafe},
10ef40d7f6Sopenharmony_ci    sync::atomic::{AtomicBool, AtomicPtr, Ordering},
11ef40d7f6Sopenharmony_ci    thread::{self, Thread},
12ef40d7f6Sopenharmony_ci};
13ef40d7f6Sopenharmony_ci
14ef40d7f6Sopenharmony_ci#[derive(Debug)]
15ef40d7f6Sopenharmony_cipub(crate) struct OnceCell<T> {
16ef40d7f6Sopenharmony_ci    // This `queue` field is the core of the implementation. It encodes two
17ef40d7f6Sopenharmony_ci    // pieces of information:
18ef40d7f6Sopenharmony_ci    //
19ef40d7f6Sopenharmony_ci    // * The current state of the cell (`INCOMPLETE`, `RUNNING`, `COMPLETE`)
20ef40d7f6Sopenharmony_ci    // * Linked list of threads waiting for the current cell.
21ef40d7f6Sopenharmony_ci    //
22ef40d7f6Sopenharmony_ci    // State is encoded in two low bits. Only `INCOMPLETE` and `RUNNING` states
23ef40d7f6Sopenharmony_ci    // allow waiters.
24ef40d7f6Sopenharmony_ci    queue: AtomicPtr<Waiter>,
25ef40d7f6Sopenharmony_ci    _marker: PhantomData<*mut Waiter>,
26ef40d7f6Sopenharmony_ci    value: UnsafeCell<Option<T>>,
27ef40d7f6Sopenharmony_ci}
28ef40d7f6Sopenharmony_ci
29ef40d7f6Sopenharmony_ci// Why do we need `T: Send`?
30ef40d7f6Sopenharmony_ci// Thread A creates a `OnceCell` and shares it with
31ef40d7f6Sopenharmony_ci// scoped thread B, which fills the cell, which is
32ef40d7f6Sopenharmony_ci// then destroyed by A. That is, destructor observes
33ef40d7f6Sopenharmony_ci// a sent value.
34ef40d7f6Sopenharmony_ciunsafe impl<T: Sync + Send> Sync for OnceCell<T> {}
35ef40d7f6Sopenharmony_ciunsafe impl<T: Send> Send for OnceCell<T> {}
36ef40d7f6Sopenharmony_ci
37ef40d7f6Sopenharmony_ciimpl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceCell<T> {}
38ef40d7f6Sopenharmony_ciimpl<T: UnwindSafe> UnwindSafe for OnceCell<T> {}
39ef40d7f6Sopenharmony_ci
40ef40d7f6Sopenharmony_ciimpl<T> OnceCell<T> {
41ef40d7f6Sopenharmony_ci    pub(crate) const fn new() -> OnceCell<T> {
42ef40d7f6Sopenharmony_ci        OnceCell {
43ef40d7f6Sopenharmony_ci            queue: AtomicPtr::new(INCOMPLETE_PTR),
44ef40d7f6Sopenharmony_ci            _marker: PhantomData,
45ef40d7f6Sopenharmony_ci            value: UnsafeCell::new(None),
46ef40d7f6Sopenharmony_ci        }
47ef40d7f6Sopenharmony_ci    }
48ef40d7f6Sopenharmony_ci
49ef40d7f6Sopenharmony_ci    pub(crate) const fn with_value(value: T) -> OnceCell<T> {
50ef40d7f6Sopenharmony_ci        OnceCell {
51ef40d7f6Sopenharmony_ci            queue: AtomicPtr::new(COMPLETE_PTR),
52ef40d7f6Sopenharmony_ci            _marker: PhantomData,
53ef40d7f6Sopenharmony_ci            value: UnsafeCell::new(Some(value)),
54ef40d7f6Sopenharmony_ci        }
55ef40d7f6Sopenharmony_ci    }
56ef40d7f6Sopenharmony_ci
57ef40d7f6Sopenharmony_ci    /// Safety: synchronizes with store to value via Release/(Acquire|SeqCst).
58ef40d7f6Sopenharmony_ci    #[inline]
59ef40d7f6Sopenharmony_ci    pub(crate) fn is_initialized(&self) -> bool {
60ef40d7f6Sopenharmony_ci        // An `Acquire` load is enough because that makes all the initialization
61ef40d7f6Sopenharmony_ci        // operations visible to us, and, this being a fast path, weaker
62ef40d7f6Sopenharmony_ci        // ordering helps with performance. This `Acquire` synchronizes with
63ef40d7f6Sopenharmony_ci        // `SeqCst` operations on the slow path.
64ef40d7f6Sopenharmony_ci        self.queue.load(Ordering::Acquire) == COMPLETE_PTR
65ef40d7f6Sopenharmony_ci    }
66ef40d7f6Sopenharmony_ci
67ef40d7f6Sopenharmony_ci    /// Safety: synchronizes with store to value via SeqCst read from state,
68ef40d7f6Sopenharmony_ci    /// writes value only once because we never get to INCOMPLETE state after a
69ef40d7f6Sopenharmony_ci    /// successful write.
70ef40d7f6Sopenharmony_ci    #[cold]
71ef40d7f6Sopenharmony_ci    pub(crate) fn initialize<F, E>(&self, f: F) -> Result<(), E>
72ef40d7f6Sopenharmony_ci    where
73ef40d7f6Sopenharmony_ci        F: FnOnce() -> Result<T, E>,
74ef40d7f6Sopenharmony_ci    {
75ef40d7f6Sopenharmony_ci        let mut f = Some(f);
76ef40d7f6Sopenharmony_ci        let mut res: Result<(), E> = Ok(());
77ef40d7f6Sopenharmony_ci        let slot: *mut Option<T> = self.value.get();
78ef40d7f6Sopenharmony_ci        initialize_or_wait(
79ef40d7f6Sopenharmony_ci            &self.queue,
80ef40d7f6Sopenharmony_ci            Some(&mut || {
81ef40d7f6Sopenharmony_ci                let f = unsafe { crate::unwrap_unchecked(f.take()) };
82ef40d7f6Sopenharmony_ci                match f() {
83ef40d7f6Sopenharmony_ci                    Ok(value) => {
84ef40d7f6Sopenharmony_ci                        unsafe { *slot = Some(value) };
85ef40d7f6Sopenharmony_ci                        true
86ef40d7f6Sopenharmony_ci                    }
87ef40d7f6Sopenharmony_ci                    Err(err) => {
88ef40d7f6Sopenharmony_ci                        res = Err(err);
89ef40d7f6Sopenharmony_ci                        false
90ef40d7f6Sopenharmony_ci                    }
91ef40d7f6Sopenharmony_ci                }
92ef40d7f6Sopenharmony_ci            }),
93ef40d7f6Sopenharmony_ci        );
94ef40d7f6Sopenharmony_ci        res
95ef40d7f6Sopenharmony_ci    }
96ef40d7f6Sopenharmony_ci
97ef40d7f6Sopenharmony_ci    #[cold]
98ef40d7f6Sopenharmony_ci    pub(crate) fn wait(&self) {
99ef40d7f6Sopenharmony_ci        initialize_or_wait(&self.queue, None);
100ef40d7f6Sopenharmony_ci    }
101ef40d7f6Sopenharmony_ci
102ef40d7f6Sopenharmony_ci    /// Get the reference to the underlying value, without checking if the cell
103ef40d7f6Sopenharmony_ci    /// is initialized.
104ef40d7f6Sopenharmony_ci    ///
105ef40d7f6Sopenharmony_ci    /// # Safety
106ef40d7f6Sopenharmony_ci    ///
107ef40d7f6Sopenharmony_ci    /// Caller must ensure that the cell is in initialized state, and that
108ef40d7f6Sopenharmony_ci    /// the contents are acquired by (synchronized to) this thread.
109ef40d7f6Sopenharmony_ci    pub(crate) unsafe fn get_unchecked(&self) -> &T {
110ef40d7f6Sopenharmony_ci        debug_assert!(self.is_initialized());
111ef40d7f6Sopenharmony_ci        let slot = &*self.value.get();
112ef40d7f6Sopenharmony_ci        crate::unwrap_unchecked(slot.as_ref())
113ef40d7f6Sopenharmony_ci    }
114ef40d7f6Sopenharmony_ci
115ef40d7f6Sopenharmony_ci    /// Gets the mutable reference to the underlying value.
116ef40d7f6Sopenharmony_ci    /// Returns `None` if the cell is empty.
117ef40d7f6Sopenharmony_ci    pub(crate) fn get_mut(&mut self) -> Option<&mut T> {
118ef40d7f6Sopenharmony_ci        // Safe b/c we have a unique access.
119ef40d7f6Sopenharmony_ci        unsafe { &mut *self.value.get() }.as_mut()
120ef40d7f6Sopenharmony_ci    }
121ef40d7f6Sopenharmony_ci
122ef40d7f6Sopenharmony_ci    /// Consumes this `OnceCell`, returning the wrapped value.
123ef40d7f6Sopenharmony_ci    /// Returns `None` if the cell was empty.
124ef40d7f6Sopenharmony_ci    #[inline]
125ef40d7f6Sopenharmony_ci    pub(crate) fn into_inner(self) -> Option<T> {
126ef40d7f6Sopenharmony_ci        // Because `into_inner` takes `self` by value, the compiler statically
127ef40d7f6Sopenharmony_ci        // verifies that it is not currently borrowed.
128ef40d7f6Sopenharmony_ci        // So, it is safe to move out `Option<T>`.
129ef40d7f6Sopenharmony_ci        self.value.into_inner()
130ef40d7f6Sopenharmony_ci    }
131ef40d7f6Sopenharmony_ci}
132ef40d7f6Sopenharmony_ci
133ef40d7f6Sopenharmony_ci// Three states that a OnceCell can be in, encoded into the lower bits of `queue` in
134ef40d7f6Sopenharmony_ci// the OnceCell structure.
135ef40d7f6Sopenharmony_ciconst INCOMPLETE: usize = 0x0;
136ef40d7f6Sopenharmony_ciconst RUNNING: usize = 0x1;
137ef40d7f6Sopenharmony_ciconst COMPLETE: usize = 0x2;
138ef40d7f6Sopenharmony_ciconst INCOMPLETE_PTR: *mut Waiter = INCOMPLETE as *mut Waiter;
139ef40d7f6Sopenharmony_ciconst COMPLETE_PTR: *mut Waiter = COMPLETE as *mut Waiter;
140ef40d7f6Sopenharmony_ci
141ef40d7f6Sopenharmony_ci// Mask to learn about the state. All other bits are the queue of waiters if
142ef40d7f6Sopenharmony_ci// this is in the RUNNING state.
143ef40d7f6Sopenharmony_ciconst STATE_MASK: usize = 0x3;
144ef40d7f6Sopenharmony_ci
145ef40d7f6Sopenharmony_ci/// Representation of a node in the linked list of waiters in the RUNNING state.
146ef40d7f6Sopenharmony_ci/// A waiters is stored on the stack of the waiting threads.
147ef40d7f6Sopenharmony_ci#[repr(align(4))] // Ensure the two lower bits are free to use as state bits.
148ef40d7f6Sopenharmony_cistruct Waiter {
149ef40d7f6Sopenharmony_ci    thread: Cell<Option<Thread>>,
150ef40d7f6Sopenharmony_ci    signaled: AtomicBool,
151ef40d7f6Sopenharmony_ci    next: *mut Waiter,
152ef40d7f6Sopenharmony_ci}
153ef40d7f6Sopenharmony_ci
154ef40d7f6Sopenharmony_ci/// Drains and notifies the queue of waiters on drop.
155ef40d7f6Sopenharmony_cistruct Guard<'a> {
156ef40d7f6Sopenharmony_ci    queue: &'a AtomicPtr<Waiter>,
157ef40d7f6Sopenharmony_ci    new_queue: *mut Waiter,
158ef40d7f6Sopenharmony_ci}
159ef40d7f6Sopenharmony_ci
160ef40d7f6Sopenharmony_ciimpl Drop for Guard<'_> {
161ef40d7f6Sopenharmony_ci    fn drop(&mut self) {
162ef40d7f6Sopenharmony_ci        let queue = self.queue.swap(self.new_queue, Ordering::AcqRel);
163ef40d7f6Sopenharmony_ci
164ef40d7f6Sopenharmony_ci        let state = strict::addr(queue) & STATE_MASK;
165ef40d7f6Sopenharmony_ci        assert_eq!(state, RUNNING);
166ef40d7f6Sopenharmony_ci
167ef40d7f6Sopenharmony_ci        unsafe {
168ef40d7f6Sopenharmony_ci            let mut waiter = strict::map_addr(queue, |q| q & !STATE_MASK);
169ef40d7f6Sopenharmony_ci            while !waiter.is_null() {
170ef40d7f6Sopenharmony_ci                let next = (*waiter).next;
171ef40d7f6Sopenharmony_ci                let thread = (*waiter).thread.take().unwrap();
172ef40d7f6Sopenharmony_ci                (*waiter).signaled.store(true, Ordering::Release);
173ef40d7f6Sopenharmony_ci                waiter = next;
174ef40d7f6Sopenharmony_ci                thread.unpark();
175ef40d7f6Sopenharmony_ci            }
176ef40d7f6Sopenharmony_ci        }
177ef40d7f6Sopenharmony_ci    }
178ef40d7f6Sopenharmony_ci}
179ef40d7f6Sopenharmony_ci
180ef40d7f6Sopenharmony_ci// Corresponds to `std::sync::Once::call_inner`.
181ef40d7f6Sopenharmony_ci//
182ef40d7f6Sopenharmony_ci// Originally copied from std, but since modified to remove poisoning and to
183ef40d7f6Sopenharmony_ci// support wait.
184ef40d7f6Sopenharmony_ci//
185ef40d7f6Sopenharmony_ci// Note: this is intentionally monomorphic
186ef40d7f6Sopenharmony_ci#[inline(never)]
187ef40d7f6Sopenharmony_cifn initialize_or_wait(queue: &AtomicPtr<Waiter>, mut init: Option<&mut dyn FnMut() -> bool>) {
188ef40d7f6Sopenharmony_ci    let mut curr_queue = queue.load(Ordering::Acquire);
189ef40d7f6Sopenharmony_ci
190ef40d7f6Sopenharmony_ci    loop {
191ef40d7f6Sopenharmony_ci        let curr_state = strict::addr(curr_queue) & STATE_MASK;
192ef40d7f6Sopenharmony_ci        match (curr_state, &mut init) {
193ef40d7f6Sopenharmony_ci            (COMPLETE, _) => return,
194ef40d7f6Sopenharmony_ci            (INCOMPLETE, Some(init)) => {
195ef40d7f6Sopenharmony_ci                let exchange = queue.compare_exchange(
196ef40d7f6Sopenharmony_ci                    curr_queue,
197ef40d7f6Sopenharmony_ci                    strict::map_addr(curr_queue, |q| (q & !STATE_MASK) | RUNNING),
198ef40d7f6Sopenharmony_ci                    Ordering::Acquire,
199ef40d7f6Sopenharmony_ci                    Ordering::Acquire,
200ef40d7f6Sopenharmony_ci                );
201ef40d7f6Sopenharmony_ci                if let Err(new_queue) = exchange {
202ef40d7f6Sopenharmony_ci                    curr_queue = new_queue;
203ef40d7f6Sopenharmony_ci                    continue;
204ef40d7f6Sopenharmony_ci                }
205ef40d7f6Sopenharmony_ci                let mut guard = Guard { queue, new_queue: INCOMPLETE_PTR };
206ef40d7f6Sopenharmony_ci                if init() {
207ef40d7f6Sopenharmony_ci                    guard.new_queue = COMPLETE_PTR;
208ef40d7f6Sopenharmony_ci                }
209ef40d7f6Sopenharmony_ci                return;
210ef40d7f6Sopenharmony_ci            }
211ef40d7f6Sopenharmony_ci            (INCOMPLETE, None) | (RUNNING, _) => {
212ef40d7f6Sopenharmony_ci                wait(&queue, curr_queue);
213ef40d7f6Sopenharmony_ci                curr_queue = queue.load(Ordering::Acquire);
214ef40d7f6Sopenharmony_ci            }
215ef40d7f6Sopenharmony_ci            _ => debug_assert!(false),
216ef40d7f6Sopenharmony_ci        }
217ef40d7f6Sopenharmony_ci    }
218ef40d7f6Sopenharmony_ci}
219ef40d7f6Sopenharmony_ci
220ef40d7f6Sopenharmony_cifn wait(queue: &AtomicPtr<Waiter>, mut curr_queue: *mut Waiter) {
221ef40d7f6Sopenharmony_ci    let curr_state = strict::addr(curr_queue) & STATE_MASK;
222ef40d7f6Sopenharmony_ci    loop {
223ef40d7f6Sopenharmony_ci        let node = Waiter {
224ef40d7f6Sopenharmony_ci            thread: Cell::new(Some(thread::current())),
225ef40d7f6Sopenharmony_ci            signaled: AtomicBool::new(false),
226ef40d7f6Sopenharmony_ci            next: strict::map_addr(curr_queue, |q| q & !STATE_MASK),
227ef40d7f6Sopenharmony_ci        };
228ef40d7f6Sopenharmony_ci        let me = &node as *const Waiter as *mut Waiter;
229ef40d7f6Sopenharmony_ci
230ef40d7f6Sopenharmony_ci        let exchange = queue.compare_exchange(
231ef40d7f6Sopenharmony_ci            curr_queue,
232ef40d7f6Sopenharmony_ci            strict::map_addr(me, |q| q | curr_state),
233ef40d7f6Sopenharmony_ci            Ordering::Release,
234ef40d7f6Sopenharmony_ci            Ordering::Relaxed,
235ef40d7f6Sopenharmony_ci        );
236ef40d7f6Sopenharmony_ci        if let Err(new_queue) = exchange {
237ef40d7f6Sopenharmony_ci            if strict::addr(new_queue) & STATE_MASK != curr_state {
238ef40d7f6Sopenharmony_ci                return;
239ef40d7f6Sopenharmony_ci            }
240ef40d7f6Sopenharmony_ci            curr_queue = new_queue;
241ef40d7f6Sopenharmony_ci            continue;
242ef40d7f6Sopenharmony_ci        }
243ef40d7f6Sopenharmony_ci
244ef40d7f6Sopenharmony_ci        while !node.signaled.load(Ordering::Acquire) {
245ef40d7f6Sopenharmony_ci            thread::park();
246ef40d7f6Sopenharmony_ci        }
247ef40d7f6Sopenharmony_ci        break;
248ef40d7f6Sopenharmony_ci    }
249ef40d7f6Sopenharmony_ci}
250ef40d7f6Sopenharmony_ci
251ef40d7f6Sopenharmony_ci// Polyfill of strict provenance from https://crates.io/crates/sptr.
252ef40d7f6Sopenharmony_ci//
253ef40d7f6Sopenharmony_ci// Use free-standing function rather than a trait to keep things simple and
254ef40d7f6Sopenharmony_ci// avoid any potential conflicts with future stabile std API.
255ef40d7f6Sopenharmony_cimod strict {
256ef40d7f6Sopenharmony_ci    #[must_use]
257ef40d7f6Sopenharmony_ci    #[inline]
258ef40d7f6Sopenharmony_ci    pub(crate) fn addr<T>(ptr: *mut T) -> usize
259ef40d7f6Sopenharmony_ci    where
260ef40d7f6Sopenharmony_ci        T: Sized,
261ef40d7f6Sopenharmony_ci    {
262ef40d7f6Sopenharmony_ci        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
263ef40d7f6Sopenharmony_ci        // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
264ef40d7f6Sopenharmony_ci        // provenance).
265ef40d7f6Sopenharmony_ci        unsafe { core::mem::transmute(ptr) }
266ef40d7f6Sopenharmony_ci    }
267ef40d7f6Sopenharmony_ci
268ef40d7f6Sopenharmony_ci    #[must_use]
269ef40d7f6Sopenharmony_ci    #[inline]
270ef40d7f6Sopenharmony_ci    pub(crate) fn with_addr<T>(ptr: *mut T, addr: usize) -> *mut T
271ef40d7f6Sopenharmony_ci    where
272ef40d7f6Sopenharmony_ci        T: Sized,
273ef40d7f6Sopenharmony_ci    {
274ef40d7f6Sopenharmony_ci        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
275ef40d7f6Sopenharmony_ci        //
276ef40d7f6Sopenharmony_ci        // In the mean-time, this operation is defined to be "as if" it was
277ef40d7f6Sopenharmony_ci        // a wrapping_offset, so we can emulate it as such. This should properly
278ef40d7f6Sopenharmony_ci        // restore pointer provenance even under today's compiler.
279ef40d7f6Sopenharmony_ci        let self_addr = self::addr(ptr) as isize;
280ef40d7f6Sopenharmony_ci        let dest_addr = addr as isize;
281ef40d7f6Sopenharmony_ci        let offset = dest_addr.wrapping_sub(self_addr);
282ef40d7f6Sopenharmony_ci
283ef40d7f6Sopenharmony_ci        // This is the canonical desugarring of this operation,
284ef40d7f6Sopenharmony_ci        // but `pointer::cast` was only stabilized in 1.38.
285ef40d7f6Sopenharmony_ci        // self.cast::<u8>().wrapping_offset(offset).cast::<T>()
286ef40d7f6Sopenharmony_ci        (ptr as *mut u8).wrapping_offset(offset) as *mut T
287ef40d7f6Sopenharmony_ci    }
288ef40d7f6Sopenharmony_ci
289ef40d7f6Sopenharmony_ci    #[must_use]
290ef40d7f6Sopenharmony_ci    #[inline]
291ef40d7f6Sopenharmony_ci    pub(crate) fn map_addr<T>(ptr: *mut T, f: impl FnOnce(usize) -> usize) -> *mut T
292ef40d7f6Sopenharmony_ci    where
293ef40d7f6Sopenharmony_ci        T: Sized,
294ef40d7f6Sopenharmony_ci    {
295ef40d7f6Sopenharmony_ci        self::with_addr(ptr, f(addr(ptr)))
296ef40d7f6Sopenharmony_ci    }
297ef40d7f6Sopenharmony_ci}
298ef40d7f6Sopenharmony_ci
299ef40d7f6Sopenharmony_ci// These test are snatched from std as well.
300ef40d7f6Sopenharmony_ci#[cfg(test)]
301ef40d7f6Sopenharmony_cimod tests {
302ef40d7f6Sopenharmony_ci    use std::panic;
303ef40d7f6Sopenharmony_ci    use std::{sync::mpsc::channel, thread};
304ef40d7f6Sopenharmony_ci
305ef40d7f6Sopenharmony_ci    use super::OnceCell;
306ef40d7f6Sopenharmony_ci
307ef40d7f6Sopenharmony_ci    impl<T> OnceCell<T> {
308ef40d7f6Sopenharmony_ci        fn init(&self, f: impl FnOnce() -> T) {
309ef40d7f6Sopenharmony_ci            enum Void {}
310ef40d7f6Sopenharmony_ci            let _ = self.initialize(|| Ok::<T, Void>(f()));
311ef40d7f6Sopenharmony_ci        }
312ef40d7f6Sopenharmony_ci    }
313ef40d7f6Sopenharmony_ci
314ef40d7f6Sopenharmony_ci    #[test]
315ef40d7f6Sopenharmony_ci    fn smoke_once() {
316ef40d7f6Sopenharmony_ci        static O: OnceCell<()> = OnceCell::new();
317ef40d7f6Sopenharmony_ci        let mut a = 0;
318ef40d7f6Sopenharmony_ci        O.init(|| a += 1);
319ef40d7f6Sopenharmony_ci        assert_eq!(a, 1);
320ef40d7f6Sopenharmony_ci        O.init(|| a += 1);
321ef40d7f6Sopenharmony_ci        assert_eq!(a, 1);
322ef40d7f6Sopenharmony_ci    }
323ef40d7f6Sopenharmony_ci
324ef40d7f6Sopenharmony_ci    #[test]
325ef40d7f6Sopenharmony_ci    fn stampede_once() {
326ef40d7f6Sopenharmony_ci        static O: OnceCell<()> = OnceCell::new();
327ef40d7f6Sopenharmony_ci        static mut RUN: bool = false;
328ef40d7f6Sopenharmony_ci
329ef40d7f6Sopenharmony_ci        let (tx, rx) = channel();
330ef40d7f6Sopenharmony_ci        for _ in 0..10 {
331ef40d7f6Sopenharmony_ci            let tx = tx.clone();
332ef40d7f6Sopenharmony_ci            thread::spawn(move || {
333ef40d7f6Sopenharmony_ci                for _ in 0..4 {
334ef40d7f6Sopenharmony_ci                    thread::yield_now()
335ef40d7f6Sopenharmony_ci                }
336ef40d7f6Sopenharmony_ci                unsafe {
337ef40d7f6Sopenharmony_ci                    O.init(|| {
338ef40d7f6Sopenharmony_ci                        assert!(!RUN);
339ef40d7f6Sopenharmony_ci                        RUN = true;
340ef40d7f6Sopenharmony_ci                    });
341ef40d7f6Sopenharmony_ci                    assert!(RUN);
342ef40d7f6Sopenharmony_ci                }
343ef40d7f6Sopenharmony_ci                tx.send(()).unwrap();
344ef40d7f6Sopenharmony_ci            });
345ef40d7f6Sopenharmony_ci        }
346ef40d7f6Sopenharmony_ci
347ef40d7f6Sopenharmony_ci        unsafe {
348ef40d7f6Sopenharmony_ci            O.init(|| {
349ef40d7f6Sopenharmony_ci                assert!(!RUN);
350ef40d7f6Sopenharmony_ci                RUN = true;
351ef40d7f6Sopenharmony_ci            });
352ef40d7f6Sopenharmony_ci            assert!(RUN);
353ef40d7f6Sopenharmony_ci        }
354ef40d7f6Sopenharmony_ci
355ef40d7f6Sopenharmony_ci        for _ in 0..10 {
356ef40d7f6Sopenharmony_ci            rx.recv().unwrap();
357ef40d7f6Sopenharmony_ci        }
358ef40d7f6Sopenharmony_ci    }
359ef40d7f6Sopenharmony_ci
360ef40d7f6Sopenharmony_ci    #[test]
361ef40d7f6Sopenharmony_ci    fn poison_bad() {
362ef40d7f6Sopenharmony_ci        static O: OnceCell<()> = OnceCell::new();
363ef40d7f6Sopenharmony_ci
364ef40d7f6Sopenharmony_ci        // poison the once
365ef40d7f6Sopenharmony_ci        let t = panic::catch_unwind(|| {
366ef40d7f6Sopenharmony_ci            O.init(|| panic!());
367ef40d7f6Sopenharmony_ci        });
368ef40d7f6Sopenharmony_ci        assert!(t.is_err());
369ef40d7f6Sopenharmony_ci
370ef40d7f6Sopenharmony_ci        // we can subvert poisoning, however
371ef40d7f6Sopenharmony_ci        let mut called = false;
372ef40d7f6Sopenharmony_ci        O.init(|| {
373ef40d7f6Sopenharmony_ci            called = true;
374ef40d7f6Sopenharmony_ci        });
375ef40d7f6Sopenharmony_ci        assert!(called);
376ef40d7f6Sopenharmony_ci
377ef40d7f6Sopenharmony_ci        // once any success happens, we stop propagating the poison
378ef40d7f6Sopenharmony_ci        O.init(|| {});
379ef40d7f6Sopenharmony_ci    }
380ef40d7f6Sopenharmony_ci
381ef40d7f6Sopenharmony_ci    #[test]
382ef40d7f6Sopenharmony_ci    fn wait_for_force_to_finish() {
383ef40d7f6Sopenharmony_ci        static O: OnceCell<()> = OnceCell::new();
384ef40d7f6Sopenharmony_ci
385ef40d7f6Sopenharmony_ci        // poison the once
386ef40d7f6Sopenharmony_ci        let t = panic::catch_unwind(|| {
387ef40d7f6Sopenharmony_ci            O.init(|| panic!());
388ef40d7f6Sopenharmony_ci        });
389ef40d7f6Sopenharmony_ci        assert!(t.is_err());
390ef40d7f6Sopenharmony_ci
391ef40d7f6Sopenharmony_ci        // make sure someone's waiting inside the once via a force
392ef40d7f6Sopenharmony_ci        let (tx1, rx1) = channel();
393ef40d7f6Sopenharmony_ci        let (tx2, rx2) = channel();
394ef40d7f6Sopenharmony_ci        let t1 = thread::spawn(move || {
395ef40d7f6Sopenharmony_ci            O.init(|| {
396ef40d7f6Sopenharmony_ci                tx1.send(()).unwrap();
397ef40d7f6Sopenharmony_ci                rx2.recv().unwrap();
398ef40d7f6Sopenharmony_ci            });
399ef40d7f6Sopenharmony_ci        });
400ef40d7f6Sopenharmony_ci
401ef40d7f6Sopenharmony_ci        rx1.recv().unwrap();
402ef40d7f6Sopenharmony_ci
403ef40d7f6Sopenharmony_ci        // put another waiter on the once
404ef40d7f6Sopenharmony_ci        let t2 = thread::spawn(|| {
405ef40d7f6Sopenharmony_ci            let mut called = false;
406ef40d7f6Sopenharmony_ci            O.init(|| {
407ef40d7f6Sopenharmony_ci                called = true;
408ef40d7f6Sopenharmony_ci            });
409ef40d7f6Sopenharmony_ci            assert!(!called);
410ef40d7f6Sopenharmony_ci        });
411ef40d7f6Sopenharmony_ci
412ef40d7f6Sopenharmony_ci        tx2.send(()).unwrap();
413ef40d7f6Sopenharmony_ci
414ef40d7f6Sopenharmony_ci        assert!(t1.join().is_ok());
415ef40d7f6Sopenharmony_ci        assert!(t2.join().is_ok());
416ef40d7f6Sopenharmony_ci    }
417ef40d7f6Sopenharmony_ci
418ef40d7f6Sopenharmony_ci    #[test]
419ef40d7f6Sopenharmony_ci    #[cfg(target_pointer_width = "64")]
420ef40d7f6Sopenharmony_ci    fn test_size() {
421ef40d7f6Sopenharmony_ci        use std::mem::size_of;
422ef40d7f6Sopenharmony_ci
423ef40d7f6Sopenharmony_ci        assert_eq!(size_of::<OnceCell<u32>>(), 4 * size_of::<u32>());
424ef40d7f6Sopenharmony_ci    }
425ef40d7f6Sopenharmony_ci}
426