atagen

malice

public
Star0
Watch0
ded870272f400fd76a356bdde46a3e03d9de8e7c
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: crates/malice-static/src/idle.rs

973f8fbdaf80atagen2026-09-28 09:10 UTC1
//! Idle-exit tracking for the socket-activated static-only server: counts
973f8fbdaf80atagen2026-09-28 09:10 UTC2
//! in-flight requests and the time of the last one, so the process can shut
973f8fbdaf80atagen2026-09-28 09:10 UTC3
//! itself down once nothing has happened for `MALICE_IDLE_EXIT_SECS`.
973f8fbdaf80atagen2026-09-28 09:10 UTC4
973f8fbdaf80atagen2026-09-28 09:10 UTC5
use std::{
973f8fbdaf80atagen2026-09-28 09:10 UTC6
    sync::{
973f8fbdaf80atagen2026-09-28 09:10 UTC7
        Arc,
973f8fbdaf80atagen2026-09-28 09:10 UTC8
        atomic::{AtomicUsize, Ordering},
973f8fbdaf80atagen2026-09-28 09:10 UTC9
    },
973f8fbdaf80atagen2026-09-28 09:10 UTC10
    time::{Duration, Instant},
973f8fbdaf80atagen2026-09-28 09:10 UTC11
};
973f8fbdaf80atagen2026-09-28 09:10 UTC12
973f8fbdaf80atagen2026-09-28 09:10 UTC13
use axum::{extract::State, middleware::Next};
973f8fbdaf80atagen2026-09-28 09:10 UTC14
973f8fbdaf80atagen2026-09-28 09:10 UTC15
#[derive(Clone)]
973f8fbdaf80atagen2026-09-28 09:10 UTC16
pub struct IdleTracker {
973f8fbdaf80atagen2026-09-28 09:10 UTC17
    in_flight: Arc<AtomicUsize>,
973f8fbdaf80atagen2026-09-28 09:10 UTC18
    last_activity: Arc<std::sync::Mutex<Instant>>,
973f8fbdaf80atagen2026-09-28 09:10 UTC19
}
973f8fbdaf80atagen2026-09-28 09:10 UTC20
973f8fbdaf80atagen2026-09-28 09:10 UTC21
impl IdleTracker {
973f8fbdaf80atagen2026-09-28 09:10 UTC22
    pub fn new() -> Self {
973f8fbdaf80atagen2026-09-28 09:10 UTC23
        Self {
973f8fbdaf80atagen2026-09-28 09:10 UTC24
            in_flight: Arc::new(AtomicUsize::new(0)),
973f8fbdaf80atagen2026-09-28 09:10 UTC25
            last_activity: Arc::new(std::sync::Mutex::new(Instant::now())),
973f8fbdaf80atagen2026-09-28 09:10 UTC26
        }
973f8fbdaf80atagen2026-09-28 09:10 UTC27
    }
973f8fbdaf80atagen2026-09-28 09:10 UTC28
973f8fbdaf80atagen2026-09-28 09:10 UTC29
    fn touch(&self) {
973f8fbdaf80atagen2026-09-28 09:10 UTC30
        *self
973f8fbdaf80atagen2026-09-28 09:10 UTC31
            .last_activity
973f8fbdaf80atagen2026-09-28 09:10 UTC32
            .lock()
973f8fbdaf80atagen2026-09-28 09:10 UTC33
            .expect("idle tracker mutex poisoned") = Instant::now();
973f8fbdaf80atagen2026-09-28 09:10 UTC34
    }
973f8fbdaf80atagen2026-09-28 09:10 UTC35
973f8fbdaf80atagen2026-09-28 09:10 UTC36
    /// Resolves once no request has started or finished, and none is still
973f8fbdaf80atagen2026-09-28 09:10 UTC37
    /// in flight, for at least `idle`.
973f8fbdaf80atagen2026-09-28 09:10 UTC38
    pub async fn wait_idle(&self, idle: Duration) {
973f8fbdaf80atagen2026-09-28 09:10 UTC39
        loop {
973f8fbdaf80atagen2026-09-28 09:10 UTC40
            tokio::time::sleep(idle.min(Duration::from_millis(200))).await;
973f8fbdaf80atagen2026-09-28 09:10 UTC41
            if self.in_flight.load(Ordering::SeqCst) != 0 {
973f8fbdaf80atagen2026-09-28 09:10 UTC42
                continue;
973f8fbdaf80atagen2026-09-28 09:10 UTC43
            }
973f8fbdaf80atagen2026-09-28 09:10 UTC44
            let elapsed = self
973f8fbdaf80atagen2026-09-28 09:10 UTC45
                .last_activity
973f8fbdaf80atagen2026-09-28 09:10 UTC46
                .lock()
973f8fbdaf80atagen2026-09-28 09:10 UTC47
                .expect("idle tracker mutex poisoned")
973f8fbdaf80atagen2026-09-28 09:10 UTC48
                .elapsed();
973f8fbdaf80atagen2026-09-28 09:10 UTC49
            if elapsed >= idle {
973f8fbdaf80atagen2026-09-28 09:10 UTC50
                return;
973f8fbdaf80atagen2026-09-28 09:10 UTC51
            }
973f8fbdaf80atagen2026-09-28 09:10 UTC52
        }
973f8fbdaf80atagen2026-09-28 09:10 UTC53
    }
973f8fbdaf80atagen2026-09-28 09:10 UTC54
}
973f8fbdaf80atagen2026-09-28 09:10 UTC55
973f8fbdaf80atagen2026-09-28 09:10 UTC56
impl Default for IdleTracker {
973f8fbdaf80atagen2026-09-28 09:10 UTC57
    fn default() -> Self {
973f8fbdaf80atagen2026-09-28 09:10 UTC58
        Self::new()
973f8fbdaf80atagen2026-09-28 09:10 UTC59
    }
973f8fbdaf80atagen2026-09-28 09:10 UTC60
}
973f8fbdaf80atagen2026-09-28 09:10 UTC61
973f8fbdaf80atagen2026-09-28 09:10 UTC62
pub async fn track(
973f8fbdaf80atagen2026-09-28 09:10 UTC63
    State(idle): State<IdleTracker>,
973f8fbdaf80atagen2026-09-28 09:10 UTC64
    request: axum::extract::Request,
973f8fbdaf80atagen2026-09-28 09:10 UTC65
    next: Next,
973f8fbdaf80atagen2026-09-28 09:10 UTC66
) -> axum::response::Response {
973f8fbdaf80atagen2026-09-28 09:10 UTC67
    idle.in_flight.fetch_add(1, Ordering::SeqCst);
973f8fbdaf80atagen2026-09-28 09:10 UTC68
    idle.touch();
973f8fbdaf80atagen2026-09-28 09:10 UTC69
    let response = next.run(request).await;
973f8fbdaf80atagen2026-09-28 09:10 UTC70
    idle.touch();
973f8fbdaf80atagen2026-09-28 09:10 UTC71
    idle.in_flight.fetch_sub(1, Ordering::SeqCst);
973f8fbdaf80atagen2026-09-28 09:10 UTC72
    response
973f8fbdaf80atagen2026-09-28 09:10 UTC73
}