use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Clone, Default)]
pub(crate) struct Outstanding {
count: Arc<AtomicU64>,
}
impl Outstanding {
pub(crate) fn track(&self) -> Ticket {
self.count.fetch_add(1, Ordering::SeqCst);
Ticket {
count: self.count.clone(),
}
}
pub(crate) fn count(&self) -> u64 {
self.count.load(Ordering::SeqCst)
}
}
pub(crate) struct Ticket {
count: Arc<AtomicU64>,
}
impl Drop for Ticket {
fn drop(&mut self) {
self.count.fetch_sub(1, Ordering::SeqCst);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn track_counts_until_the_ticket_drops() {
let outstanding = Outstanding::default();
assert_eq!(outstanding.count(), 0);
let ticket = outstanding.track();
assert_eq!(outstanding.count(), 1);
let second = outstanding.track();
assert_eq!(outstanding.count(), 2);
drop(ticket);
assert_eq!(outstanding.count(), 1);
drop(second);
assert_eq!(outstanding.count(), 0);
}
#[test]
fn a_panicking_worker_still_counts_out() {
let outstanding = Outstanding::default();
let tracked = outstanding.clone();
let handle = std::thread::spawn(move || {
let _ticket = tracked.track();
panic!("worker body died");
});
assert!(handle.join().is_err(), "the worker did panic");
assert_eq!(outstanding.count(), 0);
}
}