ref:main
//! Atomic counter of currently-busy job slots. The heartbeat reads this
//! to report `slots_busy` to the server. Each slot's `poller_loop`
//! increments when it claims a job and decrements when the executor
//! returns — via the RAII `Reservation` so a panic inside execute_job
//! still releases the slot.
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[derive(Clone, Default)]
pub struct SlotCounter {
busy: Arc<AtomicUsize>,
}
impl SlotCounter {
pub fn new() -> Self {
Self::default()
}
pub fn busy(&self) -> usize {
self.busy.load(Ordering::Relaxed)
}
/// Mark one slot busy until the returned guard is dropped.
pub fn reserve(&self) -> Reservation {
self.busy.fetch_add(1, Ordering::Relaxed);
Reservation {
busy: self.busy.clone(),
}
}
}
pub struct Reservation {
busy: Arc<AtomicUsize>,
}
impl Drop for Reservation {
fn drop(&mut self) {
self.busy.fetch_sub(1, Ordering::Relaxed);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn busy_starts_at_zero() {
let c = SlotCounter::new();
assert_eq!(c.busy(), 0);
}
#[test]
fn reservation_increments_and_drops_decrement() {
let c = SlotCounter::new();
let r1 = c.reserve();
assert_eq!(c.busy(), 1);
let r2 = c.reserve();
assert_eq!(c.busy(), 2);
drop(r2);
assert_eq!(c.busy(), 1);
drop(r1);
assert_eq!(c.busy(), 0);
}
}