atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
159f0998263bdbc45fc96bfc40453947b3a44cc3
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git

Blame: src/http/registry.rs

159f0998263batagen2026-10-12 11:01 UTC1
//! The live runs. The table is only locked long enough to find a run, and each run has its own
159f0998263batagen2026-10-12 11:01 UTC2
//! lock, so one slow turn never holds up anyone else's.
159f0998263batagen2026-10-12 11:01 UTC3
159f0998263batagen2026-10-12 11:01 UTC4
use std::{
159f0998263batagen2026-10-12 11:01 UTC5
    collections::HashMap,
159f0998263batagen2026-10-12 11:01 UTC6
    sync::{Arc, Mutex, MutexGuard, PoisonError, RwLock, TryLockError},
159f0998263batagen2026-10-12 11:01 UTC7
    time::{Duration, Instant},
159f0998263batagen2026-10-12 11:01 UTC8
};
159f0998263batagen2026-10-12 11:01 UTC9
159f0998263batagen2026-10-12 11:01 UTC10
use crate::game::Run;
159f0998263batagen2026-10-12 11:01 UTC11
159f0998263batagen2026-10-12 11:01 UTC12
pub type Slot = Arc<Mutex<Run>>;
159f0998263batagen2026-10-12 11:01 UTC13
159f0998263batagen2026-10-12 11:01 UTC14
#[derive(Default)]
159f0998263batagen2026-10-12 11:01 UTC15
pub struct Registry {
159f0998263batagen2026-10-12 11:01 UTC16
    runs: RwLock<HashMap<String, Slot>>,
159f0998263batagen2026-10-12 11:01 UTC17
}
159f0998263batagen2026-10-12 11:01 UTC18
159f0998263batagen2026-10-12 11:01 UTC19
/// A panic mid-request must not take the run down with it, so a poisoned lock is recovered.
159f0998263batagen2026-10-12 11:01 UTC20
pub fn hold(slot: &Slot) -> MutexGuard<'_, Run> {
159f0998263batagen2026-10-12 11:01 UTC21
    slot.lock().unwrap_or_else(PoisonError::into_inner)
159f0998263batagen2026-10-12 11:01 UTC22
}
159f0998263batagen2026-10-12 11:01 UTC23
159f0998263batagen2026-10-12 11:01 UTC24
impl Registry {
159f0998263batagen2026-10-12 11:01 UTC25
    pub fn get(&self, id: &str) -> Option<Slot> {
159f0998263batagen2026-10-12 11:01 UTC26
        self.read().get(id).cloned()
159f0998263batagen2026-10-12 11:01 UTC27
    }
159f0998263batagen2026-10-12 11:01 UTC28
159f0998263batagen2026-10-12 11:01 UTC29
    pub fn len(&self) -> usize {
159f0998263batagen2026-10-12 11:01 UTC30
        self.read().len()
159f0998263batagen2026-10-12 11:01 UTC31
    }
159f0998263batagen2026-10-12 11:01 UTC32
159f0998263batagen2026-10-12 11:01 UTC33
    pub fn insert(&self, run: Run) {
159f0998263batagen2026-10-12 11:01 UTC34
        let id = run.id.clone();
159f0998263batagen2026-10-12 11:01 UTC35
        self.write().insert(id, Arc::new(Mutex::new(run)));
159f0998263batagen2026-10-12 11:01 UTC36
    }
159f0998263batagen2026-10-12 11:01 UTC37
159f0998263batagen2026-10-12 11:01 UTC38
    pub fn remove(&self, id: &str) {
159f0998263batagen2026-10-12 11:01 UTC39
        self.write().remove(id);
159f0998263batagen2026-10-12 11:01 UTC40
    }
159f0998263batagen2026-10-12 11:01 UTC41
159f0998263batagen2026-10-12 11:01 UTC42
    pub fn contains(&self, id: &str) -> bool {
159f0998263batagen2026-10-12 11:01 UTC43
        self.read().contains_key(id)
159f0998263batagen2026-10-12 11:01 UTC44
    }
159f0998263batagen2026-10-12 11:01 UTC45
159f0998263batagen2026-10-12 11:01 UTC46
    /// Every live run, each visited under its own lock.
159f0998263batagen2026-10-12 11:01 UTC47
    pub fn each<T>(&self, mut visit: impl FnMut(&Run) -> T) -> Vec<T> {
159f0998263batagen2026-10-12 11:01 UTC48
        let slots: Vec<Slot> = self.read().values().cloned().collect();
159f0998263batagen2026-10-12 11:01 UTC49
        slots.iter().map(|slot| visit(&hold(slot))).collect()
159f0998263batagen2026-10-12 11:01 UTC50
    }
159f0998263batagen2026-10-12 11:01 UTC51
159f0998263batagen2026-10-12 11:01 UTC52
    /// Takes out the runs nobody has touched for `ttl`, and hands them back for the record.
159f0998263batagen2026-10-12 11:01 UTC53
    pub fn evict_idle(&self, now: Instant, ttl: Duration) -> Vec<Slot> {
159f0998263batagen2026-10-12 11:01 UTC54
        let mut runs = self.write();
159f0998263batagen2026-10-12 11:01 UTC55
        let idle: Vec<String> = runs
159f0998263batagen2026-10-12 11:01 UTC56
            .iter()
159f0998263batagen2026-10-12 11:01 UTC57
            .filter(|(_, slot)| {
159f0998263batagen2026-10-12 11:01 UTC58
                let idle = |run: &Run| now.duration_since(run.last_seen) >= ttl;
159f0998263batagen2026-10-12 11:01 UTC59
                match slot.try_lock() {
159f0998263batagen2026-10-12 11:01 UTC60
                    Ok(run) => idle(&run),
159f0998263batagen2026-10-12 11:01 UTC61
                    Err(TryLockError::Poisoned(poisoned)) => idle(&poisoned.into_inner()),
159f0998263batagen2026-10-12 11:01 UTC62
                    Err(TryLockError::WouldBlock) => false,
159f0998263batagen2026-10-12 11:01 UTC63
                }
159f0998263batagen2026-10-12 11:01 UTC64
            })
159f0998263batagen2026-10-12 11:01 UTC65
            .map(|(id, _)| id.clone())
159f0998263batagen2026-10-12 11:01 UTC66
            .collect();
159f0998263batagen2026-10-12 11:01 UTC67
        idle.iter().filter_map(|id| runs.remove(id)).collect()
159f0998263batagen2026-10-12 11:01 UTC68
    }
159f0998263batagen2026-10-12 11:01 UTC69
159f0998263batagen2026-10-12 11:01 UTC70
    fn read(&self) -> std::sync::RwLockReadGuard<'_, HashMap<String, Slot>> {
159f0998263batagen2026-10-12 11:01 UTC71
        self.runs.read().unwrap_or_else(PoisonError::into_inner)
159f0998263batagen2026-10-12 11:01 UTC72
    }
159f0998263batagen2026-10-12 11:01 UTC73
159f0998263batagen2026-10-12 11:01 UTC74
    fn write(&self) -> std::sync::RwLockWriteGuard<'_, HashMap<String, Slot>> {
159f0998263batagen2026-10-12 11:01 UTC75
        self.runs.write().unwrap_or_else(PoisonError::into_inner)
159f0998263batagen2026-10-12 11:01 UTC76
    }
159f0998263batagen2026-10-12 11:01 UTC77
}