diff --git a/src/shard.rs b/src/shard.rs index bf01c02..c493371 100644 --- a/src/shard.rs +++ b/src/shard.rs @@ -223,6 +223,7 @@ impl CacheShard Lifecycle for DefaultLifecycle { #[cfg(test)] mod tests { use super::*; + use crate::shard::SharedPlaceholder as _; use std::{ sync::{Arc, Barrier}, thread, @@ -1763,4 +1764,55 @@ mod tests { drop(guards); assert_eq!(cache.get(&2), None); } + + #[test] + fn test_guard_leak() { + let cache: Cache = Cache::new(8); + let guard1 = match cache.get_value_or_guard(&1, None) { + GuardResult::Guard(g) => g, + _ => panic!("expected guard"), + }; + let idx1 = guard1.shared().idx(); + drop(guard1); + let guard2 = match cache.get_value_or_guard(&1, None) { + GuardResult::Guard(g) => g, + _ => panic!("expected guard"), + }; + let idx2 = guard2.shared().idx(); + drop(guard2); + assert_eq!(idx1, idx2); + } + + // A real insert overwrites the placeholder in place, reusing its slab slot as + // a Resident. Dropping the now-stale guard must not free that slot, otherwise + // the live entry is evicted while the map still references it. + #[test] + fn test_guard_drop_after_overwrite_insert() { + let cache: Cache = Cache::new(8); + let guard = match cache.get_value_or_guard(&1, None) { + GuardResult::Guard(g) => g, + _ => panic!("expected guard"), + }; + cache.insert(1, 100); + assert_eq!(cache.get(&1), Some(100)); + drop(guard); + assert_eq!(cache.get(&1), Some(100)); + } + + // A remove frees the placeholder's slab slot, which a later insert reuses for a + // different key. Dropping the original guard must not free that slot again, or + // it evicts the unrelated key. + #[test] + fn test_guard_drop_after_remove_and_reuse() { + let cache: Cache = Cache::new(8); + let guard = match cache.get_value_or_guard(&1, None) { + GuardResult::Guard(g) => g, + _ => panic!("expected guard"), + }; + cache.remove(&1); + cache.insert(2, 222); + assert_eq!(cache.get(&2), Some(222)); + drop(guard); + assert_eq!(cache.get(&2), Some(222)); + } } diff --git a/src/sync_placeholder.rs b/src/sync_placeholder.rs index 8f09d11..5da45e2 100644 --- a/src/sync_placeholder.rs +++ b/src/sync_placeholder.rs @@ -93,6 +93,13 @@ pub struct PlaceholderGuard<'a, Key, Val, We, B, L> { inserted: bool, } +#[cfg(test)] +impl<'a, Key, Val, We, B, L> PlaceholderGuard<'a, Key, Val, We, B, L> { + pub fn shared(&self) -> &SharedPlaceholder { + &self.shared + } +} + #[derive(Debug)] enum Waiter { Thread {