use self::cell::RecorderOnceCell;
use crate::{Counter, Gauge, Histogram, Key, KeyName, SharedString, Unit};
use core::fmt;
mod cell {
use super::{Recorder, SetRecorderError};
use std::cell::UnsafeCell;
use std::sync::atomic::{AtomicUsize, Ordering};
#[allow(clippy::declare_interior_mutable_const)]
pub const INIT: RecorderOnceCell = RecorderOnceCell {
recorder: UnsafeCell::new(None),
state: AtomicUsize::new(UNINITIALIZED),
};
pub struct RecorderOnceCell {
recorder: UnsafeCell<Option<&'static dyn Recorder>>,
state: AtomicUsize,
}
const UNINITIALIZED: usize = 0;
const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;
impl RecorderOnceCell {
#[cfg(atomic_cas)]
pub fn set(&self, recorder: &'static dyn Recorder) -> Result<(), SetRecorderError> {
match self.state.compare_exchange(
UNINITIALIZED,
INITIALIZING,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(UNINITIALIZED) => {
unsafe {
self.recorder.get().write(Some(recorder));
}
self.state.store(INITIALIZED, Ordering::Release);
Ok(())
}
_ => Err(SetRecorderError(())),
}
}
pub unsafe fn clear(&self) {
self.state.store(UNINITIALIZED, Ordering::Relaxed);
}
pub fn try_load(&self) -> Option<&'static dyn Recorder> {
if self.state.load(Ordering::Acquire) != INITIALIZED {
None
} else {
unsafe { self.recorder.get().read() }
}
}
pub unsafe fn set_racy(
&self,
recorder: &'static dyn Recorder,
) -> Result<(), SetRecorderError> {
match self.state.load(Ordering::Relaxed) {
UNINITIALIZED => {
self.recorder.get().write(Some(recorder));
self.state.store(INITIALIZED, Ordering::Release);
Ok(())
}
INITIALIZING => {
unreachable!(
"set_recorder_racy must not be used with other initialization functions"
)
}
_ => Err(SetRecorderError(())),
}
}
}
unsafe impl Send for RecorderOnceCell {}
unsafe impl Sync for RecorderOnceCell {}
}
static RECORDER: RecorderOnceCell = cell::INIT;
static SET_RECORDER_ERROR: &str =
"attempted to set a recorder after the metrics system was already initialized";
pub trait Recorder {
fn describe_counter(&self, key: KeyName, unit: Option<Unit>, description: SharedString);
fn describe_gauge(&self, key: KeyName, unit: Option<Unit>, description: SharedString);
fn describe_histogram(&self, key: KeyName, unit: Option<Unit>, description: SharedString);
fn register_counter(&self, key: &Key) -> Counter;
fn register_gauge(&self, key: &Key) -> Gauge;
fn register_histogram(&self, key: &Key) -> Histogram;
}
pub struct NoopRecorder;
impl Recorder for NoopRecorder {
fn describe_counter(&self, _key: KeyName, _unit: Option<Unit>, _description: SharedString) {}
fn describe_gauge(&self, _key: KeyName, _unit: Option<Unit>, _description: SharedString) {}
fn describe_histogram(&self, _key: KeyName, _unit: Option<Unit>, _description: SharedString) {}
fn register_counter(&self, _key: &Key) -> Counter {
Counter::noop()
}
fn register_gauge(&self, _key: &Key) -> Gauge {
Gauge::noop()
}
fn register_histogram(&self, _key: &Key) -> Histogram {
Histogram::noop()
}
}
#[cfg(atomic_cas)]
pub fn set_recorder(recorder: &'static dyn Recorder) -> Result<(), SetRecorderError> {
RECORDER.set(recorder)
}
#[cfg(atomic_cas)]
pub fn set_boxed_recorder(recorder: Box<dyn Recorder>) -> Result<(), SetRecorderError> {
RECORDER.set(Box::leak(recorder))
}
pub unsafe fn set_recorder_racy(recorder: &'static dyn Recorder) -> Result<(), SetRecorderError> {
RECORDER.set_racy(recorder)
}
#[doc(hidden)]
pub unsafe fn clear_recorder() {
RECORDER.clear();
}
#[derive(Debug)]
pub struct SetRecorderError(());
impl fmt::Display for SetRecorderError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(SET_RECORDER_ERROR)
}
}
impl std::error::Error for SetRecorderError {
fn description(&self) -> &str {
SET_RECORDER_ERROR
}
}
pub fn recorder() -> &'static dyn Recorder {
static NOOP: NoopRecorder = NoopRecorder;
try_recorder().unwrap_or(&NOOP)
}
pub fn try_recorder() -> Option<&'static dyn Recorder> {
RECORDER.try_load()
}