use core::{
cell::UnsafeCell,
ptr::{
self,
NonNull,
},
sync::atomic::{
AtomicBool,
AtomicPtr,
Ordering,
},
};
pub trait Cache {
fn resolve(&self, _: impl FnOnce() -> NonNull<()>) -> NonNull<()>;
}
pub struct StaticCache {
value: UnsafeCell<Option<NonNull<()>>>,
}
unsafe impl Sync for StaticCache {}
impl StaticCache {
pub const fn new() -> Self {
Self {
value: UnsafeCell::new(None),
}
}
}
impl Default for StaticCache {
fn default() -> Self {
Self::new()
}
}
impl Cache for StaticCache {
fn resolve(&self, resolver: impl FnOnce() -> NonNull<()>) -> NonNull<()> {
let value = unsafe { &mut *self.value.get() };
*value.get_or_insert_with(resolver)
}
}
pub struct StaticAtomicCache {
value: AtomicPtr<()>,
resolve_lock: AtomicBool,
}
impl StaticAtomicCache {
pub const fn new() -> Self {
Self {
value: AtomicPtr::new(ptr::null_mut()),
resolve_lock: AtomicBool::new(false),
}
}
}
impl Default for StaticAtomicCache {
fn default() -> Self {
Self::new()
}
}
impl Cache for StaticAtomicCache {
fn resolve(&self, resolver: impl FnOnce() -> NonNull<()>) -> NonNull<()> {
loop {
let value = self.value.load(Ordering::Relaxed);
if let Some(value) = NonNull::new(value) {
return value;
}
if self
.resolve_lock
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_err()
{
continue;
}
let result = resolver();
self.value.store(result.as_ptr(), Ordering::Relaxed);
return result;
}
}
}
pub struct NoCache;
impl NoCache {
pub const fn new() -> Self {
Self
}
}
impl Default for NoCache {
fn default() -> Self {
Self::new()
}
}
impl Cache for NoCache {
fn resolve(&self, resolver: impl FnOnce() -> NonNull<()>) -> NonNull<()> {
resolver()
}
}