xref: /drstd/dlibc/src/unix/sync/once.rs (revision 69bbf99969c635b975633fbae5786a97353ca9ae)
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