use std::collections::HashMap;
use chronix_core::SegmentId;
use parking_lot::Mutex;
#[derive(Debug, Default)]
pub(crate) struct SegmentLeases {
held: Mutex<HashMap<SegmentId, u32>>,
}
impl SegmentLeases {
pub(crate) fn acquire<'a>(
&'a self,
ids: impl IntoIterator<Item = SegmentId>,
) -> SegmentLease<'a> {
let ids: Vec<SegmentId> = ids.into_iter().collect();
if !ids.is_empty() {
let mut held = self.held.lock();
for id in &ids {
*held.entry(*id).or_insert(0) += 1;
}
}
SegmentLease { leases: self, ids }
}
pub(crate) fn is_leased(&self, id: SegmentId) -> bool {
self.held.lock().contains_key(&id)
}
pub(crate) fn len(&self) -> usize {
self.held.lock().len()
}
}
#[derive(Debug)]
pub(crate) struct SegmentLease<'a> {
leases: &'a SegmentLeases,
ids: Vec<SegmentId>,
}
impl Drop for SegmentLease<'_> {
fn drop(&mut self) {
if self.ids.is_empty() {
return;
}
let mut held = self.leases.held.lock();
for id in &self.ids {
if let Some(n) = held.get_mut(id) {
*n -= 1;
if *n == 0 {
held.remove(id);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_lease_is_released_on_drop() {
let leases = SegmentLeases::default();
{
let _a = leases.acquire([SegmentId(1), SegmentId(2)]);
assert!(leases.is_leased(SegmentId(1)));
assert_eq!(leases.len(), 2);
}
assert!(!leases.is_leased(SegmentId(1)));
assert_eq!(leases.len(), 0);
}
#[test]
fn two_readers_of_one_segment_both_have_to_finish() {
let leases = SegmentLeases::default();
let a = leases.acquire([SegmentId(7)]);
let b = leases.acquire([SegmentId(7)]);
drop(a);
assert!(
leases.is_leased(SegmentId(7)),
"the second reader still holds the path"
);
drop(b);
assert!(!leases.is_leased(SegmentId(7)));
}
#[test]
fn an_empty_lease_costs_nothing() {
let leases = SegmentLeases::default();
let _l = leases.acquire([]);
assert_eq!(leases.len(), 0);
}
}