atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
f48daa8adf591a211fbfdca2c98805b04b0db3c6
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
8b8e83329a1datagen2026-10-12 12:01 UTC2
//! lock, so one slow turn never holds up anyone else's. Nothing ever waits on a run's lock while
8b8e83329a1datagen2026-10-12 12:01 UTC3
//! holding the table's, which is what keeps the two from deadlocking.
159f0998263batagen2026-10-12 11:01 UTC4
159f0998263batagen2026-10-12 11:01 UTC5
use std::{
8b8e83329a1datagen2026-10-12 12:01 UTC6
    collections::{HashMap, hash_map::Entry},
8b8e83329a1datagen2026-10-12 12:01 UTC7
    sync::{
8b8e83329a1datagen2026-10-12 12:01 UTC8
        Arc, Mutex, MutexGuard, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard,
8b8e83329a1datagen2026-10-12 12:01 UTC9
        TryLockError,
8b8e83329a1datagen2026-10-12 12:01 UTC10
    },
159f0998263batagen2026-10-12 11:01 UTC11
    time::{Duration, Instant},
159f0998263batagen2026-10-12 11:01 UTC12
};
159f0998263batagen2026-10-12 11:01 UTC13
159f0998263batagen2026-10-12 11:01 UTC14
use crate::game::Run;
159f0998263batagen2026-10-12 11:01 UTC15
159f0998263batagen2026-10-12 11:01 UTC16
pub type Slot = Arc<Mutex<Run>>;
159f0998263batagen2026-10-12 11:01 UTC17
159f0998263batagen2026-10-12 11:01 UTC18
#[derive(Default)]
159f0998263batagen2026-10-12 11:01 UTC19
pub struct Registry {
159f0998263batagen2026-10-12 11:01 UTC20
    runs: RwLock<HashMap<String, Slot>>,
159f0998263batagen2026-10-12 11:01 UTC21
}
159f0998263batagen2026-10-12 11:01 UTC22
8b8e83329a1datagen2026-10-12 12:01 UTC23
/// A run's lock as it was found. A panic mid-turn poisons it, and that run cannot be trusted.
8b8e83329a1datagen2026-10-12 12:01 UTC24
pub enum Lock<'a> {
8b8e83329a1datagen2026-10-12 12:01 UTC25
    Held(MutexGuard<'a, Run>),
8b8e83329a1datagen2026-10-12 12:01 UTC26
    Poisoned(MutexGuard<'a, Run>),
8b8e83329a1datagen2026-10-12 12:01 UTC27
}
8b8e83329a1datagen2026-10-12 12:01 UTC28
8b8e83329a1datagen2026-10-12 12:01 UTC29
impl<'a> Lock<'a> {
8b8e83329a1datagen2026-10-12 12:01 UTC30
    pub fn take(slot: &'a Slot) -> Self {
8b8e83329a1datagen2026-10-12 12:01 UTC31
        match slot.lock() {
8b8e83329a1datagen2026-10-12 12:01 UTC32
            Ok(run) => Self::Held(run),
8b8e83329a1datagen2026-10-12 12:01 UTC33
            Err(poisoned) => Self::Poisoned(poisoned.into_inner()),
8b8e83329a1datagen2026-10-12 12:01 UTC34
        }
8b8e83329a1datagen2026-10-12 12:01 UTC35
    }
8b8e83329a1datagen2026-10-12 12:01 UTC36
}
8b8e83329a1datagen2026-10-12 12:01 UTC37
8b8e83329a1datagen2026-10-12 12:01 UTC38
fn peek(slot: &Slot) -> Option<MutexGuard<'_, Run>> {
8b8e83329a1datagen2026-10-12 12:01 UTC39
    match slot.try_lock() {
8b8e83329a1datagen2026-10-12 12:01 UTC40
        Ok(run) => Some(run),
8b8e83329a1datagen2026-10-12 12:01 UTC41
        Err(TryLockError::Poisoned(poisoned)) => Some(poisoned.into_inner()),
8b8e83329a1datagen2026-10-12 12:01 UTC42
        Err(TryLockError::WouldBlock) => None,
8b8e83329a1datagen2026-10-12 12:01 UTC43
    }
159f0998263batagen2026-10-12 11:01 UTC44
}
159f0998263batagen2026-10-12 11:01 UTC45
159f0998263batagen2026-10-12 11:01 UTC46
impl Registry {
159f0998263batagen2026-10-12 11:01 UTC47
    pub fn get(&self, id: &str) -> Option<Slot> {
159f0998263batagen2026-10-12 11:01 UTC48
        self.read().get(id).cloned()
159f0998263batagen2026-10-12 11:01 UTC49
    }
159f0998263batagen2026-10-12 11:01 UTC50
159f0998263batagen2026-10-12 11:01 UTC51
    pub fn len(&self) -> usize {
159f0998263batagen2026-10-12 11:01 UTC52
        self.read().len()
159f0998263batagen2026-10-12 11:01 UTC53
    }
159f0998263batagen2026-10-12 11:01 UTC54
8b8e83329a1datagen2026-10-12 12:01 UTC55
    pub fn contains(&self, id: &str) -> bool {
8b8e83329a1datagen2026-10-12 12:01 UTC56
        self.read().contains_key(id)
159f0998263batagen2026-10-12 11:01 UTC57
    }
159f0998263batagen2026-10-12 11:01 UTC58
8b8e83329a1datagen2026-10-12 12:01 UTC59
    /// Refuses to replace a live run, so a colliding id can never evict one.
8b8e83329a1datagen2026-10-12 12:01 UTC60
    pub fn insert_new(&self, run: Run) -> bool {
8b8e83329a1datagen2026-10-12 12:01 UTC61
        match self.write().entry(run.id().to_owned()) {
8b8e83329a1datagen2026-10-12 12:01 UTC62
            Entry::Occupied(_) => false,
8b8e83329a1datagen2026-10-12 12:01 UTC63
            Entry::Vacant(slot) => {
8b8e83329a1datagen2026-10-12 12:01 UTC64
                slot.insert(Arc::new(Mutex::new(run)));
8b8e83329a1datagen2026-10-12 12:01 UTC65
                true
8b8e83329a1datagen2026-10-12 12:01 UTC66
            }
8b8e83329a1datagen2026-10-12 12:01 UTC67
        }
159f0998263batagen2026-10-12 11:01 UTC68
    }
159f0998263batagen2026-10-12 11:01 UTC69
8b8e83329a1datagen2026-10-12 12:01 UTC70
    pub fn remove(&self, id: &str) {
8b8e83329a1datagen2026-10-12 12:01 UTC71
        self.write().remove(id);
159f0998263batagen2026-10-12 11:01 UTC72
    }
159f0998263batagen2026-10-12 11:01 UTC73
8b8e83329a1datagen2026-10-12 12:01 UTC74
    /// Every live run not mid-turn. A busy run is skipped rather than waited for.
159f0998263batagen2026-10-12 11:01 UTC75
    pub fn each<T>(&self, mut visit: impl FnMut(&Run) -> T) -> Vec<T> {
159f0998263batagen2026-10-12 11:01 UTC76
        let slots: Vec<Slot> = self.read().values().cloned().collect();
8b8e83329a1datagen2026-10-12 12:01 UTC77
        slots
8b8e83329a1datagen2026-10-12 12:01 UTC78
            .iter()
8b8e83329a1datagen2026-10-12 12:01 UTC79
            .filter_map(|slot| peek(slot).map(|run| visit(&run)))
8b8e83329a1datagen2026-10-12 12:01 UTC80
            .collect()
159f0998263batagen2026-10-12 11:01 UTC81
    }
159f0998263batagen2026-10-12 11:01 UTC82
159f0998263batagen2026-10-12 11:01 UTC83
    pub fn evict_idle(&self, now: Instant, ttl: Duration) -> Vec<Slot> {
8b8e83329a1datagen2026-10-12 12:01 UTC84
        let idle: Vec<String> = self
8b8e83329a1datagen2026-10-12 12:01 UTC85
            .read()
159f0998263batagen2026-10-12 11:01 UTC86
            .iter()
159f0998263batagen2026-10-12 11:01 UTC87
            .filter(|(_, slot)| {
8b8e83329a1datagen2026-10-12 12:01 UTC88
                peek(slot).is_some_and(|run| now.duration_since(run.last_seen()) >= ttl)
159f0998263batagen2026-10-12 11:01 UTC89
            })
159f0998263batagen2026-10-12 11:01 UTC90
            .map(|(id, _)| id.clone())
159f0998263batagen2026-10-12 11:01 UTC91
            .collect();
8b8e83329a1datagen2026-10-12 12:01 UTC92
        let mut runs = self.write();
159f0998263batagen2026-10-12 11:01 UTC93
        idle.iter().filter_map(|id| runs.remove(id)).collect()
159f0998263batagen2026-10-12 11:01 UTC94
    }
159f0998263batagen2026-10-12 11:01 UTC95
8b8e83329a1datagen2026-10-12 12:01 UTC96
    fn read(&self) -> RwLockReadGuard<'_, HashMap<String, Slot>> {
159f0998263batagen2026-10-12 11:01 UTC97
        self.runs.read().unwrap_or_else(PoisonError::into_inner)
159f0998263batagen2026-10-12 11:01 UTC98
    }
159f0998263batagen2026-10-12 11:01 UTC99
8b8e83329a1datagen2026-10-12 12:01 UTC100
    fn write(&self) -> RwLockWriteGuard<'_, HashMap<String, Slot>> {
159f0998263batagen2026-10-12 11:01 UTC101
        self.runs.write().unwrap_or_else(PoisonError::into_inner)
159f0998263batagen2026-10-12 11:01 UTC102
    }
159f0998263batagen2026-10-12 11:01 UTC103
}