1 use super::{AtomicLock, AttemptStatus}; 2 3 use core::{cell::UnsafeCell, mem::MaybeUninit, sync::atomic::Ordering::SeqCst}; 4 5 const UNINITIALIZED: ::c_int = 0; 6 const INITIALIZING: ::c_int = 1; 7 const WAITING: ::c_int = 2; 8 const INITIALIZED: ::c_int = 3; 9 10 pub struct Once<T> { 11 status: AtomicLock, 12 data: UnsafeCell<MaybeUninit<T>>, 13 } 14 unsafe impl<T: Send> Send for Once<T> {} 15 unsafe impl<T: Send> Sync for Once<T> {} 16 impl<T> Once<T> { 17 pub const fn new() -> Self { 18 Self { 19 status: AtomicLock::new(UNINITIALIZED), 20 data: UnsafeCell::new(MaybeUninit::uninit()), 21 } 22 } 23 pub fn call_once<F>(&self, f: F) -> &mut T 24 where 25 F: FnOnce() -> T, 26 { 27 match self 28 .status 29 .compare_and_swap(UNINITIALIZED, INITIALIZING, SeqCst) 30 { 31 UNINITIALIZED => { 32 // We now have a lock, let's initiate things! 33 let _ret = unsafe { &mut *self.data.get() }.write(f()); 34 35 // Mark the data as initialized 36 if self.status.swap(INITIALIZED, SeqCst) == WAITING { 37 // At least one thread is waiting on this to finish 38 self.status.notify_all(); 39 } 40 } 41 INITIALIZING | WAITING => self.status.wait_until( 42 |lock| match lock.load(SeqCst) { 43 WAITING => AttemptStatus::Waiting, 44 INITIALIZED => AttemptStatus::Desired, 45 _ => AttemptStatus::Other, 46 }, 47 |lock| match lock 48 .compare_exchange_weak(INITIALIZING, WAITING, SeqCst, SeqCst) 49 .unwrap_or_else(|e| e) 50 { 51 WAITING => AttemptStatus::Waiting, 52 INITIALIZED => AttemptStatus::Desired, 53 _ => AttemptStatus::Other, 54 }, 55 WAITING, 56 ), 57 INITIALIZED => (), 58 _ => unreachable!("invalid state for Once<T>"), 59 } 60 61 // At this point the data must be initialized! 62 unsafe { &mut *(&mut *self.data.get()).as_mut_ptr() } 63 } 64 } 65 impl<T> Default for Once<T> { 66 fn default() -> Self { 67 Self::new() 68 } 69 } 70