use base::prelude::*;
use atomic::{Atomic};
const UNINITIALIZED: u8 = 0;
const WORKING: u8 = 1;
const INITIALIZED: u8 = 2;
pub enum OnceStatus {
Uninitialized,
Working,
Initialized,
}
#[repr(C)]
pub struct Once {
status: Atomic<u8>,
}
impl<'a> Once {
pub const fn new() -> Once {
Once { status: Atomic::new(UNINITIALIZED) }
}
pub fn status(&self) -> OnceStatus {
match self.status.load_unordered() {
UNINITIALIZED => OnceStatus::Uninitialized,
WORKING => OnceStatus::Working,
_ => OnceStatus::Initialized,
}
}
pub fn once<F, T>(&self, f: F) -> Option<T>
where F: FnOnce() -> T,
{
let mut status = self.status.load_acquire();
if status == INITIALIZED {
return None;
}
if status == UNINITIALIZED {
status = self.status.compare_exchange(UNINITIALIZED, WORKING);
}
if status == UNINITIALIZED {
let res = f();
self.status.store_release(INITIALIZED);
return Some(res);
}
while status == WORKING {
status = self.status.load_acquire();
}
None
}
}