use super::singleton::Singleton;
use std::thread::{self, ThreadId};
#[doc(hidden)]
pub struct PreemptiveInner<T> {
thread_id: ThreadId,
data: T,
}
unsafe impl<T> Sync for PreemptiveInner<T> {}
pub struct PreemptiveSingleton<T: Send> {
#[doc(hidden)]
pub singleton: Singleton<PreemptiveInner<T>>,
}
#[macro_export]
macro_rules! make_preemptive_singleton {
() => {
$crate::PreemptiveSingleton {
singleton: make_singleton!()
}
};
}
impl<T: Send> PreemptiveSingleton<T> {
#[cfg(feature = "const_fn")]
pub const fn new() -> Self {
make_preemptive_singleton!()
}
#[cfg(not(feature = "const_fn"))]
pub fn new() -> Self {
make_preemptive_singleton!()
}
pub fn get(&self) -> &T
where
T: Default,
{
self.get_or_insert_with(<T as Default>::default)
}
pub fn get_opt(&self) -> Option<&T> {
if let Some(pre_ref) = self.singleton.get_opt() {
if pre_ref.thread_id == thread::current().id() {
return Some(&pre_ref.data);
}
}
return None;
}
pub fn get_or_insert_with<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
{
let pre_ref = self.singleton.get_or_insert_with(move || PreemptiveInner {
thread_id: thread::current().id(),
data: f(),
});
if pre_ref.thread_id == thread::current().id() {
return &pre_ref.data;
}
Self::error_occupied();
unreachable!()
}
fn error_occupied() {
panic!("singleton: trying to access an occupied preemptive singleton from another thread. ");
}
pub unsafe fn finalize(&self) {
if self.singleton.get_opt().is_some() {
return self.singleton.finalize();
}
}
}
impl<T: Send> Drop for PreemptiveSingleton<T> {
fn drop(&mut self) {
unsafe {
self.finalize();
}
}
}