use std::sync::{
Arc, Mutex, MutexGuard,
atomic::{AtomicBool, Ordering},
};
use crate::RunError;
pub(crate) enum MintPosture {
Multiple,
FreshOnly(AtomicBool),
}
impl MintPosture {
pub(crate) const fn new(fresh_only: bool) -> Self {
if fresh_only {
Self::FreshOnly(AtomicBool::new(false))
} else {
Self::Multiple
}
}
pub(crate) fn claim(&self) -> Result<(), RunError> {
match self {
Self::Multiple => Ok(()),
Self::FreshOnly(claimed) if !claimed.swap(true, Ordering::Relaxed) => Ok(()),
Self::FreshOnly(_) => Err(RunError::FreshOnlyRunAlreadyAttempted),
}
}
pub(crate) const fn is_fresh_only(&self) -> bool {
matches!(self, Self::FreshOnly(_))
}
}
impl std::fmt::Debug for MintPosture {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MintPosture")
.field("fresh_only", &self.is_fresh_only())
.finish()
}
}
pub(crate) struct ReuseLifecycle {
state: Arc<Mutex<ReuseState>>,
}
#[derive(Debug, Default)]
struct ReuseState {
bound: bool,
poisoned: bool,
sealed: bool,
}
impl ReuseLifecycle {
pub(crate) fn unbound() -> Self {
Self {
state: Arc::new(Mutex::new(ReuseState::default())),
}
}
pub(crate) fn require_unbound(&self) -> Result<(), RunError> {
let state = self.lock();
if state.poisoned {
return Err(RunError::ReusableWorkspaceRawAccess);
}
if state.bound {
return Err(RunError::ReusableWorkspaceAlreadyBound);
}
Ok(())
}
pub(crate) fn mark_bound(&self) -> Result<(), RunError> {
let mut state = self.lock();
if state.poisoned {
return Err(RunError::ReusableWorkspaceRawAccess);
}
if state.bound {
return Err(RunError::ReusableWorkspaceAlreadyBound);
}
state.bound = true;
Ok(())
}
pub(crate) fn lease_run(&self) -> Result<ReuseLease, RunError> {
let state = self.lock();
if !state.bound {
return Err(RunError::ReusableWorkspaceToolsUnbound);
}
if state.poisoned {
return Err(RunError::ReusableWorkspaceRawAccess);
}
if state.sealed {
return Err(RunError::ReusableWorkspaceSealed);
}
Ok(ReuseLease {
state: Arc::clone(&self.state),
})
}
pub(crate) fn poison(&self) {
self.lock().poisoned = true;
}
pub(crate) fn seal_for_rebuild(&self) -> Result<(), RunError> {
let mut state = self.lock();
state.sealed = true;
if !state.bound {
return Err(RunError::ReusableWorkspaceToolsUnbound);
}
if state.poisoned {
return Err(RunError::ReusableWorkspaceRawAccess);
}
let leases = Arc::strong_count(&self.state).saturating_sub(1);
if leases == 0 {
Ok(())
} else {
Err(RunError::ReusableWorkspaceOutstanding { leases })
}
}
fn lock(&self) -> MutexGuard<'_, ReuseState> {
self.state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
}
#[derive(Clone)]
pub(crate) struct ReuseLease {
state: Arc<Mutex<ReuseState>>,
}
impl ReuseLease {
pub(crate) fn poison(&self) {
self.state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.poisoned = true;
}
}