Files
RedBear-OS/src/sync/once.rs
T
jD91mZM2 b8c50c7c64 Format
2019-08-04 19:05:45 +02:00

70 lines
2.2 KiB
Rust

use super::{AtomicLock, AttemptStatus};
use crate::platform::types::*;
use core::{cell::UnsafeCell, mem::MaybeUninit, sync::atomic::Ordering::SeqCst};
const UNINITIALIZED: c_int = 0;
const INITIALIZING: c_int = 1;
const WAITING: c_int = 2;
const INITIALIZED: c_int = 3;
pub struct Once<T> {
status: AtomicLock,
data: UnsafeCell<MaybeUninit<T>>,
}
unsafe impl<T: Send> Send for Once<T> {}
unsafe impl<T: Send> Sync for Once<T> {}
impl<T> Once<T> {
pub const fn new() -> Self {
Self {
status: AtomicLock::new(UNINITIALIZED),
data: UnsafeCell::new(MaybeUninit::uninit()),
}
}
pub fn call_once<F>(&self, f: F) -> &mut T
where
F: FnOnce() -> T,
{
match self
.status
.compare_and_swap(UNINITIALIZED, INITIALIZING, SeqCst)
{
UNINITIALIZED => {
// We now have a lock, let's initiate things!
let ret = unsafe { &mut *self.data.get() }.write(f());
// Mark the data as initialized
if self.status.swap(INITIALIZED, SeqCst) == WAITING {
// At least one thread is waiting on this to finish
self.status.notify_all();
}
}
INITIALIZING | WAITING => self.status.wait_until(
|lock| match lock.load(SeqCst) {
WAITING => AttemptStatus::Waiting,
INITIALIZED => AttemptStatus::Desired,
_ => AttemptStatus::Other,
},
|lock| match lock
.compare_exchange_weak(INITIALIZING, WAITING, SeqCst, SeqCst)
.unwrap_or_else(|e| e)
{
WAITING => AttemptStatus::Waiting,
INITIALIZED => AttemptStatus::Desired,
_ => AttemptStatus::Other,
},
WAITING,
),
INITIALIZED => (),
_ => unreachable!("invalid state for Once<T>"),
}
// At this point the data must be initialized!
unsafe { &mut *(&mut *self.data.get()).as_mut_ptr() }
}
}
impl<T> Default for Once<T> {
fn default() -> Self {
Self::new()
}
}