atagen

malice

public
Star0
Watch0
5b0525b663533f6b8aef29faefd47305462f4f0f
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git
crates/malice-static/src/idle.rsRust73 lines1.9 KB
1
//! Idle-exit tracking for the socket-activated static-only server: counts
2
//! in-flight requests and the time of the last one, so the process can shut
3
//! itself down once nothing has happened for `MALICE_IDLE_EXIT_SECS`.
4
5
use std::{
6
    sync::{
7
        Arc,
8
        atomic::{AtomicUsize, Ordering},
9
    },
10
    time::{Duration, Instant},
11
};
12
13
use axum::{extract::State, middleware::Next};
14
15
#[derive(Clone)]
16
pub struct IdleTracker {
17
    in_flight: Arc<AtomicUsize>,
18
    last_activity: Arc<std::sync::Mutex<Instant>>,
19
}
20
21
impl IdleTracker {
22
    pub fn new() -> Self {
23
        Self {
24
            in_flight: Arc::new(AtomicUsize::new(0)),
25
            last_activity: Arc::new(std::sync::Mutex::new(Instant::now())),
26
        }
27
    }
28
29
    fn touch(&self) {
30
        *self
31
            .last_activity
32
            .lock()
33
            .expect("idle tracker mutex poisoned") = Instant::now();
34
    }
35
36
    /// Resolves once no request has started or finished, and none is still
37
    /// in flight, for at least `idle`.
38
    pub async fn wait_idle(&self, idle: Duration) {
39
        loop {
40
            tokio::time::sleep(idle.min(Duration::from_millis(200))).await;
41
            if self.in_flight.load(Ordering::SeqCst) != 0 {
42
                continue;
43
            }
44
            let elapsed = self
45
                .last_activity
46
                .lock()
47
                .expect("idle tracker mutex poisoned")
48
                .elapsed();
49
            if elapsed >= idle {
50
                return;
51
            }
52
        }
53
    }
54
}
55
56
impl Default for IdleTracker {
57
    fn default() -> Self {
58
        Self::new()
59
    }
60
}
61
62
pub async fn track(
63
    State(idle): State<IdleTracker>,
64
    request: axum::extract::Request,
65
    next: Next,
66
) -> axum::response::Response {
67
    idle.in_flight.fetch_add(1, Ordering::SeqCst);
68
    idle.touch();
69
    let response = next.run(request).await;
70
    idle.touch();
71
    idle.in_flight.fetch_sub(1, Ordering::SeqCst);
72
    response
73
}