an http based roguelike rpg about jelqing
Blame: src/http/registry.rs
159f0998263b | atagen | 2026-10-12 11:01 UTC | 1 | //! The live runs. The table is only locked long enough to find a run, and each run has its own |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 2 | //! lock, so one slow turn never holds up anyone else's. Nothing ever waits on a run's lock while |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 3 | //! holding the table's, which is what keeps the two from deadlocking. |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 4 | |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 5 | use std::{ |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 6 | collections::{HashMap, hash_map::Entry}, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 7 | sync::{ |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 8 | Arc, Mutex, MutexGuard, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 9 | TryLockError, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 10 | }, |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 11 | time::{Duration, Instant}, |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 12 | }; |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 13 | |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 14 | use crate::game::Run; |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 15 | |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 16 | pub type Slot = Arc<Mutex<Run>>; |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 17 | |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 18 | #[derive(Default)] |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 19 | pub struct Registry { |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 20 | runs: RwLock<HashMap<String, Slot>>, |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 21 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 22 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 23 | /// A run's lock as it was found. A panic mid-turn poisons it, and that run cannot be trusted. |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 24 | pub enum Lock<'a> { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 25 | Held(MutexGuard<'a, Run>), |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 26 | Poisoned(MutexGuard<'a, Run>), |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 27 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 28 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 29 | impl<'a> Lock<'a> { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 30 | pub fn take(slot: &'a Slot) -> Self { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 31 | match slot.lock() { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 32 | Ok(run) => Self::Held(run), |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 33 | Err(poisoned) => Self::Poisoned(poisoned.into_inner()), |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 34 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 35 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 36 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 37 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 38 | fn peek(slot: &Slot) -> Option<MutexGuard<'_, Run>> { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 39 | match slot.try_lock() { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 40 | Ok(run) => Some(run), |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 41 | Err(TryLockError::Poisoned(poisoned)) => Some(poisoned.into_inner()), |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 42 | Err(TryLockError::WouldBlock) => None, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 43 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 44 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 45 | |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 46 | impl Registry { |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 47 | pub fn get(&self, id: &str) -> Option<Slot> { |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 48 | self.read().get(id).cloned() |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 49 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 50 | |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 51 | pub fn len(&self) -> usize { |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 52 | self.read().len() |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 53 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 54 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 55 | pub fn contains(&self, id: &str) -> bool { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 56 | self.read().contains_key(id) |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 57 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 58 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 59 | /// Refuses to replace a live run, so a colliding id can never evict one. |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 60 | pub fn insert_new(&self, run: Run) -> bool { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 61 | match self.write().entry(run.id().to_owned()) { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 62 | Entry::Occupied(_) => false, |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 63 | Entry::Vacant(slot) => { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 64 | slot.insert(Arc::new(Mutex::new(run))); |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 65 | true |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 66 | } |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 67 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 68 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 69 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 70 | pub fn remove(&self, id: &str) { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 71 | self.write().remove(id); |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 72 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 73 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 74 | /// Every live run not mid-turn. A busy run is skipped rather than waited for. |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 75 | pub fn each<T>(&self, mut visit: impl FnMut(&Run) -> T) -> Vec<T> { |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 76 | let slots: Vec<Slot> = self.read().values().cloned().collect(); |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 77 | slots |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 78 | .iter() |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 79 | .filter_map(|slot| peek(slot).map(|run| visit(&run))) |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 80 | .collect() |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 81 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 82 | |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 83 | pub fn evict_idle(&self, now: Instant, ttl: Duration) -> Vec<Slot> { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 84 | let idle: Vec<String> = self |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 85 | .read() |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 86 | .iter() |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 87 | .filter(|(_, slot)| { |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 88 | peek(slot).is_some_and(|run| now.duration_since(run.last_seen()) >= ttl) |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 89 | }) |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 90 | .map(|(id, _)| id.clone()) |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 91 | .collect(); |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 92 | let mut runs = self.write(); |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 93 | idle.iter().filter_map(|id| runs.remove(id)).collect() |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 94 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 95 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 96 | fn read(&self) -> RwLockReadGuard<'_, HashMap<String, Slot>> { |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 97 | self.runs.read().unwrap_or_else(PoisonError::into_inner) |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 98 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 99 | |
8b8e83329a1d | atagen | 2026-10-12 12:01 UTC | 100 | fn write(&self) -> RwLockWriteGuard<'_, HashMap<String, Slot>> { |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 101 | self.runs.write().unwrap_or_else(PoisonError::into_inner) |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 102 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 103 | } |