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