taskvisor 0.9.0

In-process Tokio task supervisor: one job per key with queue/replace/reject admission, retries, graceful shutdown, and reliable final outcomes
Documentation
//! Maps internal slot state to the public snapshot API.
//!
//! [`SupervisorHandle::controller_snapshot`](crate::SupervisorHandle::controller_snapshot) reads the configured controller through this boundary.
//! Public values expose no slot locks or private state-machine phases.

use std::sync::Arc;
use std::time::Duration;

use crate::controller::{ControllerSnapshot, SlotStatusKind, SlotView};

use super::{Controller, state::SlotPhase};

impl Controller {
    pub(crate) async fn snapshot(&self) -> ControllerSnapshot {
        let tracked_slots: Vec<_> = {
            let state = self.state();
            state
                .slots
                .iter()
                .map(|(key, slot)| (Arc::clone(key), Arc::clone(slot)))
                .collect()
        };

        let mut slots = Vec::with_capacity(tracked_slots.len());
        for (key, slot_arc) in tracked_slots {
            let slot = slot_arc.lock().await;
            let phase = slot.phase();
            let (status, status_for) = match phase {
                SlotPhase::Idle => (SlotStatusKind::Idle, Duration::ZERO),
                SlotPhase::Admitting { since, .. } => (SlotStatusKind::Admitting, since.elapsed()),
                SlotPhase::Running { started_at, .. } => {
                    (SlotStatusKind::Running, started_at.elapsed())
                }
                SlotPhase::CancelPendingAdmission { requested_at, .. }
                | SlotPhase::Terminating { requested_at, .. } => {
                    (SlotStatusKind::Terminating, requested_at.elapsed())
                }
            };

            slots.push(SlotView {
                slot: Arc::clone(&key),
                status,
                owner_id: phase.owner_id(),
                queue_depth: slot.queue.len(),
                status_for,
            });
        }

        slots.sort_by(|a, b| a.slot.cmp(&b.slot));
        ControllerSnapshot { slots }
    }
}