6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 1 | use std::{ |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 2 | collections::BTreeMap, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 3 | fs, io, |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 4 | os::{ |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 5 | fd::FromRawFd, |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 6 | unix::net::{UnixListener, UnixStream}, |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 7 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 8 | path::PathBuf, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 9 | process, |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 10 | sync::{ |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 11 | Arc, Mutex, MutexGuard, |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 12 | atomic::{AtomicBool, Ordering}, |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 13 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 14 | thread, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 15 | time::{Duration, Instant}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 16 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 17 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 18 | use crate::{ |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 19 | client, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 20 | config::Config, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 21 | paths, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 22 | proto::{self, Acquire, Event, IntentInfo, LeaseId, LeaseInfo, Reader, Request, Status}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 23 | sched::{self, Capacity, Disk, FsId, Need, Queued, Reason, Running, Verdict}, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 24 | sys, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 25 | units::Bytes, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 26 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 27 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 28 | struct Run { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 29 | started: Instant, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 30 | rss: Bytes, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 31 | reminded: Option<Instant>, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 32 | /// asked-for plus bonus. only the asked-for part counts for admission, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 33 | /// oversubscribed cores just make everyone slower |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 34 | cores: u32, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 35 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 36 | |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 37 | struct Intent { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 38 | need: Need, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 39 | until: Instant, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 40 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 41 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 42 | struct Lease { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 43 | req: Acquire, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 44 | heavy: bool, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 45 | fs: Vec<FsId>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 46 | queued_at: Instant, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 47 | run: Option<Run>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 48 | out: UnixStream, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 49 | waiting: Option<Reason>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 50 | last_pos: usize, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 51 | watchers: Vec<UnixStream>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 52 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 53 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 54 | impl Lease { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 55 | fn elapsed(&self) -> Duration { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 56 | self.run |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 57 | .as_ref() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 58 | .map_or(self.queued_at, |r| r.started) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 59 | .elapsed() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 60 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 61 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 62 | fn overdue(&self, grace: f64) -> bool { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 63 | self.run.is_some() && self.elapsed() > self.req.need.time.mul_f64(grace) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 64 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 65 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 66 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 67 | struct State { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 68 | cfg: Config, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 69 | next: LeaseId, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 70 | leases: BTreeMap<LeaseId, Lease>, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 71 | intents: BTreeMap<String, Intent>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 72 | cap: Capacity, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 73 | baselines: BTreeMap<FsId, Bytes>, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 74 | conns: usize, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 75 | idle_since: Instant, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 76 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 77 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 78 | type Shared = Arc<Mutex<State>>; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 79 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 80 | fn lock(shared: &Shared) -> MutexGuard<'_, State> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 81 | shared |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 82 | .lock() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 83 | .unwrap_or_else(std::sync::PoisonError::into_inner) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 84 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 85 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 86 | impl State { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 87 | fn new(cfg: Config) -> Self { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 88 | let mut st = Self { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 89 | cfg, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 90 | next: 1, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 91 | leases: BTreeMap::new(), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 92 | intents: BTreeMap::new(), |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 93 | cap: Capacity { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 94 | ram_budget: Bytes::ZERO, |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 95 | ram_live: Bytes::ZERO, |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 96 | ram_reserve: Bytes::ZERO, |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 97 | cores: 1, |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 98 | disks: BTreeMap::new(), |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 99 | pressure: false, |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 100 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 101 | baselines: BTreeMap::new(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 102 | conns: 0, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 103 | idle_since: Instant::now(), |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 104 | }; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 105 | st.apply_budget(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 106 | st |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 107 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 108 | |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 109 | fn apply_budget(&mut self) { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 110 | let (total, live) = sys::memory(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 111 | self.cap.ram_budget = self.cfg.budget.ram.resolve(total); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 112 | self.cap.ram_live = live; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 113 | self.cap.ram_reserve = self.cfg.budget.ram_reserve; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 114 | self.cap.cores = self.cfg.budget.cores.resolve(sys::cores()); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 115 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 116 | |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 117 | /// running leases keep the heaviness they started with, so a reload can't |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 118 | /// retroactively put two heavies side by side |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 119 | fn reload(&mut self, cfg: Config) { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 120 | self.cfg = cfg; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 121 | self.apply_budget(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 122 | let queued: Vec<(LeaseId, Need)> = self |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 123 | .leases |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 124 | .iter() |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 125 | .filter(|(_, l)| l.run.is_none()) |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 126 | .map(|(id, l)| (*id, l.req.need)) |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 127 | .collect(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 128 | for (id, need) in queued { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 129 | let heavy = self.heavy(&need); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 130 | if let Some(l) = self.leases.get_mut(&id) { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 131 | l.heavy = heavy; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 132 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 133 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 134 | self.refresh(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 135 | self.reschedule(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 136 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 137 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 138 | fn heavy(&self, need: &Need) -> bool { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 139 | let h = &self.cfg.heavy; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 140 | need.ram >= h.ram || need.disk >= h.disk || need.cores >= h.cores.resolve(sys::cores()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 141 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 142 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 143 | /// capped at a tenth of the fs, or a 16G tmpfs /tmp with a 20G reserve |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 144 | /// admits nothing ever |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 145 | fn reserve(&self, size: Bytes) -> Bytes { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 146 | self.cfg.budget.disk_reserve.min(Bytes(size.0 / 10)) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 147 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 148 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 149 | fn fs_for(&mut self, cwd: &PathBuf) -> Vec<FsId> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 150 | let mut ids = Vec::new(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 151 | for path in std::iter::once(cwd).chain(&self.cfg.budget.disk_paths) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 152 | if let Some(fs) = sys::fs(path) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 153 | if !ids.contains(&fs.id) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 154 | ids.push(fs.id); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 155 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 156 | let reserve = self.reserve(fs.size); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 157 | self.cap.disks.entry(fs.id).or_insert(Disk { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 158 | free: fs.free, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 159 | reserve, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 160 | consumed: Bytes::ZERO, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 161 | }); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 162 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 163 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 164 | ids |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 165 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 166 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 167 | fn refresh(&mut self) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 168 | let (_, live) = sys::memory(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 169 | self.cap.ram_live = live; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 170 | self.cap.pressure = self |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 171 | .cfg |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 172 | .budget |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 173 | .psi_max |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 174 | .is_some_and(|max| sys::pressure().is_some_and(|p| p > max)); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 175 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 176 | let mut paths: Vec<PathBuf> = self.leases.values().map(|l| l.req.cwd.clone()).collect(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 177 | paths.extend(self.cfg.budget.disk_paths.iter().cloned()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 178 | let mut disks = BTreeMap::new(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 179 | for path in paths { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 180 | if let Some(fs) = sys::fs(&path) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 181 | disks.insert(fs.id, (fs.free, self.reserve(fs.size))); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 182 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 183 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 184 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 185 | let busy: Vec<FsId> = self |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 186 | .leases |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 187 | .values() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 188 | .filter(|l| l.run.is_some()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 189 | .flat_map(|l| l.fs.iter().copied()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 190 | .collect(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 191 | self.baselines.retain(|id, _| busy.contains(id)); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 192 | self.cap.disks = disks |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 193 | .into_iter() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 194 | .map(|(id, (free, reserve))| { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 195 | let consumed = if busy.contains(&id) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 196 | self.baselines |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 197 | .entry(id) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 198 | .or_insert(free) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 199 | .saturating_sub(free) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 200 | } else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 201 | Bytes::ZERO |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 202 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 203 | ( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 204 | id, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 205 | Disk { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 206 | free, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 207 | reserve, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 208 | consumed, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 209 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 210 | ) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 211 | }) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 212 | .collect(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 213 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 214 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 215 | fn running(&self) -> Vec<Running> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 216 | self.leases |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 217 | .values() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 218 | .filter_map(|l| { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 219 | let run = l.run.as_ref()?; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 220 | Some(Running { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 221 | need: l.req.need, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 222 | heavy: l.heavy, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 223 | fs: l.fs.clone(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 224 | rss: run.rss, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 225 | remaining: l.req.need.time.saturating_sub(run.started.elapsed()), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 226 | }) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 227 | }) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 228 | .collect() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 229 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 230 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 231 | fn reschedule(&mut self) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 232 | let queue: Vec<Queued> = self |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 233 | .leases |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 234 | .iter() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 235 | .filter(|(_, l)| l.run.is_none()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 236 | .map(|(id, l)| Queued { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 237 | id: *id, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 238 | need: l.req.need, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 239 | heavy: l.heavy, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 240 | fs: l.fs.clone(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 241 | }) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 242 | .collect(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 243 | if queue.is_empty() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 244 | return; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 245 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 246 | let verdicts = sched::plan( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 247 | &queue, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 248 | &self.running(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 249 | &self.cap, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 250 | self.cfg.queue.backfill_max_time.0, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 251 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 252 | let cores = self.cap.cores; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 253 | let mut position = 0; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 254 | for (id, verdict) in verdicts { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 255 | match verdict { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 256 | Verdict::Admit => { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 257 | let (extra, extra_ram) = self.bonus(id); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 258 | let Some(lease) = self.leases.get_mut(&id) else { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 259 | continue; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 260 | }; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 261 | lease.req.need.ram += extra_ram; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 262 | let granted = lease.req.need.cores.clamp(1, cores.max(1)) + extra; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 263 | lease.run = Some(Run { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 264 | started: Instant::now(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 265 | rss: Bytes::ZERO, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 266 | reminded: None, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 267 | cores: granted, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 268 | }); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 269 | let _ = proto::send( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 270 | &mut lease.out, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 271 | &Event::Granted { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 272 | lease: id, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 273 | cores: granted, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 274 | ram: lease.req.need.ram, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 275 | heavy: lease.heavy, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 276 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 277 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 278 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 279 | Verdict::Wait(reason) => { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 280 | let Some(lease) = self.leases.get_mut(&id) else { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 281 | continue; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 282 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 283 | position += 1; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 284 | lease.waiting = Some(reason); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 285 | if lease.last_pos != position { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 286 | lease.last_pos = position; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 287 | let _ = proto::send( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 288 | &mut lease.out, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 289 | &Event::Queued { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 290 | lease: id, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 291 | position, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 292 | reason: Some(reason), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 293 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 294 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 295 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 296 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 297 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 298 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 299 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 300 | |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 301 | fn held_by(&self, session: &str) -> (u32, Bytes) { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 302 | self.leases |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 303 | .values() |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 304 | .filter(|l| l.req.session.as_deref() == Some(session)) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 305 | .fold((0, Bytes::ZERO), |(c, r), l| { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 306 | ( |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 307 | c + l.run.as_ref().map_or(l.req.need.cores, |run| run.cores), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 308 | r + l.req.need.ram, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 309 | ) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 310 | }) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 311 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 312 | |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 313 | /// idle cores the lease can have on top of its ask, minus whatever other |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 314 | /// sessions said they'll want and don't hold yet. capped by ram at the |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 315 | /// lease's own ram per core, since every extra job eats its share |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 316 | fn bonus(&self, id: LeaseId) -> (u32, Bytes) { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 317 | let Some(lease) = self.leases.get(&id) else { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 318 | return (0, Bytes::ZERO); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 319 | }; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 320 | if lease.req.exact || !self.cfg.queue.elastic { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 321 | return (0, Bytes::ZERO); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 322 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 323 | let need = lease.req.need; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 324 | let (taken, queued_ram) = self.leases.iter().filter(|(k, _)| **k != id).fold( |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 325 | (need.cores, need.ram), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 326 | |(c, r), (_, l)| match &l.run { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 327 | Some(run) => (c + run.cores, r), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 328 | None => (c + l.req.need.cores, r + l.req.need.ram), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 329 | }, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 330 | ); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 331 | let mut spare = self.cap.cores.saturating_sub(taken); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 332 | let mut ram_spare = sched::headroom(&self.running(), &self.cap) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 333 | .0 |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 334 | .saturating_sub(queued_ram); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 335 | for (session, intent) in &self.intents { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 336 | if lease.req.session.as_deref() == Some(session) { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 337 | continue; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 338 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 339 | let (cores, ram) = self.held_by(session); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 340 | spare = spare.saturating_sub(intent.need.cores.saturating_sub(cores)); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 341 | ram_spare = ram_spare.saturating_sub(intent.need.ram.saturating_sub(ram)); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 342 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 343 | let per_core = need.ram.0 / u64::from(need.cores.max(1)); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 344 | let by_ram = ram_spare |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 345 | .0 |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 346 | .checked_div(per_core) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 347 | .map_or(spare, |n| n as u32); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 348 | let extra = spare.min(by_ram); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 349 | (extra, Bytes(per_core * u64::from(extra))) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 350 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 351 | |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 352 | /// explicit intents set the size, builds only keep one alive or start a |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 353 | /// default one sized like themselves |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 354 | fn intend(&mut self, session: &str, need: Need, explicit: bool) { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 355 | let until = Instant::now() + self.cfg.queue.intent_ttl.0; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 356 | match self.intents.get_mut(session) { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 357 | Some(i) => { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 358 | i.until = until; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 359 | if explicit { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 360 | i.need = need; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 361 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 362 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 363 | None => { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 364 | self.intents |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 365 | .insert(session.to_owned(), Intent { need, until }); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 366 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 367 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 368 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 369 | |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 370 | fn expire_intents(&mut self) { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 371 | let had = !self.intents.is_empty(); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 372 | let now = Instant::now(); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 373 | self.intents.retain(|_, i| i.until > now); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 374 | if had && self.intents.is_empty() && self.leases.is_empty() { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 375 | self.idle_since = now; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 376 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 377 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 378 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 379 | fn remove(&mut self, id: LeaseId) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 380 | if let Some(lease) = self.leases.remove(&id) { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 381 | if let Some(s) = &lease.req.session |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 382 | && let Some(i) = self.intents.get_mut(s) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 383 | { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 384 | i.until = Instant::now() + self.cfg.queue.intent_ttl.0; |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 385 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 386 | for mut w in lease.watchers { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 387 | let _ = proto::send(&mut w, &Event::Fired { overdue: false }); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 388 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 389 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 390 | if self.leases.is_empty() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 391 | self.idle_since = Instant::now(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 392 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 393 | self.refresh(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 394 | self.reschedule(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 395 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 396 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 397 | fn fire_overdue(&mut self) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 398 | let grace = self.cfg.overrun.time_grace; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 399 | for lease in self.leases.values_mut() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 400 | if !lease.watchers.is_empty() && lease.overdue(grace) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 401 | for mut w in lease.watchers.drain(..) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 402 | let _ = proto::send(&mut w, &Event::Fired { overdue: true }); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 403 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 404 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 405 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 406 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 407 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 408 | fn info(&self) -> Vec<LeaseInfo> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 409 | let grace = self.cfg.overrun.time_grace; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 410 | let mut position = 0; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 411 | self.leases |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 412 | .iter() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 413 | .map(|(id, l)| { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 414 | if l.run.is_none() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 415 | position += 1; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 416 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 417 | LeaseInfo { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 418 | id: *id, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 419 | cmd: l.req.cmd.clone(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 420 | cwd: l.req.cwd.clone(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 421 | session: l.req.session.clone(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 422 | need: l.req.need, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 423 | heavy: l.heavy, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 424 | running: l.run.is_some(), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 425 | granted: l.run.as_ref().map_or(0, |r| r.cores), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 426 | elapsed: l.elapsed().as_secs(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 427 | rss: l.run.as_ref().map_or(Bytes::ZERO, |r| r.rss), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 428 | overdue: l.overdue(grace), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 429 | waiting: l.waiting.filter(|_| l.run.is_none()), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 430 | position: if l.run.is_none() { position } else { 0 }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 431 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 432 | }) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 433 | .collect() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 434 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 435 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 436 | fn status(&self) -> Status { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 437 | let (ram_free, cores_free) = sched::headroom(&self.running(), &self.cap); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 438 | let now = Instant::now(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 439 | Status { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 440 | leases: self.info(), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 441 | intents: self |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 442 | .intents |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 443 | .iter() |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 444 | .map(|(session, i)| IntentInfo { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 445 | session: session.clone(), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 446 | need: i.need, |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 447 | expires_in: i.until.saturating_duration_since(now).as_secs(), |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 448 | }) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 449 | .collect(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 450 | ram_free, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 451 | ram_budget: self.cap.ram_budget, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 452 | cores_free, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 453 | cores: self.cap.cores, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 454 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 455 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 456 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 457 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 458 | fn serve(shared: &Shared, mut stream: UnixStream) -> io::Result<()> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 459 | let mut reader = Reader::new(&stream)?; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 460 | let Some(req) = reader.recv::<Request>()? else { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 461 | return Ok(()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 462 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 463 | match req { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 464 | Request::Acquire(req) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 465 | let id = { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 466 | let mut st = lock(shared); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 467 | let id = st.next; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 468 | st.next += 1; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 469 | let fs = st.fs_for(&req.cwd); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 470 | let heavy = st.heavy(&req.need); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 471 | if let Some(s) = req.session.clone() { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 472 | st.intend(&s, req.need, false); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 473 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 474 | st.leases.insert( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 475 | id, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 476 | Lease { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 477 | req, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 478 | heavy, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 479 | fs, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 480 | queued_at: Instant::now(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 481 | run: None, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 482 | out: stream.try_clone()?, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 483 | waiting: None, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 484 | last_pos: 0, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 485 | watchers: Vec::new(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 486 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 487 | ); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 488 | st.reschedule(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 489 | id |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 490 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 491 | hold(shared, id, &mut reader); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 492 | lock(shared).remove(id); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 493 | Ok(()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 494 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 495 | Request::Expect { session, need } => { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 496 | let mut st = lock(shared); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 497 | if let Some(need) = need { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 498 | st.intend(&session, need, true); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 499 | } else { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 500 | st.intents.remove(&session); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 501 | if st.intents.is_empty() && st.leases.is_empty() { |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 502 | st.idle_since = Instant::now(); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 503 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 504 | } |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 505 | drop(st); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 506 | proto::send(&mut stream, &Event::Ok) |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 507 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 508 | Request::Status => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 509 | let status = lock(shared).status(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 510 | proto::send(&mut stream, &Event::Status(status)) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 511 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 512 | Request::Kill { lease } => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 513 | let mut st = lock(shared); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 514 | let reply = match st.leases.get_mut(&lease) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 515 | Some(l) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 516 | let _ = proto::send(&mut l.out, &Event::Kill); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 517 | Event::Ok |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 518 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 519 | None => Event::Err { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 520 | msg: format!("no lease {lease}"), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 521 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 522 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 523 | drop(st); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 524 | proto::send(&mut stream, &reply) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 525 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 526 | Request::Extend { lease, by } => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 527 | let mut st = lock(shared); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 528 | let reply = match st.leases.get_mut(&lease) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 529 | Some(l) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 530 | l.req.need.time += by.0; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 531 | if let Some(run) = &mut l.run { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 532 | run.reminded = None; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 533 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 534 | Event::Ok |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 535 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 536 | None => Event::Err { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 537 | msg: format!("no lease {lease}"), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 538 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 539 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 540 | drop(st); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 541 | proto::send(&mut stream, &reply) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 542 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 543 | Request::Watch { lease } => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 544 | let mut st = lock(shared); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 545 | let grace = st.cfg.overrun.time_grace; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 546 | match st.leases.get_mut(&lease) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 547 | Some(l) if l.overdue(grace) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 548 | proto::send(&mut stream, &Event::Fired { overdue: true }) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 549 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 550 | Some(l) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 551 | l.watchers.push(stream); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 552 | Ok(()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 553 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 554 | None => proto::send(&mut stream, &Event::Fired { overdue: false }), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 555 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 556 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 557 | Request::Overdue { session } => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 558 | let mut st = lock(shared); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 559 | let grace = st.cfg.overrun.time_grace; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 560 | let every = st.cfg.overrun.remind_every.0; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 561 | let mut due = Vec::new(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 562 | for (id, l) in &mut st.leases { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 563 | if l.req.session.as_deref() != Some(&session) || !l.overdue(grace) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 564 | continue; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 565 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 566 | let Some(run) = &mut l.run else { continue }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 567 | if run.reminded.is_some_and(|t| t.elapsed() < every) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 568 | continue; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 569 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 570 | run.reminded = Some(Instant::now()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 571 | due.push(*id); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 572 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 573 | let leases = st |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 574 | .info() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 575 | .into_iter() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 576 | .filter(|i| due.contains(&i.id)) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 577 | .collect(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 578 | drop(st); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 579 | proto::send(&mut stream, &Event::Overdue { leases }) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 580 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 581 | Request::Started { .. } | Request::Usage { .. } | Request::Done => proto::send( |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 582 | &mut stream, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 583 | &Event::Err { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 584 | msg: "no lease on this connection".into(), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 585 | }, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 586 | ), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 587 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 588 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 589 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 590 | /// a client that vanishes without saying done got sigkilled or similar, and |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 591 | /// pdeathsig only reaches its direct child. take the rest of the group down |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 592 | /// so a headless build doesn't keep eating ram off the books |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 593 | fn hold(shared: &Shared, id: LeaseId, reader: &mut Reader) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 594 | let mut pgid = None; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 595 | let clean = loop { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 596 | match reader.recv::<Request>() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 597 | Ok(Some(Request::Usage { rss })) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 598 | if let Some(run) = lock(shared) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 599 | .leases |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 600 | .get_mut(&id) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 601 | .and_then(|l| l.run.as_mut()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 602 | { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 603 | run.rss = rss; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 604 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 605 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 606 | Ok(Some(Request::Started { pid })) => pgid = Some(pid), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 607 | Ok(Some(Request::Done)) => break true, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 608 | Ok(Some(_)) => {} |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 609 | Ok(None) | Err(_) => break false, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 610 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 611 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 612 | if !clean && let Some(pgid) = pgid.filter(|p| *p > 1) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 613 | unsafe { libc::kill(-pgid, libc::SIGTERM) }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 614 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 615 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 616 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 617 | fn tick(shared: &Shared) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 618 | loop { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 619 | thread::sleep(Duration::from_secs(1)); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 620 | let mut st = lock(shared); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 621 | st.refresh(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 622 | st.reschedule(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 623 | st.fire_overdue(); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 624 | st.expire_intents(); |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 625 | if st.conns == 0 |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 626 | && st.leases.is_empty() |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 627 | && st.intents.is_empty() |
ecc1cf8df0f5 | atagen | 2026-09-27 13:36 UTC | 628 | && st.idle_since.elapsed() >= st.cfg.queue.linger.0 |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 629 | { |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 630 | // systemd owns the socket and queues whoever knocks while we're |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 631 | // leaving, so there's nothing to tidy |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 632 | if ACTIVATED.load(Ordering::Relaxed) { |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 633 | process::exit(0); |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 634 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 635 | // a spawner holds this while it clears a stale socket, so taking it |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 636 | // here stops us unlinking our successor's socket on the way out |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 637 | if let Some(_guard) = try_spawn_lock() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 638 | let _ = fs::remove_file(paths::socket()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 639 | process::exit(0); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 640 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 641 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 642 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 643 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 644 | |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 645 | /// watches the directory, not the file. editors save by renaming over it, |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 646 | /// which would orphan a watch on the old inode after the first edit |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 647 | #[cfg(target_os = "linux")] |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 648 | fn watch_config(shared: &Shared) { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 649 | use std::os::unix::ffi::OsStrExt; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 650 | |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 651 | let path = paths::config_file(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 652 | let (Some(dir), Some(name)) = (path.parent(), path.file_name()) else { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 653 | return; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 654 | }; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 655 | let _ = fs::create_dir_all(dir); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 656 | let Ok(cdir) = std::ffi::CString::new(dir.as_os_str().as_bytes()) else { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 657 | return; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 658 | }; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 659 | let fd = unsafe { libc::inotify_init1(libc::IN_CLOEXEC) }; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 660 | if fd < 0 { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 661 | return; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 662 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 663 | let mask = libc::IN_CLOSE_WRITE |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 664 | | libc::IN_MOVED_TO |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 665 | | libc::IN_MOVED_FROM |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 666 | | libc::IN_CREATE |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 667 | | libc::IN_DELETE; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 668 | if unsafe { libc::inotify_add_watch(fd, cdir.as_ptr(), mask) } < 0 { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 669 | return; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 670 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 671 | |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 672 | let header = std::mem::size_of::<libc::inotify_event>(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 673 | let mut buf = [0u8; 4096]; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 674 | loop { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 675 | let n = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) }; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 676 | let Ok(n) = usize::try_from(n) else { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 677 | return; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 678 | }; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 679 | let mut hit = false; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 680 | let mut off = 0; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 681 | while off + header <= n { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 682 | let ev: libc::inotify_event = |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 683 | unsafe { std::ptr::read_unaligned(buf.as_ptr().add(off).cast()) }; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 684 | let len = ev.len as usize; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 685 | let raw = &buf[off + header..(off + header + len).min(n)]; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 686 | let ev_name = raw.split(|b| *b == 0).next().unwrap_or_default(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 687 | hit |= ev_name == name.as_bytes(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 688 | off += header + len; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 689 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 690 | if !hit { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 691 | continue; |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 692 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 693 | thread::sleep(Duration::from_millis(100)); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 694 | match Config::load() { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 695 | Ok(cfg) => { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 696 | lock(shared).reload(cfg); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 697 | eprintln!("roundabout: config reloaded"); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 698 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 699 | Err(e) => eprintln!("roundabout: kept the old config, {e}"), |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 700 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 701 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 702 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 703 | |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 704 | /// creates the file if it's gone, else a wiped runtime dir leaves us idling |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 705 | /// forever unable to take the lock we need to exit |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 706 | fn try_spawn_lock() -> Option<client::Flock> { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 707 | let file = fs::OpenOptions::new() |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 708 | .create(true) |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 709 | .truncate(false) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 710 | .write(true) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 711 | .open(paths::spawn_lock()) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 712 | .ok()?; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 713 | client::Flock::try_exclusive(file) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 714 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 715 | |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 716 | static ACTIVATED: AtomicBool = AtomicBool::new(false); |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 717 | |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 718 | fn inherited() -> Option<UnixListener> { |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 719 | let ours = std::env::var("LISTEN_PID").ok()?.parse::<u32>().ok()? == process::id(); |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 720 | if !ours || std::env::var("LISTEN_FDS").ok()? != "1" { |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 721 | return None; |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 722 | } |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 723 | // SAFETY: sd_listen_fds protocol, fd 3 is the listening socket |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 724 | Some(unsafe { UnixListener::from_raw_fd(3) }) |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 725 | } |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 726 | |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 727 | fn bind() -> UnixListener { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 728 | // fork once more so the client that spawned us can reap its child now |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 729 | // instead of carrying a zombie for the length of its build |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 730 | match unsafe { libc::fork() } { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 731 | -1 => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 732 | eprintln!("roundabout: fork failed: {}", io::Error::last_os_error()); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 733 | process::exit(1); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 734 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 735 | 0 => {} |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 736 | _ => process::exit(0), |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 737 | } |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 738 | match UnixListener::bind(paths::socket()) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 739 | Ok(l) => l, |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 740 | Err(e) if e.kind() == io::ErrorKind::AddrInUse && client::connect().is_some() => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 741 | process::exit(0) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 742 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 743 | Err(e) => { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 744 | eprintln!("roundabout: bind: {e}"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 745 | process::exit(1); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 746 | } |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 747 | } |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 748 | } |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 749 | |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 750 | pub fn main(cfg: Config) -> ! { |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 751 | let listener = if let Some(l) = inherited() { |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 752 | ACTIVATED.store(true, Ordering::Relaxed); |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 753 | l |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 754 | } else { |
61c6e94355d9 | atagen | 2026-09-27 03:59 UTC | 755 | bind() |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 756 | }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 757 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 758 | let shared: Shared = Arc::new(Mutex::new(State::new(cfg))); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 759 | { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 760 | let shared = shared.clone(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 761 | thread::spawn(move || tick(&shared)); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 762 | } |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 763 | #[cfg(target_os = "linux")] |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 764 | { |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 765 | let shared = shared.clone(); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 766 | thread::spawn(move || watch_config(&shared)); |
56a853ebf254 | atagen | 2026-09-27 03:59 UTC | 767 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 768 | |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 769 | for stream in listener.incoming() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 770 | let Ok(stream) = stream else { continue }; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 771 | lock(&shared).conns += 1; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 772 | let shared = shared.clone(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 773 | thread::spawn(move || { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 774 | if let Err(e) = serve(&shared, stream) { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 775 | eprintln!("roundabout: {e}"); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 776 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 777 | let mut st = lock(&shared); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 778 | st.conns -= 1; |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 779 | if st.leases.is_empty() { |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 780 | st.idle_since = Instant::now(); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 781 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 782 | }); |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 783 | } |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 784 | process::exit(0) |
6f5b84fad3ef | atagen | 2026-09-27 03:59 UTC | 785 | } |