use std::sync::Arc;
use crate::MetricError;
use crate::internal::OperationLifecycle;
use crate::internal::UpdateGuard;
use crate::internal::operation_gate::GateLifecycle;
use crate::internal::operation_gate::OperationGate;
use crate::internal::operation_gate::StdScheduler;
pub(crate) struct OperationState {
gate: OperationGate<
std::sync::atomic::AtomicU8,
std::sync::atomic::AtomicUsize,
StdScheduler,
>,
}
impl OperationState {
pub(crate) fn new() -> Arc<Self> {
Arc::new(Self {
gate: OperationGate::new(),
})
}
#[inline(never)]
pub(crate) fn lifecycle(&self) -> OperationLifecycle {
match self.gate.lifecycle() {
GateLifecycle::Open => OperationLifecycle::Open,
GateLifecycle::Finishing => OperationLifecycle::Finishing,
GateLifecycle::Closed => OperationLifecycle::Closed,
}
}
#[inline]
pub(crate) fn enter_update<'state>(
&'state self,
metric_id: &str,
) -> Result<UpdateGuard<'state>, MetricError> {
match self.gate.enter_update() {
Ok(()) => Ok(UpdateGuard::new(self)),
Err(state) => Err(MetricError::OperationNotOpen {
metric_id: metric_id.into(),
state: operation_lifecycle(state),
}),
}
}
pub(crate) fn begin_finish(&self) -> FinishGuard<'_> {
assert!(
self.gate.try_begin_finish(),
"Progress owns the only lifecycle transition authority",
);
FinishGuard { state: self }
}
#[inline]
pub(crate) fn close(&self) {
self.gate.close();
}
#[inline]
pub(crate) fn leave_update(&self) {
self.gate.leave_update();
}
}
pub(crate) struct FinishGuard<'state> {
state: &'state OperationState,
}
impl FinishGuard<'_> {
pub(crate) fn reopen(self) {
self.state.gate.reopen();
}
pub(crate) fn close(self) {
self.state.close();
}
}
impl Drop for FinishGuard<'_> {
#[inline(never)]
fn drop(&mut self) {
if self.state.lifecycle() == OperationLifecycle::Finishing {
self.state.close();
}
}
}
#[inline(never)]
fn operation_lifecycle(state: GateLifecycle) -> OperationLifecycle {
match state {
GateLifecycle::Open => OperationLifecycle::Open,
GateLifecycle::Finishing => OperationLifecycle::Finishing,
GateLifecycle::Closed => OperationLifecycle::Closed,
}
}
#[cfg(coverage)]
pub(crate) fn __coverage_operation_state() {
let state = OperationState::new();
let guard = state.begin_finish();
assert_eq!(state.lifecycle(), OperationLifecycle::Finishing);
assert!(matches!(
state.enter_update("coverage"),
Err(MetricError::OperationNotOpen {
state: OperationLifecycle::Finishing,
..
})
));
drop(guard);
assert_eq!(state.lifecycle(), OperationLifecycle::Closed);
}
#[cfg(coverage)]
#[doc(hidden)]
pub fn __coverage_internal() {
super::operation_gate::__coverage_operation_gate();
__coverage_operation_state();
}