atagen

roundabout

public
Star0
Watch0
93b435e669a5f6986da74da157a887bf25f9007a
1
ssh://brewery@pls.lobotomise.me:33/atagen/roundabout.git

Blame: src/daemon.rs

6f5b84fad3efatagen2026-09-27 03:59 UTC1
use std::{
6f5b84fad3efatagen2026-09-27 03:59 UTC2
    collections::BTreeMap,
6f5b84fad3efatagen2026-09-27 03:59 UTC3
    fs, io,
6f5b84fad3efatagen2026-09-27 03:59 UTC4
    os::unix::net::{UnixListener, UnixStream},
6f5b84fad3efatagen2026-09-27 03:59 UTC5
    path::PathBuf,
6f5b84fad3efatagen2026-09-27 03:59 UTC6
    process,
6f5b84fad3efatagen2026-09-27 03:59 UTC7
    sync::{Arc, Mutex, MutexGuard},
6f5b84fad3efatagen2026-09-27 03:59 UTC8
    thread,
6f5b84fad3efatagen2026-09-27 03:59 UTC9
    time::{Duration, Instant},
6f5b84fad3efatagen2026-09-27 03:59 UTC10
};
6f5b84fad3efatagen2026-09-27 03:59 UTC11
6f5b84fad3efatagen2026-09-27 03:59 UTC12
use crate::{
6f5b84fad3efatagen2026-09-27 03:59 UTC13
    client,
6f5b84fad3efatagen2026-09-27 03:59 UTC14
    config::Config,
6f5b84fad3efatagen2026-09-27 03:59 UTC15
    paths,
6f5b84fad3efatagen2026-09-27 03:59 UTC16
    proto::{self, Acquire, Event, LeaseId, LeaseInfo, Reader, Request, Status},
6f5b84fad3efatagen2026-09-27 03:59 UTC17
    sched::{self, Capacity, Disk, FsId, Need, Queued, Reason, Running, Verdict},
6f5b84fad3efatagen2026-09-27 03:59 UTC18
    sys,
6f5b84fad3efatagen2026-09-27 03:59 UTC19
    units::Bytes,
6f5b84fad3efatagen2026-09-27 03:59 UTC20
};
6f5b84fad3efatagen2026-09-27 03:59 UTC21
6f5b84fad3efatagen2026-09-27 03:59 UTC22
struct Run {
6f5b84fad3efatagen2026-09-27 03:59 UTC23
    started: Instant,
6f5b84fad3efatagen2026-09-27 03:59 UTC24
    rss: Bytes,
6f5b84fad3efatagen2026-09-27 03:59 UTC25
    reminded: Option<Instant>,
6f5b84fad3efatagen2026-09-27 03:59 UTC26
}
6f5b84fad3efatagen2026-09-27 03:59 UTC27
6f5b84fad3efatagen2026-09-27 03:59 UTC28
struct Lease {
6f5b84fad3efatagen2026-09-27 03:59 UTC29
    req: Acquire,
6f5b84fad3efatagen2026-09-27 03:59 UTC30
    heavy: bool,
6f5b84fad3efatagen2026-09-27 03:59 UTC31
    fs: Vec<FsId>,
6f5b84fad3efatagen2026-09-27 03:59 UTC32
    queued_at: Instant,
6f5b84fad3efatagen2026-09-27 03:59 UTC33
    run: Option<Run>,
6f5b84fad3efatagen2026-09-27 03:59 UTC34
    out: UnixStream,
6f5b84fad3efatagen2026-09-27 03:59 UTC35
    waiting: Option<Reason>,
6f5b84fad3efatagen2026-09-27 03:59 UTC36
    last_pos: usize,
6f5b84fad3efatagen2026-09-27 03:59 UTC37
    watchers: Vec<UnixStream>,
6f5b84fad3efatagen2026-09-27 03:59 UTC38
}
6f5b84fad3efatagen2026-09-27 03:59 UTC39
6f5b84fad3efatagen2026-09-27 03:59 UTC40
impl Lease {
6f5b84fad3efatagen2026-09-27 03:59 UTC41
    fn elapsed(&self) -> Duration {
6f5b84fad3efatagen2026-09-27 03:59 UTC42
        self.run
6f5b84fad3efatagen2026-09-27 03:59 UTC43
            .as_ref()
6f5b84fad3efatagen2026-09-27 03:59 UTC44
            .map_or(self.queued_at, |r| r.started)
6f5b84fad3efatagen2026-09-27 03:59 UTC45
            .elapsed()
6f5b84fad3efatagen2026-09-27 03:59 UTC46
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC47
6f5b84fad3efatagen2026-09-27 03:59 UTC48
    fn overdue(&self, grace: f64) -> bool {
6f5b84fad3efatagen2026-09-27 03:59 UTC49
        self.run.is_some() && self.elapsed() > self.req.need.time.mul_f64(grace)
6f5b84fad3efatagen2026-09-27 03:59 UTC50
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC51
}
6f5b84fad3efatagen2026-09-27 03:59 UTC52
6f5b84fad3efatagen2026-09-27 03:59 UTC53
struct State {
6f5b84fad3efatagen2026-09-27 03:59 UTC54
    cfg: Config,
6f5b84fad3efatagen2026-09-27 03:59 UTC55
    next: LeaseId,
6f5b84fad3efatagen2026-09-27 03:59 UTC56
    leases: BTreeMap<LeaseId, Lease>,
6f5b84fad3efatagen2026-09-27 03:59 UTC57
    cap: Capacity,
6f5b84fad3efatagen2026-09-27 03:59 UTC58
    baselines: BTreeMap<FsId, Bytes>,
6f5b84fad3efatagen2026-09-27 03:59 UTC59
    conns: usize,
6f5b84fad3efatagen2026-09-27 03:59 UTC60
    idle_since: Instant,
6f5b84fad3efatagen2026-09-27 03:59 UTC61
}
6f5b84fad3efatagen2026-09-27 03:59 UTC62
6f5b84fad3efatagen2026-09-27 03:59 UTC63
type Shared = Arc<Mutex<State>>;
6f5b84fad3efatagen2026-09-27 03:59 UTC64
6f5b84fad3efatagen2026-09-27 03:59 UTC65
fn lock(shared: &Shared) -> MutexGuard<'_, State> {
6f5b84fad3efatagen2026-09-27 03:59 UTC66
    shared
6f5b84fad3efatagen2026-09-27 03:59 UTC67
        .lock()
6f5b84fad3efatagen2026-09-27 03:59 UTC68
        .unwrap_or_else(std::sync::PoisonError::into_inner)
6f5b84fad3efatagen2026-09-27 03:59 UTC69
}
6f5b84fad3efatagen2026-09-27 03:59 UTC70
6f5b84fad3efatagen2026-09-27 03:59 UTC71
impl State {
6f5b84fad3efatagen2026-09-27 03:59 UTC72
    fn new(cfg: Config) -> Self {
6f5b84fad3efatagen2026-09-27 03:59 UTC73
        let (total, live) = sys::memory();
6f5b84fad3efatagen2026-09-27 03:59 UTC74
        let cap = Capacity {
6f5b84fad3efatagen2026-09-27 03:59 UTC75
            ram_budget: cfg.budget.ram.resolve(total),
6f5b84fad3efatagen2026-09-27 03:59 UTC76
            ram_live: live,
6f5b84fad3efatagen2026-09-27 03:59 UTC77
            ram_reserve: cfg.budget.ram_reserve,
6f5b84fad3efatagen2026-09-27 03:59 UTC78
            cores: cfg.budget.cores.resolve(sys::cores()),
6f5b84fad3efatagen2026-09-27 03:59 UTC79
            disks: BTreeMap::new(),
6f5b84fad3efatagen2026-09-27 03:59 UTC80
            pressure: false,
6f5b84fad3efatagen2026-09-27 03:59 UTC81
        };
6f5b84fad3efatagen2026-09-27 03:59 UTC82
        Self {
6f5b84fad3efatagen2026-09-27 03:59 UTC83
            cfg,
6f5b84fad3efatagen2026-09-27 03:59 UTC84
            next: 1,
6f5b84fad3efatagen2026-09-27 03:59 UTC85
            leases: BTreeMap::new(),
6f5b84fad3efatagen2026-09-27 03:59 UTC86
            cap,
6f5b84fad3efatagen2026-09-27 03:59 UTC87
            baselines: BTreeMap::new(),
6f5b84fad3efatagen2026-09-27 03:59 UTC88
            conns: 0,
6f5b84fad3efatagen2026-09-27 03:59 UTC89
            idle_since: Instant::now(),
6f5b84fad3efatagen2026-09-27 03:59 UTC90
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC91
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC92
6f5b84fad3efatagen2026-09-27 03:59 UTC93
    fn heavy(&self, need: &Need) -> bool {
6f5b84fad3efatagen2026-09-27 03:59 UTC94
        let h = &self.cfg.heavy;
6f5b84fad3efatagen2026-09-27 03:59 UTC95
        need.ram >= h.ram || need.disk >= h.disk || need.cores >= h.cores.resolve(sys::cores())
6f5b84fad3efatagen2026-09-27 03:59 UTC96
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC97
6f5b84fad3efatagen2026-09-27 03:59 UTC98
    /// capped at a tenth of the fs, or a 16G tmpfs /tmp with a 20G reserve
6f5b84fad3efatagen2026-09-27 03:59 UTC99
    /// admits nothing ever
6f5b84fad3efatagen2026-09-27 03:59 UTC100
    fn reserve(&self, size: Bytes) -> Bytes {
6f5b84fad3efatagen2026-09-27 03:59 UTC101
        self.cfg.budget.disk_reserve.min(Bytes(size.0 / 10))
6f5b84fad3efatagen2026-09-27 03:59 UTC102
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC103
6f5b84fad3efatagen2026-09-27 03:59 UTC104
    fn fs_for(&mut self, cwd: &PathBuf) -> Vec<FsId> {
6f5b84fad3efatagen2026-09-27 03:59 UTC105
        let mut ids = Vec::new();
6f5b84fad3efatagen2026-09-27 03:59 UTC106
        for path in std::iter::once(cwd).chain(&self.cfg.budget.disk_paths) {
6f5b84fad3efatagen2026-09-27 03:59 UTC107
            if let Some(fs) = sys::fs(path) {
6f5b84fad3efatagen2026-09-27 03:59 UTC108
                if !ids.contains(&fs.id) {
6f5b84fad3efatagen2026-09-27 03:59 UTC109
                    ids.push(fs.id);
6f5b84fad3efatagen2026-09-27 03:59 UTC110
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC111
                let reserve = self.reserve(fs.size);
6f5b84fad3efatagen2026-09-27 03:59 UTC112
                self.cap.disks.entry(fs.id).or_insert(Disk {
6f5b84fad3efatagen2026-09-27 03:59 UTC113
                    free: fs.free,
6f5b84fad3efatagen2026-09-27 03:59 UTC114
                    reserve,
6f5b84fad3efatagen2026-09-27 03:59 UTC115
                    consumed: Bytes::ZERO,
6f5b84fad3efatagen2026-09-27 03:59 UTC116
                });
6f5b84fad3efatagen2026-09-27 03:59 UTC117
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC118
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC119
        ids
6f5b84fad3efatagen2026-09-27 03:59 UTC120
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC121
6f5b84fad3efatagen2026-09-27 03:59 UTC122
    fn refresh(&mut self) {
6f5b84fad3efatagen2026-09-27 03:59 UTC123
        let (_, live) = sys::memory();
6f5b84fad3efatagen2026-09-27 03:59 UTC124
        self.cap.ram_live = live;
6f5b84fad3efatagen2026-09-27 03:59 UTC125
        self.cap.pressure = self
6f5b84fad3efatagen2026-09-27 03:59 UTC126
            .cfg
6f5b84fad3efatagen2026-09-27 03:59 UTC127
            .budget
6f5b84fad3efatagen2026-09-27 03:59 UTC128
            .psi_max
6f5b84fad3efatagen2026-09-27 03:59 UTC129
            .is_some_and(|max| sys::pressure().is_some_and(|p| p > max));
6f5b84fad3efatagen2026-09-27 03:59 UTC130
6f5b84fad3efatagen2026-09-27 03:59 UTC131
        let mut paths: Vec<PathBuf> = self.leases.values().map(|l| l.req.cwd.clone()).collect();
6f5b84fad3efatagen2026-09-27 03:59 UTC132
        paths.extend(self.cfg.budget.disk_paths.iter().cloned());
6f5b84fad3efatagen2026-09-27 03:59 UTC133
        let mut disks = BTreeMap::new();
6f5b84fad3efatagen2026-09-27 03:59 UTC134
        for path in paths {
6f5b84fad3efatagen2026-09-27 03:59 UTC135
            if let Some(fs) = sys::fs(&path) {
6f5b84fad3efatagen2026-09-27 03:59 UTC136
                disks.insert(fs.id, (fs.free, self.reserve(fs.size)));
6f5b84fad3efatagen2026-09-27 03:59 UTC137
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC138
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC139
6f5b84fad3efatagen2026-09-27 03:59 UTC140
        let busy: Vec<FsId> = self
6f5b84fad3efatagen2026-09-27 03:59 UTC141
            .leases
6f5b84fad3efatagen2026-09-27 03:59 UTC142
            .values()
6f5b84fad3efatagen2026-09-27 03:59 UTC143
            .filter(|l| l.run.is_some())
6f5b84fad3efatagen2026-09-27 03:59 UTC144
            .flat_map(|l| l.fs.iter().copied())
6f5b84fad3efatagen2026-09-27 03:59 UTC145
            .collect();
6f5b84fad3efatagen2026-09-27 03:59 UTC146
        self.baselines.retain(|id, _| busy.contains(id));
6f5b84fad3efatagen2026-09-27 03:59 UTC147
        self.cap.disks = disks
6f5b84fad3efatagen2026-09-27 03:59 UTC148
            .into_iter()
6f5b84fad3efatagen2026-09-27 03:59 UTC149
            .map(|(id, (free, reserve))| {
6f5b84fad3efatagen2026-09-27 03:59 UTC150
                let consumed = if busy.contains(&id) {
6f5b84fad3efatagen2026-09-27 03:59 UTC151
                    self.baselines
6f5b84fad3efatagen2026-09-27 03:59 UTC152
                        .entry(id)
6f5b84fad3efatagen2026-09-27 03:59 UTC153
                        .or_insert(free)
6f5b84fad3efatagen2026-09-27 03:59 UTC154
                        .saturating_sub(free)
6f5b84fad3efatagen2026-09-27 03:59 UTC155
                } else {
6f5b84fad3efatagen2026-09-27 03:59 UTC156
                    Bytes::ZERO
6f5b84fad3efatagen2026-09-27 03:59 UTC157
                };
6f5b84fad3efatagen2026-09-27 03:59 UTC158
                (
6f5b84fad3efatagen2026-09-27 03:59 UTC159
                    id,
6f5b84fad3efatagen2026-09-27 03:59 UTC160
                    Disk {
6f5b84fad3efatagen2026-09-27 03:59 UTC161
                        free,
6f5b84fad3efatagen2026-09-27 03:59 UTC162
                        reserve,
6f5b84fad3efatagen2026-09-27 03:59 UTC163
                        consumed,
6f5b84fad3efatagen2026-09-27 03:59 UTC164
                    },
6f5b84fad3efatagen2026-09-27 03:59 UTC165
                )
6f5b84fad3efatagen2026-09-27 03:59 UTC166
            })
6f5b84fad3efatagen2026-09-27 03:59 UTC167
            .collect();
6f5b84fad3efatagen2026-09-27 03:59 UTC168
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC169
6f5b84fad3efatagen2026-09-27 03:59 UTC170
    fn running(&self) -> Vec<Running> {
6f5b84fad3efatagen2026-09-27 03:59 UTC171
        self.leases
6f5b84fad3efatagen2026-09-27 03:59 UTC172
            .values()
6f5b84fad3efatagen2026-09-27 03:59 UTC173
            .filter_map(|l| {
6f5b84fad3efatagen2026-09-27 03:59 UTC174
                let run = l.run.as_ref()?;
6f5b84fad3efatagen2026-09-27 03:59 UTC175
                Some(Running {
6f5b84fad3efatagen2026-09-27 03:59 UTC176
                    need: l.req.need,
6f5b84fad3efatagen2026-09-27 03:59 UTC177
                    heavy: l.heavy,
6f5b84fad3efatagen2026-09-27 03:59 UTC178
                    fs: l.fs.clone(),
6f5b84fad3efatagen2026-09-27 03:59 UTC179
                    rss: run.rss,
6f5b84fad3efatagen2026-09-27 03:59 UTC180
                    remaining: l.req.need.time.saturating_sub(run.started.elapsed()),
6f5b84fad3efatagen2026-09-27 03:59 UTC181
                })
6f5b84fad3efatagen2026-09-27 03:59 UTC182
            })
6f5b84fad3efatagen2026-09-27 03:59 UTC183
            .collect()
6f5b84fad3efatagen2026-09-27 03:59 UTC184
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC185
6f5b84fad3efatagen2026-09-27 03:59 UTC186
    fn reschedule(&mut self) {
6f5b84fad3efatagen2026-09-27 03:59 UTC187
        let queue: Vec<Queued> = self
6f5b84fad3efatagen2026-09-27 03:59 UTC188
            .leases
6f5b84fad3efatagen2026-09-27 03:59 UTC189
            .iter()
6f5b84fad3efatagen2026-09-27 03:59 UTC190
            .filter(|(_, l)| l.run.is_none())
6f5b84fad3efatagen2026-09-27 03:59 UTC191
            .map(|(id, l)| Queued {
6f5b84fad3efatagen2026-09-27 03:59 UTC192
                id: *id,
6f5b84fad3efatagen2026-09-27 03:59 UTC193
                need: l.req.need,
6f5b84fad3efatagen2026-09-27 03:59 UTC194
                heavy: l.heavy,
6f5b84fad3efatagen2026-09-27 03:59 UTC195
                fs: l.fs.clone(),
6f5b84fad3efatagen2026-09-27 03:59 UTC196
            })
6f5b84fad3efatagen2026-09-27 03:59 UTC197
            .collect();
6f5b84fad3efatagen2026-09-27 03:59 UTC198
        if queue.is_empty() {
6f5b84fad3efatagen2026-09-27 03:59 UTC199
            return;
6f5b84fad3efatagen2026-09-27 03:59 UTC200
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC201
        let verdicts = sched::plan(
6f5b84fad3efatagen2026-09-27 03:59 UTC202
            &queue,
6f5b84fad3efatagen2026-09-27 03:59 UTC203
            &self.running(),
6f5b84fad3efatagen2026-09-27 03:59 UTC204
            &self.cap,
6f5b84fad3efatagen2026-09-27 03:59 UTC205
            self.cfg.queue.backfill_max_time.0,
6f5b84fad3efatagen2026-09-27 03:59 UTC206
        );
6f5b84fad3efatagen2026-09-27 03:59 UTC207
        let cores = self.cap.cores;
6f5b84fad3efatagen2026-09-27 03:59 UTC208
        let mut position = 0;
6f5b84fad3efatagen2026-09-27 03:59 UTC209
        for (id, verdict) in verdicts {
6f5b84fad3efatagen2026-09-27 03:59 UTC210
            let Some(lease) = self.leases.get_mut(&id) else {
6f5b84fad3efatagen2026-09-27 03:59 UTC211
                continue;
6f5b84fad3efatagen2026-09-27 03:59 UTC212
            };
6f5b84fad3efatagen2026-09-27 03:59 UTC213
            match verdict {
6f5b84fad3efatagen2026-09-27 03:59 UTC214
                Verdict::Admit => {
6f5b84fad3efatagen2026-09-27 03:59 UTC215
                    lease.run = Some(Run {
6f5b84fad3efatagen2026-09-27 03:59 UTC216
                        started: Instant::now(),
6f5b84fad3efatagen2026-09-27 03:59 UTC217
                        rss: Bytes::ZERO,
6f5b84fad3efatagen2026-09-27 03:59 UTC218
                        reminded: None,
6f5b84fad3efatagen2026-09-27 03:59 UTC219
                    });
6f5b84fad3efatagen2026-09-27 03:59 UTC220
                    let _ = proto::send(
6f5b84fad3efatagen2026-09-27 03:59 UTC221
                        &mut lease.out,
6f5b84fad3efatagen2026-09-27 03:59 UTC222
                        &Event::Granted {
6f5b84fad3efatagen2026-09-27 03:59 UTC223
                            lease: id,
6f5b84fad3efatagen2026-09-27 03:59 UTC224
                            cores: lease.req.need.cores.clamp(1, cores.max(1)),
6f5b84fad3efatagen2026-09-27 03:59 UTC225
                            heavy: lease.heavy,
6f5b84fad3efatagen2026-09-27 03:59 UTC226
                        },
6f5b84fad3efatagen2026-09-27 03:59 UTC227
                    );
6f5b84fad3efatagen2026-09-27 03:59 UTC228
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC229
                Verdict::Wait(reason) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC230
                    position += 1;
6f5b84fad3efatagen2026-09-27 03:59 UTC231
                    lease.waiting = Some(reason);
6f5b84fad3efatagen2026-09-27 03:59 UTC232
                    if lease.last_pos != position {
6f5b84fad3efatagen2026-09-27 03:59 UTC233
                        lease.last_pos = position;
6f5b84fad3efatagen2026-09-27 03:59 UTC234
                        let _ = proto::send(
6f5b84fad3efatagen2026-09-27 03:59 UTC235
                            &mut lease.out,
6f5b84fad3efatagen2026-09-27 03:59 UTC236
                            &Event::Queued {
6f5b84fad3efatagen2026-09-27 03:59 UTC237
                                lease: id,
6f5b84fad3efatagen2026-09-27 03:59 UTC238
                                position,
6f5b84fad3efatagen2026-09-27 03:59 UTC239
                                reason: Some(reason),
6f5b84fad3efatagen2026-09-27 03:59 UTC240
                            },
6f5b84fad3efatagen2026-09-27 03:59 UTC241
                        );
6f5b84fad3efatagen2026-09-27 03:59 UTC242
                    }
6f5b84fad3efatagen2026-09-27 03:59 UTC243
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC244
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC245
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC246
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC247
6f5b84fad3efatagen2026-09-27 03:59 UTC248
    fn remove(&mut self, id: LeaseId) {
6f5b84fad3efatagen2026-09-27 03:59 UTC249
        if let Some(lease) = self.leases.remove(&id) {
6f5b84fad3efatagen2026-09-27 03:59 UTC250
            for mut w in lease.watchers {
6f5b84fad3efatagen2026-09-27 03:59 UTC251
                let _ = proto::send(&mut w, &Event::Fired { overdue: false });
6f5b84fad3efatagen2026-09-27 03:59 UTC252
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC253
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC254
        if self.leases.is_empty() {
6f5b84fad3efatagen2026-09-27 03:59 UTC255
            self.idle_since = Instant::now();
6f5b84fad3efatagen2026-09-27 03:59 UTC256
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC257
        self.refresh();
6f5b84fad3efatagen2026-09-27 03:59 UTC258
        self.reschedule();
6f5b84fad3efatagen2026-09-27 03:59 UTC259
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC260
6f5b84fad3efatagen2026-09-27 03:59 UTC261
    fn fire_overdue(&mut self) {
6f5b84fad3efatagen2026-09-27 03:59 UTC262
        let grace = self.cfg.overrun.time_grace;
6f5b84fad3efatagen2026-09-27 03:59 UTC263
        for lease in self.leases.values_mut() {
6f5b84fad3efatagen2026-09-27 03:59 UTC264
            if !lease.watchers.is_empty() && lease.overdue(grace) {
6f5b84fad3efatagen2026-09-27 03:59 UTC265
                for mut w in lease.watchers.drain(..) {
6f5b84fad3efatagen2026-09-27 03:59 UTC266
                    let _ = proto::send(&mut w, &Event::Fired { overdue: true });
6f5b84fad3efatagen2026-09-27 03:59 UTC267
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC268
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC269
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC270
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC271
6f5b84fad3efatagen2026-09-27 03:59 UTC272
    fn info(&self) -> Vec<LeaseInfo> {
6f5b84fad3efatagen2026-09-27 03:59 UTC273
        let grace = self.cfg.overrun.time_grace;
6f5b84fad3efatagen2026-09-27 03:59 UTC274
        let mut position = 0;
6f5b84fad3efatagen2026-09-27 03:59 UTC275
        self.leases
6f5b84fad3efatagen2026-09-27 03:59 UTC276
            .iter()
6f5b84fad3efatagen2026-09-27 03:59 UTC277
            .map(|(id, l)| {
6f5b84fad3efatagen2026-09-27 03:59 UTC278
                if l.run.is_none() {
6f5b84fad3efatagen2026-09-27 03:59 UTC279
                    position += 1;
6f5b84fad3efatagen2026-09-27 03:59 UTC280
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC281
                LeaseInfo {
6f5b84fad3efatagen2026-09-27 03:59 UTC282
                    id: *id,
6f5b84fad3efatagen2026-09-27 03:59 UTC283
                    cmd: l.req.cmd.clone(),
6f5b84fad3efatagen2026-09-27 03:59 UTC284
                    cwd: l.req.cwd.clone(),
6f5b84fad3efatagen2026-09-27 03:59 UTC285
                    session: l.req.session.clone(),
6f5b84fad3efatagen2026-09-27 03:59 UTC286
                    need: l.req.need,
6f5b84fad3efatagen2026-09-27 03:59 UTC287
                    heavy: l.heavy,
6f5b84fad3efatagen2026-09-27 03:59 UTC288
                    running: l.run.is_some(),
6f5b84fad3efatagen2026-09-27 03:59 UTC289
                    elapsed: l.elapsed().as_secs(),
6f5b84fad3efatagen2026-09-27 03:59 UTC290
                    rss: l.run.as_ref().map_or(Bytes::ZERO, |r| r.rss),
6f5b84fad3efatagen2026-09-27 03:59 UTC291
                    overdue: l.overdue(grace),
6f5b84fad3efatagen2026-09-27 03:59 UTC292
                    waiting: l.waiting.filter(|_| l.run.is_none()),
6f5b84fad3efatagen2026-09-27 03:59 UTC293
                    position: if l.run.is_none() { position } else { 0 },
6f5b84fad3efatagen2026-09-27 03:59 UTC294
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC295
            })
6f5b84fad3efatagen2026-09-27 03:59 UTC296
            .collect()
6f5b84fad3efatagen2026-09-27 03:59 UTC297
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC298
6f5b84fad3efatagen2026-09-27 03:59 UTC299
    fn status(&self) -> Status {
6f5b84fad3efatagen2026-09-27 03:59 UTC300
        let (ram_free, cores_free) = sched::headroom(&self.running(), &self.cap);
6f5b84fad3efatagen2026-09-27 03:59 UTC301
        Status {
6f5b84fad3efatagen2026-09-27 03:59 UTC302
            leases: self.info(),
6f5b84fad3efatagen2026-09-27 03:59 UTC303
            ram_free,
6f5b84fad3efatagen2026-09-27 03:59 UTC304
            ram_budget: self.cap.ram_budget,
6f5b84fad3efatagen2026-09-27 03:59 UTC305
            cores_free,
6f5b84fad3efatagen2026-09-27 03:59 UTC306
            cores: self.cap.cores,
6f5b84fad3efatagen2026-09-27 03:59 UTC307
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC308
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC309
}
6f5b84fad3efatagen2026-09-27 03:59 UTC310
6f5b84fad3efatagen2026-09-27 03:59 UTC311
fn serve(shared: &Shared, mut stream: UnixStream) -> io::Result<()> {
6f5b84fad3efatagen2026-09-27 03:59 UTC312
    let mut reader = Reader::new(&stream)?;
6f5b84fad3efatagen2026-09-27 03:59 UTC313
    let Some(req) = reader.recv::<Request>()? else {
6f5b84fad3efatagen2026-09-27 03:59 UTC314
        return Ok(());
6f5b84fad3efatagen2026-09-27 03:59 UTC315
    };
6f5b84fad3efatagen2026-09-27 03:59 UTC316
    match req {
6f5b84fad3efatagen2026-09-27 03:59 UTC317
        Request::Acquire(req) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC318
            let id = {
6f5b84fad3efatagen2026-09-27 03:59 UTC319
                let mut st = lock(shared);
6f5b84fad3efatagen2026-09-27 03:59 UTC320
                let id = st.next;
6f5b84fad3efatagen2026-09-27 03:59 UTC321
                st.next += 1;
6f5b84fad3efatagen2026-09-27 03:59 UTC322
                let fs = st.fs_for(&req.cwd);
6f5b84fad3efatagen2026-09-27 03:59 UTC323
                let heavy = st.heavy(&req.need);
6f5b84fad3efatagen2026-09-27 03:59 UTC324
                st.leases.insert(
6f5b84fad3efatagen2026-09-27 03:59 UTC325
                    id,
6f5b84fad3efatagen2026-09-27 03:59 UTC326
                    Lease {
6f5b84fad3efatagen2026-09-27 03:59 UTC327
                        req,
6f5b84fad3efatagen2026-09-27 03:59 UTC328
                        heavy,
6f5b84fad3efatagen2026-09-27 03:59 UTC329
                        fs,
6f5b84fad3efatagen2026-09-27 03:59 UTC330
                        queued_at: Instant::now(),
6f5b84fad3efatagen2026-09-27 03:59 UTC331
                        run: None,
6f5b84fad3efatagen2026-09-27 03:59 UTC332
                        out: stream.try_clone()?,
6f5b84fad3efatagen2026-09-27 03:59 UTC333
                        waiting: None,
6f5b84fad3efatagen2026-09-27 03:59 UTC334
                        last_pos: 0,
6f5b84fad3efatagen2026-09-27 03:59 UTC335
                        watchers: Vec::new(),
6f5b84fad3efatagen2026-09-27 03:59 UTC336
                    },
6f5b84fad3efatagen2026-09-27 03:59 UTC337
                );
6f5b84fad3efatagen2026-09-27 03:59 UTC338
                st.reschedule();
6f5b84fad3efatagen2026-09-27 03:59 UTC339
                id
6f5b84fad3efatagen2026-09-27 03:59 UTC340
            };
6f5b84fad3efatagen2026-09-27 03:59 UTC341
            hold(shared, id, &mut reader);
6f5b84fad3efatagen2026-09-27 03:59 UTC342
            lock(shared).remove(id);
6f5b84fad3efatagen2026-09-27 03:59 UTC343
            Ok(())
6f5b84fad3efatagen2026-09-27 03:59 UTC344
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC345
        Request::Status => {
6f5b84fad3efatagen2026-09-27 03:59 UTC346
            let status = lock(shared).status();
6f5b84fad3efatagen2026-09-27 03:59 UTC347
            proto::send(&mut stream, &Event::Status(status))
6f5b84fad3efatagen2026-09-27 03:59 UTC348
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC349
        Request::Kill { lease } => {
6f5b84fad3efatagen2026-09-27 03:59 UTC350
            let mut st = lock(shared);
6f5b84fad3efatagen2026-09-27 03:59 UTC351
            let reply = match st.leases.get_mut(&lease) {
6f5b84fad3efatagen2026-09-27 03:59 UTC352
                Some(l) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC353
                    let _ = proto::send(&mut l.out, &Event::Kill);
6f5b84fad3efatagen2026-09-27 03:59 UTC354
                    Event::Ok
6f5b84fad3efatagen2026-09-27 03:59 UTC355
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC356
                None => Event::Err {
6f5b84fad3efatagen2026-09-27 03:59 UTC357
                    msg: format!("no lease {lease}"),
6f5b84fad3efatagen2026-09-27 03:59 UTC358
                },
6f5b84fad3efatagen2026-09-27 03:59 UTC359
            };
6f5b84fad3efatagen2026-09-27 03:59 UTC360
            drop(st);
6f5b84fad3efatagen2026-09-27 03:59 UTC361
            proto::send(&mut stream, &reply)
6f5b84fad3efatagen2026-09-27 03:59 UTC362
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC363
        Request::Extend { lease, by } => {
6f5b84fad3efatagen2026-09-27 03:59 UTC364
            let mut st = lock(shared);
6f5b84fad3efatagen2026-09-27 03:59 UTC365
            let reply = match st.leases.get_mut(&lease) {
6f5b84fad3efatagen2026-09-27 03:59 UTC366
                Some(l) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC367
                    l.req.need.time += by.0;
6f5b84fad3efatagen2026-09-27 03:59 UTC368
                    if let Some(run) = &mut l.run {
6f5b84fad3efatagen2026-09-27 03:59 UTC369
                        run.reminded = None;
6f5b84fad3efatagen2026-09-27 03:59 UTC370
                    }
6f5b84fad3efatagen2026-09-27 03:59 UTC371
                    Event::Ok
6f5b84fad3efatagen2026-09-27 03:59 UTC372
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC373
                None => Event::Err {
6f5b84fad3efatagen2026-09-27 03:59 UTC374
                    msg: format!("no lease {lease}"),
6f5b84fad3efatagen2026-09-27 03:59 UTC375
                },
6f5b84fad3efatagen2026-09-27 03:59 UTC376
            };
6f5b84fad3efatagen2026-09-27 03:59 UTC377
            drop(st);
6f5b84fad3efatagen2026-09-27 03:59 UTC378
            proto::send(&mut stream, &reply)
6f5b84fad3efatagen2026-09-27 03:59 UTC379
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC380
        Request::Watch { lease } => {
6f5b84fad3efatagen2026-09-27 03:59 UTC381
            let mut st = lock(shared);
6f5b84fad3efatagen2026-09-27 03:59 UTC382
            let grace = st.cfg.overrun.time_grace;
6f5b84fad3efatagen2026-09-27 03:59 UTC383
            match st.leases.get_mut(&lease) {
6f5b84fad3efatagen2026-09-27 03:59 UTC384
                Some(l) if l.overdue(grace) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC385
                    proto::send(&mut stream, &Event::Fired { overdue: true })
6f5b84fad3efatagen2026-09-27 03:59 UTC386
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC387
                Some(l) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC388
                    l.watchers.push(stream);
6f5b84fad3efatagen2026-09-27 03:59 UTC389
                    Ok(())
6f5b84fad3efatagen2026-09-27 03:59 UTC390
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC391
                None => proto::send(&mut stream, &Event::Fired { overdue: false }),
6f5b84fad3efatagen2026-09-27 03:59 UTC392
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC393
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC394
        Request::Overdue { session } => {
6f5b84fad3efatagen2026-09-27 03:59 UTC395
            let mut st = lock(shared);
6f5b84fad3efatagen2026-09-27 03:59 UTC396
            let grace = st.cfg.overrun.time_grace;
6f5b84fad3efatagen2026-09-27 03:59 UTC397
            let every = st.cfg.overrun.remind_every.0;
6f5b84fad3efatagen2026-09-27 03:59 UTC398
            let mut due = Vec::new();
6f5b84fad3efatagen2026-09-27 03:59 UTC399
            for (id, l) in &mut st.leases {
6f5b84fad3efatagen2026-09-27 03:59 UTC400
                if l.req.session.as_deref() != Some(&session) || !l.overdue(grace) {
6f5b84fad3efatagen2026-09-27 03:59 UTC401
                    continue;
6f5b84fad3efatagen2026-09-27 03:59 UTC402
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC403
                let Some(run) = &mut l.run else { continue };
6f5b84fad3efatagen2026-09-27 03:59 UTC404
                if run.reminded.is_some_and(|t| t.elapsed() < every) {
6f5b84fad3efatagen2026-09-27 03:59 UTC405
                    continue;
6f5b84fad3efatagen2026-09-27 03:59 UTC406
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC407
                run.reminded = Some(Instant::now());
6f5b84fad3efatagen2026-09-27 03:59 UTC408
                due.push(*id);
6f5b84fad3efatagen2026-09-27 03:59 UTC409
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC410
            let leases = st
6f5b84fad3efatagen2026-09-27 03:59 UTC411
                .info()
6f5b84fad3efatagen2026-09-27 03:59 UTC412
                .into_iter()
6f5b84fad3efatagen2026-09-27 03:59 UTC413
                .filter(|i| due.contains(&i.id))
6f5b84fad3efatagen2026-09-27 03:59 UTC414
                .collect();
6f5b84fad3efatagen2026-09-27 03:59 UTC415
            drop(st);
6f5b84fad3efatagen2026-09-27 03:59 UTC416
            proto::send(&mut stream, &Event::Overdue { leases })
6f5b84fad3efatagen2026-09-27 03:59 UTC417
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC418
        Request::Started { .. } | Request::Usage { .. } | Request::Done => proto::send(
6f5b84fad3efatagen2026-09-27 03:59 UTC419
            &mut stream,
6f5b84fad3efatagen2026-09-27 03:59 UTC420
            &Event::Err {
6f5b84fad3efatagen2026-09-27 03:59 UTC421
                msg: "no lease on this connection".into(),
6f5b84fad3efatagen2026-09-27 03:59 UTC422
            },
6f5b84fad3efatagen2026-09-27 03:59 UTC423
        ),
6f5b84fad3efatagen2026-09-27 03:59 UTC424
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC425
}
6f5b84fad3efatagen2026-09-27 03:59 UTC426
6f5b84fad3efatagen2026-09-27 03:59 UTC427
/// a client that vanishes without saying done got sigkilled or similar, and
6f5b84fad3efatagen2026-09-27 03:59 UTC428
/// pdeathsig only reaches its direct child. take the rest of the group down
6f5b84fad3efatagen2026-09-27 03:59 UTC429
/// so a headless build doesn't keep eating ram off the books
6f5b84fad3efatagen2026-09-27 03:59 UTC430
fn hold(shared: &Shared, id: LeaseId, reader: &mut Reader) {
6f5b84fad3efatagen2026-09-27 03:59 UTC431
    let mut pgid = None;
6f5b84fad3efatagen2026-09-27 03:59 UTC432
    let clean = loop {
6f5b84fad3efatagen2026-09-27 03:59 UTC433
        match reader.recv::<Request>() {
6f5b84fad3efatagen2026-09-27 03:59 UTC434
            Ok(Some(Request::Usage { rss })) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC435
                if let Some(run) = lock(shared)
6f5b84fad3efatagen2026-09-27 03:59 UTC436
                    .leases
6f5b84fad3efatagen2026-09-27 03:59 UTC437
                    .get_mut(&id)
6f5b84fad3efatagen2026-09-27 03:59 UTC438
                    .and_then(|l| l.run.as_mut())
6f5b84fad3efatagen2026-09-27 03:59 UTC439
                {
6f5b84fad3efatagen2026-09-27 03:59 UTC440
                    run.rss = rss;
6f5b84fad3efatagen2026-09-27 03:59 UTC441
                }
6f5b84fad3efatagen2026-09-27 03:59 UTC442
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC443
            Ok(Some(Request::Started { pid })) => pgid = Some(pid),
6f5b84fad3efatagen2026-09-27 03:59 UTC444
            Ok(Some(Request::Done)) => break true,
6f5b84fad3efatagen2026-09-27 03:59 UTC445
            Ok(Some(_)) => {}
6f5b84fad3efatagen2026-09-27 03:59 UTC446
            Ok(None) | Err(_) => break false,
6f5b84fad3efatagen2026-09-27 03:59 UTC447
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC448
    };
6f5b84fad3efatagen2026-09-27 03:59 UTC449
    if !clean && let Some(pgid) = pgid.filter(|p| *p > 1) {
6f5b84fad3efatagen2026-09-27 03:59 UTC450
        unsafe { libc::kill(-pgid, libc::SIGTERM) };
6f5b84fad3efatagen2026-09-27 03:59 UTC451
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC452
}
6f5b84fad3efatagen2026-09-27 03:59 UTC453
6f5b84fad3efatagen2026-09-27 03:59 UTC454
fn tick(shared: &Shared) {
6f5b84fad3efatagen2026-09-27 03:59 UTC455
    loop {
6f5b84fad3efatagen2026-09-27 03:59 UTC456
        thread::sleep(Duration::from_secs(1));
6f5b84fad3efatagen2026-09-27 03:59 UTC457
        let mut st = lock(shared);
6f5b84fad3efatagen2026-09-27 03:59 UTC458
        st.refresh();
6f5b84fad3efatagen2026-09-27 03:59 UTC459
        st.reschedule();
6f5b84fad3efatagen2026-09-27 03:59 UTC460
        st.fire_overdue();
6f5b84fad3efatagen2026-09-27 03:59 UTC461
        if st.conns == 0 && st.leases.is_empty() && st.idle_since.elapsed() >= st.cfg.queue.linger.0
6f5b84fad3efatagen2026-09-27 03:59 UTC462
        {
6f5b84fad3efatagen2026-09-27 03:59 UTC463
            // a spawner holds this while it clears a stale socket, so taking it
6f5b84fad3efatagen2026-09-27 03:59 UTC464
            // here stops us unlinking our successor's socket on the way out
6f5b84fad3efatagen2026-09-27 03:59 UTC465
            if let Some(_guard) = try_spawn_lock() {
6f5b84fad3efatagen2026-09-27 03:59 UTC466
                let _ = fs::remove_file(paths::socket());
6f5b84fad3efatagen2026-09-27 03:59 UTC467
                process::exit(0);
6f5b84fad3efatagen2026-09-27 03:59 UTC468
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC469
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC470
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC471
}
6f5b84fad3efatagen2026-09-27 03:59 UTC472
6f5b84fad3efatagen2026-09-27 03:59 UTC473
fn try_spawn_lock() -> Option<client::Flock> {
6f5b84fad3efatagen2026-09-27 03:59 UTC474
    let file = fs::OpenOptions::new()
6f5b84fad3efatagen2026-09-27 03:59 UTC475
        .write(true)
6f5b84fad3efatagen2026-09-27 03:59 UTC476
        .open(paths::spawn_lock())
6f5b84fad3efatagen2026-09-27 03:59 UTC477
        .ok()?;
6f5b84fad3efatagen2026-09-27 03:59 UTC478
    client::Flock::try_exclusive(file)
6f5b84fad3efatagen2026-09-27 03:59 UTC479
}
6f5b84fad3efatagen2026-09-27 03:59 UTC480
6f5b84fad3efatagen2026-09-27 03:59 UTC481
pub fn main(cfg: Config) -> ! {
6f5b84fad3efatagen2026-09-27 03:59 UTC482
    // fork once more so the client that spawned us can reap its child now
6f5b84fad3efatagen2026-09-27 03:59 UTC483
    // instead of carrying a zombie for the length of its build
6f5b84fad3efatagen2026-09-27 03:59 UTC484
    match unsafe { libc::fork() } {
6f5b84fad3efatagen2026-09-27 03:59 UTC485
        -1 => {
6f5b84fad3efatagen2026-09-27 03:59 UTC486
            eprintln!("roundabout: fork failed: {}", io::Error::last_os_error());
6f5b84fad3efatagen2026-09-27 03:59 UTC487
            process::exit(1);
6f5b84fad3efatagen2026-09-27 03:59 UTC488
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC489
        0 => {}
6f5b84fad3efatagen2026-09-27 03:59 UTC490
        _ => process::exit(0),
6f5b84fad3efatagen2026-09-27 03:59 UTC491
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC492
6f5b84fad3efatagen2026-09-27 03:59 UTC493
    let listener = match UnixListener::bind(paths::socket()) {
6f5b84fad3efatagen2026-09-27 03:59 UTC494
        Ok(l) => l,
6f5b84fad3efatagen2026-09-27 03:59 UTC495
        Err(e) if e.kind() == io::ErrorKind::AddrInUse && client::connect().is_some() => {
6f5b84fad3efatagen2026-09-27 03:59 UTC496
            process::exit(0)
6f5b84fad3efatagen2026-09-27 03:59 UTC497
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC498
        Err(e) => {
6f5b84fad3efatagen2026-09-27 03:59 UTC499
            eprintln!("roundabout: bind: {e}");
6f5b84fad3efatagen2026-09-27 03:59 UTC500
            process::exit(1);
6f5b84fad3efatagen2026-09-27 03:59 UTC501
        }
6f5b84fad3efatagen2026-09-27 03:59 UTC502
    };
6f5b84fad3efatagen2026-09-27 03:59 UTC503
6f5b84fad3efatagen2026-09-27 03:59 UTC504
    let shared: Shared = Arc::new(Mutex::new(State::new(cfg)));
6f5b84fad3efatagen2026-09-27 03:59 UTC505
    {
6f5b84fad3efatagen2026-09-27 03:59 UTC506
        let shared = shared.clone();
6f5b84fad3efatagen2026-09-27 03:59 UTC507
        thread::spawn(move || tick(&shared));
6f5b84fad3efatagen2026-09-27 03:59 UTC508
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC509
6f5b84fad3efatagen2026-09-27 03:59 UTC510
    for stream in listener.incoming() {
6f5b84fad3efatagen2026-09-27 03:59 UTC511
        let Ok(stream) = stream else { continue };
6f5b84fad3efatagen2026-09-27 03:59 UTC512
        lock(&shared).conns += 1;
6f5b84fad3efatagen2026-09-27 03:59 UTC513
        let shared = shared.clone();
6f5b84fad3efatagen2026-09-27 03:59 UTC514
        thread::spawn(move || {
6f5b84fad3efatagen2026-09-27 03:59 UTC515
            if let Err(e) = serve(&shared, stream) {
6f5b84fad3efatagen2026-09-27 03:59 UTC516
                eprintln!("roundabout: {e}");
6f5b84fad3efatagen2026-09-27 03:59 UTC517
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC518
            let mut st = lock(&shared);
6f5b84fad3efatagen2026-09-27 03:59 UTC519
            st.conns -= 1;
6f5b84fad3efatagen2026-09-27 03:59 UTC520
            if st.leases.is_empty() {
6f5b84fad3efatagen2026-09-27 03:59 UTC521
                st.idle_since = Instant::now();
6f5b84fad3efatagen2026-09-27 03:59 UTC522
            }
6f5b84fad3efatagen2026-09-27 03:59 UTC523
        });
6f5b84fad3efatagen2026-09-27 03:59 UTC524
    }
6f5b84fad3efatagen2026-09-27 03:59 UTC525
    process::exit(0)
6f5b84fad3efatagen2026-09-27 03:59 UTC526
}