use super::{Recorder, SetRecorderError};
use std::{
cell::UnsafeCell,
sync::atomic::{AtomicUsize, Ordering},
};
const UNINITIALIZED: usize = 0;
const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;
#[derive(Debug)]
pub struct RecorderOnceCell {
recorder: UnsafeCell<Option<&'static dyn Recorder>>,
state: AtomicUsize,
}
impl RecorderOnceCell {
pub const fn new() -> Self {
Self { recorder: UnsafeCell::new(None), state: AtomicUsize::new(UNINITIALIZED) }
}
pub fn set<R>(&self, recorder: R) -> Result<(), SetRecorderError<R>>
where
R: Recorder + 'static,
{
match self.state.compare_exchange(
UNINITIALIZED,
INITIALIZING,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(UNINITIALIZED) => {
unsafe {
self.recorder.get().write(Some(Box::leak(Box::new(recorder))));
}
self.state.store(INITIALIZED, Ordering::Release);
Ok(())
}
_ => Err(SetRecorderError(recorder)),
}
}
pub fn try_load(&self) -> Option<&'static dyn Recorder> {
if self.state.load(Ordering::Acquire) != INITIALIZED {
None
} else {
unsafe { self.recorder.get().read() }
}
}
}
unsafe impl Send for RecorderOnceCell {}
unsafe impl Sync for RecorderOnceCell {}