1use std::{
2 future::Future,
3 hash::{BuildHasher, Hash},
4 hint::unreachable_unchecked,
5 time::Duration,
6};
7
8use crate::{
9 linked_slab::Token,
10 options::{Options, OptionsBuilder},
11 shard::{CacheShard, InsertStrategy},
12 shim::rw_lock::RwLock,
13 sync_placeholder::SharedPlaceholder,
14 DefaultHashBuilder, Equivalent, Lifecycle, MemoryUsed, UnitWeighter, Weighter,
15};
16
17use crate::shard::EntryOrPlaceholder;
18pub use crate::sync_placeholder::{EntryAction, EntryResult, GuardResult, PlaceholderGuard};
19use crate::sync_placeholder::{JoinFuture, JoinResult};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub struct LockContention;
29
30impl std::fmt::Display for LockContention {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(f, "Lock Contention")
33 }
34}
35
36impl std::error::Error for LockContention {}
37
38pub struct Cache<
53 Key,
54 Val,
55 We = UnitWeighter,
56 B = DefaultHashBuilder,
57 L = DefaultLifecycle<Key, Val>,
58> {
59 hash_builder: B,
60 shards: Box<[RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>]>,
61 shards_mask: u64,
62}
63
64impl<Key: Eq + Hash, Val: Clone> Cache<Key, Val> {
65 pub fn new(items_capacity: usize) -> Self {
67 Self::with(
68 items_capacity,
69 items_capacity as u64,
70 Default::default(),
71 Default::default(),
72 Default::default(),
73 )
74 }
75}
76
77impl<Key: Eq + Hash, Val: Clone, We: Weighter<Key, Val> + Clone> Cache<Key, Val, We> {
78 pub fn with_weighter(
79 estimated_items_capacity: usize,
80 weight_capacity: u64,
81 weighter: We,
82 ) -> Self {
83 Self::with(
84 estimated_items_capacity,
85 weight_capacity,
86 weighter,
87 Default::default(),
88 Default::default(),
89 )
90 }
91}
92
93impl<
94 Key: Eq + Hash,
95 Val: Clone,
96 We: Weighter<Key, Val> + Clone,
97 B: BuildHasher + Clone,
98 L: Lifecycle<Key, Val> + Clone,
99 > Cache<Key, Val, We, B, L>
100{
101 pub fn with(
105 estimated_items_capacity: usize,
106 weight_capacity: u64,
107 weighter: We,
108 hash_builder: B,
109 lifecycle: L,
110 ) -> Self {
111 Self::with_options(
112 OptionsBuilder::new()
113 .estimated_items_capacity(estimated_items_capacity)
114 .weight_capacity(weight_capacity)
115 .build()
116 .unwrap(),
117 weighter,
118 hash_builder,
119 lifecycle,
120 )
121 }
122
123 pub fn with_options(options: Options, weighter: We, hash_builder: B, lifecycle: L) -> Self {
142 let mut num_shards = options.shards.next_power_of_two() as u64;
143 let estimated_items_capacity = options.estimated_items_capacity as u64;
144 let weight_capacity = options.weight_capacity;
145 let mut shard_items_cap =
146 estimated_items_capacity.saturating_add(num_shards - 1) / num_shards;
147 let mut shard_weight_cap =
148 options.weight_capacity.saturating_add(num_shards - 1) / num_shards;
149 while shard_items_cap < 32 && num_shards > 1 {
151 num_shards /= 2;
152 shard_items_cap = estimated_items_capacity.saturating_add(num_shards - 1) / num_shards;
153 shard_weight_cap = weight_capacity.saturating_add(num_shards - 1) / num_shards;
154 }
155 let shards = (0..num_shards)
156 .map(|_| {
157 RwLock::new(CacheShard::new(
158 options.hot_allocation,
159 options.ghost_allocation,
160 shard_items_cap as usize,
161 shard_weight_cap,
162 weighter.clone(),
163 hash_builder.clone(),
164 lifecycle.clone(),
165 ))
166 })
167 .collect::<Vec<_>>();
168 Self {
169 shards: shards.into_boxed_slice(),
170 hash_builder,
171 shards_mask: num_shards - 1,
172 }
173 }
174
175 #[cfg(fuzzing)]
176 pub fn validate(&self) {
177 for s in &*self.shards {
178 s.read().validate(false)
179 }
180 }
181
182 pub fn is_empty(&self) -> bool {
184 self.shards.iter().all(|s| s.read().len() == 0)
185 }
186
187 pub fn len(&self) -> usize {
189 self.shards.iter().map(|s| s.read().len()).sum()
190 }
191
192 pub fn weight(&self) -> u64 {
194 self.shards.iter().map(|s| s.read().weight()).sum()
195 }
196
197 pub fn capacity(&self) -> u64 {
201 self.shards.iter().map(|s| s.read().capacity()).sum()
202 }
203
204 pub fn shard_capacity(&self) -> u64 {
206 self.shards[0].read().capacity()
207 }
208
209 pub fn num_shards(&self) -> usize {
211 self.shards.len()
212 }
213
214 #[cfg(feature = "stats")]
216 pub fn misses(&self) -> u64 {
217 self.shards.iter().map(|s| s.read().misses()).sum()
218 }
219
220 #[cfg(feature = "stats")]
222 pub fn hits(&self) -> u64 {
223 self.shards.iter().map(|s| s.read().hits()).sum()
224 }
225
226 #[inline]
227 fn compute_shard_index(&self, hash: u64) -> u64 {
228 hash.rotate_right(usize::BITS / 2) & self.shards_mask
239 }
240
241 #[inline]
258 pub fn shard_index<Q: Hash + Equivalent<Key> + ?Sized>(&self, key: &Q) -> usize {
259 let hash = self.hash_builder.hash_one(key);
260 self.compute_shard_index(hash) as usize
261 }
262
263 #[inline]
264 fn shard_for<Q>(
265 &self,
266 key: &Q,
267 ) -> Option<(
268 &RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
269 u64,
270 )>
271 where
272 Q: Hash + Equivalent<Key> + ?Sized,
273 {
274 let hash = self.hash_builder.hash_one(key);
275 let shard_idx = self.compute_shard_index(hash) as usize;
276 self.shards.get(shard_idx).map(|s| (s, hash))
277 }
278
279 pub fn reserve(&self, additional: usize) {
282 let additional_per_shard =
283 additional.saturating_add(self.shards.len() - 1) / self.shards.len();
284 for s in &*self.shards {
285 s.write().reserve(additional_per_shard);
286 }
287 }
288
289 pub fn contains_key<Q>(&self, key: &Q) -> bool
291 where
292 Q: Hash + Equivalent<Key> + ?Sized,
293 {
294 self.shard_for(key)
295 .is_some_and(|(shard, hash)| shard.read().contains(hash, key))
296 }
297
298 pub fn try_contains_key<Q>(&self, key: &Q) -> Result<bool, LockContention>
302 where
303 Q: Hash + Equivalent<Key> + ?Sized,
304 {
305 let Some((shard, hash)) = self.shard_for(key) else {
306 return Ok(false);
307 };
308
309 match shard.try_read() {
310 Some(guard) => Ok(guard.contains(hash, key)),
311 None => Err(LockContention),
312 }
313 }
314
315 pub fn get<Q>(&self, key: &Q) -> Option<Val>
317 where
318 Q: Hash + Equivalent<Key> + ?Sized,
319 {
320 let (shard, hash) = self.shard_for(key)?;
321 shard.read().get(hash, key).cloned()
322 }
323
324 pub fn try_get<Q>(&self, key: &Q) -> Result<Option<Val>, LockContention>
328 where
329 Q: Hash + Equivalent<Key> + ?Sized,
330 {
331 let Some((shard, hash)) = self.shard_for(key) else {
332 return Ok(None);
333 };
334
335 match shard.try_read() {
336 Some(guard) => Ok(guard.get(hash, key).cloned()),
337 None => Err(LockContention),
338 }
339 }
340
341 pub fn peek<Q>(&self, key: &Q) -> Option<Val>
344 where
345 Q: Hash + Equivalent<Key> + ?Sized,
346 {
347 let (shard, hash) = self.shard_for(key)?;
348 shard.read().peek(hash, key).cloned()
349 }
350
351 pub fn try_peek<Q>(&self, key: &Q) -> Result<Option<Val>, LockContention>
356 where
357 Q: Hash + Equivalent<Key> + ?Sized,
358 {
359 let Some((shard, hash)) = self.shard_for(key) else {
360 return Ok(None);
361 };
362 match shard.try_read() {
363 Some(guard) => Ok(guard.peek(hash, key).cloned()),
364 None => Err(LockContention),
365 }
366 }
367
368 #[cfg(feature = "stats")]
371 pub fn item_stats<Q>(&self, key: &Q) -> Option<crate::ItemStats>
372 where
373 Q: Hash + Equivalent<Key> + ?Sized,
374 {
375 let (shard, hash) = self.shard_for(key)?;
376 shard.read().item_stats(hash, key)
377 }
378
379 #[cfg(feature = "stats")]
384 pub fn try_item_stats<Q>(&self, key: &Q) -> Result<Option<crate::ItemStats>, LockContention>
385 where
386 Q: Hash + Equivalent<Key> + ?Sized,
387 {
388 let Some((shard, hash)) = self.shard_for(key) else {
389 return Ok(None);
390 };
391 match shard.try_read() {
392 Some(guard) => Ok(guard.item_stats(hash, key)),
393 None => Err(LockContention),
394 }
395 }
396
397 pub fn remove<Q>(&self, key: &Q) -> Option<(Key, Val)>
400 where
401 Q: Hash + Equivalent<Key> + ?Sized,
402 {
403 let (shard, hash) = self.shard_for(key).unwrap();
404 shard.write().remove(hash, key)
405 }
406
407 pub fn try_remove<Q>(&self, key: &Q) -> Result<Option<(Key, Val)>, LockContention>
411 where
412 Q: Hash + Equivalent<Key> + ?Sized,
413 {
414 let Some((shard, hash)) = self.shard_for(key) else {
415 return Ok(None);
416 };
417
418 match shard.try_write() {
419 Some(mut guard) => Ok(guard.remove(hash, key)),
420 None => Err(LockContention),
421 }
422 }
423
424 pub fn remove_if<Q, F>(&self, key: &Q, f: F) -> Option<(Key, Val)>
429 where
430 Q: Hash + Equivalent<Key> + ?Sized,
431 F: FnOnce(&Val) -> bool,
432 {
433 let (shard, hash) = self.shard_for(key).unwrap();
434 shard.write().remove_if(hash, key, f)
435 }
436
437 pub fn replace(&self, key: Key, value: Val, soft: bool) -> Result<(), (Key, Val)> {
443 let mut lcs = Default::default();
444 self.replace_with_lifecycle(key, value, soft, &mut lcs)
445 }
446
447 pub fn replace_with_lifecycle(
458 &self,
459 key: Key,
460 value: Val,
461 soft: bool,
462 lcs: &mut L::RequestState,
463 ) -> Result<(), (Key, Val)> {
464 let (shard, hash) = self.shard_for(&key).unwrap();
465 shard
466 .write()
467 .insert(lcs, hash, key, value, InsertStrategy::Replace { soft })?;
468 Ok(())
469 }
470
471 pub fn retain<F>(&self, f: F)
475 where
476 F: Fn(&Key, &Val) -> bool,
477 {
478 for s in self.shards.iter() {
479 s.write().retain(&f);
480 }
481 }
482
483 pub fn insert(&self, key: Key, value: Val) {
485 let mut lcs = Default::default();
486 self.insert_with_lifecycle(key, value, &mut lcs);
487 }
488
489 pub fn try_insert(&self, key: Key, value: Val) -> Result<(), (Key, Val)> {
494 let mut lcs = Default::default();
495 self.try_insert_with_lifecycle(key, value, &mut lcs)
496 }
497
498 pub fn insert_with_lifecycle(&self, key: Key, value: Val, lcs: &mut L::RequestState) {
505 let (shard, hash) = self.shard_for(&key).unwrap();
506 let result = shard
507 .write()
508 .insert(lcs, hash, key, value, InsertStrategy::Insert);
509 debug_assert!(result.is_ok());
511 }
512
513 pub fn try_insert_with_lifecycle(
523 &self,
524 key: Key,
525 value: Val,
526 lcs: &mut L::RequestState,
527 ) -> Result<(), (Key, Val)> {
528 let (shard, hash) = self.shard_for(&key).unwrap();
529
530 match shard.try_write() {
531 Some(mut shard) => {
532 let result = shard.insert(lcs, hash, key, value, InsertStrategy::Insert);
533 debug_assert!(result.is_ok());
535 Ok(())
536 }
537 _ => Err((key, value)),
538 }
539 }
540
541 pub fn clear(&self) {
543 for s in self.shards.iter() {
544 s.write().clear();
545 }
546 }
547
548 pub fn iter(&self) -> Iter<'_, Key, Val, We, B, L>
554 where
555 Key: Clone,
556 {
557 Iter {
558 shards: &self.shards,
559 current_shard: 0,
560 last: None,
561 }
562 }
563
564 pub fn drain(&self) -> Drain<'_, Key, Val, We, B, L> {
575 Drain {
576 shards: &self.shards,
577 current_shard: 0,
578 last: None,
579 }
580 }
581
582 pub fn set_capacity(&self, new_weight_capacity: u64) {
588 let shard_weight_cap = new_weight_capacity.saturating_add(self.shards.len() as u64 - 1)
589 / self.shards.len() as u64;
590 for shard in &*self.shards {
591 let mut lcs = Default::default();
592 shard.write().set_capacity(shard_weight_cap, &mut lcs);
594 }
595 }
596
597 pub fn get_value_or_guard<Q>(
609 &self,
610 key: &Q,
611 timeout: Option<Duration>,
612 ) -> GuardResult<'_, Key, Val, We, B, L>
613 where
614 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
615 {
616 let (shard, hash) = self.shard_for(key).unwrap();
617 if let Some(v) = shard.read().get(hash, key) {
618 return GuardResult::Value(v.clone());
619 }
620 PlaceholderGuard::join(shard, hash, key, timeout)
621 }
622
623 pub fn get_or_insert_with<Q, E>(
627 &self,
628 key: &Q,
629 with: impl FnOnce() -> Result<Val, E>,
630 ) -> Result<Val, E>
631 where
632 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
633 {
634 match self.get_value_or_guard(key, None) {
635 GuardResult::Value(v) => Ok(v),
636 GuardResult::Guard(g) => {
637 let v = with()?;
638 let _ = g.insert(v.clone());
639 Ok(v)
640 }
641 GuardResult::Timeout => unsafe { unreachable_unchecked() },
642 }
643 }
644
645 pub async fn get_value_or_guard_async<'a, Q>(
653 &'a self,
654 key: &Q,
655 ) -> Result<Val, PlaceholderGuard<'a, Key, Val, We, B, L>>
656 where
657 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
658 {
659 let (shard, hash) = self.shard_for(key).unwrap();
660 loop {
661 if let Some(v) = shard.read().get(hash, key) {
662 return Ok(v.clone());
663 }
664 match JoinFuture::new(shard, hash, key).await {
665 JoinResult::Filled(Some(shared)) => {
666 return Ok(unsafe { shared.value().unwrap_unchecked().clone() });
668 }
669 JoinResult::Filled(None) => continue,
670 JoinResult::Guard(g) => return Err(g),
671 JoinResult::Timeout => unsafe { unreachable_unchecked() },
672 }
673 }
674 }
675
676 pub async fn get_or_insert_async<Q, E>(
678 &self,
679 key: &Q,
680 with: impl Future<Output = Result<Val, E>>,
681 ) -> Result<Val, E>
682 where
683 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
684 {
685 match self.get_value_or_guard_async(key).await {
686 Ok(v) => Ok(v),
687 Err(g) => {
688 let v = with.await?;
689 let _ = g.insert(v.clone());
690 Ok(v)
691 }
692 }
693 }
694
695 pub fn entry<Q, T>(
745 &self,
746 key: &Q,
747 timeout: Option<Duration>,
748 on_occupied: impl FnOnce(&Key, &mut Val) -> EntryAction<T>,
749 ) -> EntryResult<'_, Key, Val, We, B, L, T>
750 where
751 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
752 {
753 let (shard, hash) = self.shard_for(key).unwrap();
754 let mut on_occupied = Some(on_occupied);
759 let mut callback = |k: &Key, v: &mut Val| on_occupied.take().unwrap()(k, v);
760 let mut deadline = timeout.map(Ok);
761
762 loop {
763 let mut shard_guard = shard.write();
764 match shard_guard.entry_or_placeholder(hash, key, &mut callback) {
765 EntryOrPlaceholder::Kept(t) => return EntryResult::Retained(t),
766 EntryOrPlaceholder::Removed(k, v) => return EntryResult::Removed(k, v),
767 EntryOrPlaceholder::Replaced(shared, old_val) => {
768 drop(shard_guard);
769 return EntryResult::Replaced(
770 PlaceholderGuard::start_loading(shard, shared),
771 old_val,
772 );
773 }
774 EntryOrPlaceholder::NewPlaceholder(shared) => {
775 drop(shard_guard);
776 return EntryResult::Vacant(PlaceholderGuard::start_loading(shard, shared));
777 }
778 EntryOrPlaceholder::ExistingPlaceholder(shared) => {
779 match PlaceholderGuard::wait_for_placeholder(
780 shard,
781 shard_guard,
782 shared,
783 deadline.as_mut(),
784 ) {
785 JoinResult::Filled(_) => continue,
786 JoinResult::Guard(g) => return EntryResult::Vacant(g),
787 JoinResult::Timeout => return EntryResult::Timeout,
788 }
789 }
790 }
791 }
792 }
793
794 pub async fn entry_async<'a, Q, T>(
801 &'a self,
802 key: &Q,
803 on_occupied: impl FnOnce(&Key, &mut Val) -> EntryAction<T>,
804 ) -> EntryResult<'a, Key, Val, We, B, L, T>
805 where
806 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
807 {
808 let (shard, hash) = self.shard_for(key).unwrap();
809 let mut on_occupied = Some(on_occupied);
811 let mut callback = |k: &Key, v: &mut Val| on_occupied.take().unwrap()(k, v);
812
813 loop {
814 let result = {
817 let mut shard_guard = shard.write();
818 match shard_guard.entry_or_placeholder(hash, key, &mut callback) {
819 EntryOrPlaceholder::Kept(t) => Ok(EntryResult::Retained(t)),
820 EntryOrPlaceholder::Removed(k, v) => Ok(EntryResult::Removed(k, v)),
821 EntryOrPlaceholder::Replaced(shared, old_val) => {
822 drop(shard_guard);
823 Ok(EntryResult::Replaced(
824 PlaceholderGuard::start_loading(shard, shared),
825 old_val,
826 ))
827 }
828 EntryOrPlaceholder::NewPlaceholder(shared) => {
829 drop(shard_guard);
830 Ok(EntryResult::Vacant(PlaceholderGuard::start_loading(
831 shard, shared,
832 )))
833 }
834 EntryOrPlaceholder::ExistingPlaceholder(_) => Err(()),
835 }
836 };
837 match result {
838 Ok(entry_result) => return entry_result,
839 Err(()) => match JoinFuture::new(shard, hash, key).await {
840 JoinResult::Filled(_) => continue,
841 JoinResult::Guard(g) => return EntryResult::Vacant(g),
842 JoinResult::Timeout => unsafe { unreachable_unchecked() },
843 },
844 }
845 }
846 }
847
848 pub fn memory_used(&self) -> MemoryUsed {
853 let mut total = MemoryUsed { entries: 0, map: 0 };
854 self.shards.iter().for_each(|shard| {
855 let shard_memory = shard.read().memory_used();
856 total.entries += shard_memory.entries;
857 total.map += shard_memory.map;
858 });
859 total
860 }
861}
862
863impl<Key, Val, We, B, L> std::fmt::Debug for Cache<Key, Val, We, B, L> {
864 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
865 f.debug_struct("Cache").finish_non_exhaustive()
866 }
867}
868
869pub struct Iter<'a, Key, Val, We, B, L> {
873 shards: &'a [RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>],
874 current_shard: usize,
875 last: Option<Token>,
876}
877
878impl<Key, Val, We, B, L> Iterator for Iter<'_, Key, Val, We, B, L>
879where
880 Key: Clone,
881 Val: Clone,
882{
883 type Item = (Key, Val);
884
885 fn next(&mut self) -> Option<Self::Item> {
886 while self.current_shard < self.shards.len() {
887 let shard = &self.shards[self.current_shard];
888 let lock = shard.read();
889 if let Some((new_last, key, val)) = lock.iter_from(self.last).next() {
890 self.last = Some(new_last);
891 return Some((key.clone(), val.clone()));
892 }
893 self.last = None;
894 self.current_shard += 1;
895 }
896 None
897 }
898}
899
900impl<Key, Val, We, B, L> std::fmt::Debug for Iter<'_, Key, Val, We, B, L> {
901 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
902 f.debug_struct("Iter").finish_non_exhaustive()
903 }
904}
905
906pub struct Drain<'a, Key, Val, We, B, L> {
910 shards: &'a [RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>],
911 current_shard: usize,
912 last: Option<Token>,
913}
914
915impl<Key, Val, We, B, L> Iterator for Drain<'_, Key, Val, We, B, L>
916where
917 Key: Hash + Eq,
918 We: Weighter<Key, Val>,
919 B: BuildHasher,
920 L: Lifecycle<Key, Val>,
921{
922 type Item = (Key, Val);
923
924 fn next(&mut self) -> Option<Self::Item> {
925 while self.current_shard < self.shards.len() {
926 let shard = &self.shards[self.current_shard];
927 let mut lock = shard.write();
928 if let Some((new_last, key, value)) = lock.remove_next(self.last) {
929 self.last = Some(new_last);
930 return Some((key, value));
931 }
932 self.last = None;
933 self.current_shard += 1;
934 }
935 None
936 }
937}
938
939impl<Key, Val, We, B, L> std::fmt::Debug for Drain<'_, Key, Val, We, B, L> {
940 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
941 f.debug_struct("Drain").finish_non_exhaustive()
942 }
943}
944
945pub struct DefaultLifecycle<Key, Val>(std::marker::PhantomData<(Key, Val)>);
949
950impl<Key, Val> std::fmt::Debug for DefaultLifecycle<Key, Val> {
951 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
952 f.debug_tuple("DefaultLifecycle").finish()
953 }
954}
955
956impl<Key, Val> Default for DefaultLifecycle<Key, Val> {
957 #[inline]
958 fn default() -> Self {
959 Self(Default::default())
960 }
961}
962impl<Key, Val> Clone for DefaultLifecycle<Key, Val> {
963 #[inline]
964 fn clone(&self) -> Self {
965 Self(Default::default())
966 }
967}
968
969impl<Key, Val> Lifecycle<Key, Val> for DefaultLifecycle<Key, Val> {
970 type RequestState = [Option<(Key, Val)>; 2];
976
977 #[inline]
978 fn on_evict(&self, state: &mut Self::RequestState, key: Key, val: Val) {
979 if std::mem::needs_drop::<(Key, Val)>() {
980 if state[0].is_none() {
981 state[0] = Some((key, val));
982 } else if state[1].is_none() {
983 state[1] = Some((key, val));
984 }
985 }
986 }
987}
988
989#[cfg(test)]
990mod tests {
991 use super::*;
992 use crate::shard::SharedPlaceholder as _;
993 use std::{
994 sync::{Arc, Barrier},
995 thread,
996 };
997
998 #[test]
999 #[cfg_attr(miri, ignore)]
1000 fn test_multiple_threads() {
1001 const N_THREAD_PAIRS: usize = 8;
1002 const N_ROUNDS: usize = 1_000;
1003 const ITEMS_PER_THREAD: usize = 1_000;
1004 let mut threads = Vec::new();
1005 let barrier = Arc::new(Barrier::new(N_THREAD_PAIRS * 2));
1006 let cache = Arc::new(Cache::new(N_THREAD_PAIRS * ITEMS_PER_THREAD / 10));
1007 for t in 0..N_THREAD_PAIRS {
1008 let barrier = barrier.clone();
1009 let cache = cache.clone();
1010 let handle = thread::spawn(move || {
1011 let start = ITEMS_PER_THREAD * t;
1012 barrier.wait();
1013 for _round in 0..N_ROUNDS {
1014 for i in start..start + ITEMS_PER_THREAD {
1015 cache.insert(i, i);
1016 }
1017 }
1018 });
1019 threads.push(handle);
1020 }
1021 for t in 0..N_THREAD_PAIRS {
1022 let barrier = barrier.clone();
1023 let cache = cache.clone();
1024 let handle = thread::spawn(move || {
1025 let start = ITEMS_PER_THREAD * t;
1026 barrier.wait();
1027 for _round in 0..N_ROUNDS {
1028 for i in start..start + ITEMS_PER_THREAD {
1029 if let Some(cached) = cache.get(&i) {
1030 assert_eq!(cached, i);
1031 }
1032 }
1033 }
1034 });
1035 threads.push(handle);
1036 }
1037 for t in threads {
1038 t.join().unwrap();
1039 }
1040 }
1041
1042 #[test]
1043 fn test_iter() {
1044 let capacity = if cfg!(miri) { 100 } else { 100000 };
1045 let options = OptionsBuilder::new()
1046 .estimated_items_capacity(capacity)
1047 .weight_capacity(capacity as u64)
1048 .shards(2)
1049 .build()
1050 .unwrap();
1051 let cache = Cache::with_options(
1052 options,
1053 UnitWeighter,
1054 DefaultHashBuilder::default(),
1055 DefaultLifecycle::default(),
1056 );
1057 let items = capacity / 2;
1058 for i in 0..items {
1059 cache.insert(i, i);
1060 }
1061 assert_eq!(cache.len(), items);
1062 let mut iter_collected = cache.iter().collect::<Vec<_>>();
1063 assert_eq!(iter_collected.len(), items);
1064 iter_collected.sort();
1065 for (i, v) in iter_collected.into_iter().enumerate() {
1066 assert_eq!((i, i), v);
1067 }
1068 }
1069
1070 #[test]
1071 fn test_drain() {
1072 let capacity = if cfg!(miri) { 100 } else { 100000 };
1073 let options = OptionsBuilder::new()
1074 .estimated_items_capacity(capacity)
1075 .weight_capacity(capacity as u64)
1076 .shards(2)
1077 .build()
1078 .unwrap();
1079 let cache = Cache::with_options(
1080 options,
1081 UnitWeighter,
1082 DefaultHashBuilder::default(),
1083 DefaultLifecycle::default(),
1084 );
1085 let items = capacity / 2;
1086 for i in 0..items {
1087 cache.insert(i, i);
1088 }
1089 assert_eq!(cache.len(), items);
1090 let mut drain_collected = cache.drain().collect::<Vec<_>>();
1091 assert_eq!(cache.len(), 0);
1092 assert_eq!(drain_collected.len(), items);
1093 drain_collected.sort();
1094 for (i, v) in drain_collected.into_iter().enumerate() {
1095 assert_eq!((i, i), v);
1096 }
1097 }
1098
1099 #[test]
1100 fn test_set_capacity() {
1101 let cache = Cache::new(100);
1102 for i in 0..80 {
1103 cache.insert(i, i);
1104 }
1105 let initial_len = cache.len();
1106 assert!(initial_len <= 80);
1107
1108 cache.set_capacity(50);
1110 assert!(cache.len() <= 50);
1111 assert!(cache.weight() <= 50);
1112
1113 cache.set_capacity(200);
1115 assert_eq!(cache.capacity(), 200);
1116
1117 for i in 100..180 {
1119 cache.insert(i, i);
1120 }
1121 assert!(cache.len() <= 180);
1122 assert!(cache.weight() <= 200);
1123 }
1124
1125 #[test]
1126 fn test_remove_if() {
1127 let cache = Cache::new(100);
1128
1129 cache.insert(1, 10);
1131 cache.insert(2, 20);
1132 cache.insert(3, 30);
1133
1134 let removed = cache.remove_if(&2, |v| *v == 20);
1136 assert_eq!(removed, Some((2, 20)));
1137 assert_eq!(cache.get(&2), None);
1138
1139 let not_removed = cache.remove_if(&3, |v| *v == 999);
1141 assert_eq!(not_removed, None);
1142 assert_eq!(cache.get(&3), Some(30));
1143
1144 let not_found = cache.remove_if(&999, |_| true);
1146 assert_eq!(not_found, None);
1147 }
1148
1149 #[test]
1151 fn test_entry_actions() {
1152 let cache = Cache::new(100);
1153 cache.insert(1, 10);
1154 cache.insert(2, 20);
1155
1156 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1158 assert!(matches!(result, EntryResult::Retained(10)));
1159 assert_eq!(cache.get(&1), Some(10));
1160
1161 let result = cache.entry(&1, None, |_k, v| {
1163 *v += 5;
1164 EntryAction::Retain(())
1165 });
1166 assert!(matches!(result, EntryResult::Retained(())));
1167 assert_eq!(cache.get(&1), Some(15));
1168
1169 let result = cache.entry(&1, None, |_k, _v| EntryAction::<()>::Remove);
1171 assert!(matches!(result, EntryResult::Removed(1, 15)));
1172 assert_eq!(cache.get(&1), None);
1173
1174 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1176 match result {
1177 EntryResult::Vacant(g) => {
1178 let _ = g.insert(99);
1179 assert_eq!(cache.get(&1), Some(99));
1180 }
1181 _ => panic!("expected Vacant for removed key"),
1182 }
1183
1184 let mut old_val = 0;
1186 let result = cache.entry(&2, None, |_k, v| {
1187 old_val = *v;
1188 EntryAction::<()>::ReplaceWithGuard
1189 });
1190 assert_eq!(old_val, 20);
1191 match result {
1192 EntryResult::Replaced(g, old) => {
1193 assert_eq!(old, 20);
1194 let _ = g.insert(old_val + 100);
1195 assert_eq!(cache.get(&2), Some(120));
1196 }
1197 _ => panic!("expected Replaced"),
1198 }
1199
1200 let result = cache.entry(&2, None, |_k, _v| EntryAction::<()>::ReplaceWithGuard);
1202 match result {
1203 EntryResult::Replaced(g, _old) => {
1204 drop(g);
1205 assert_eq!(cache.get(&2), None);
1206 }
1207 _ => panic!("expected Replaced"),
1208 }
1209
1210 let result = cache.entry(&3, None, |_k, v| EntryAction::Retain(*v));
1212 match result {
1213 EntryResult::Vacant(g) => {
1214 let _ = g.insert(30);
1215 assert_eq!(cache.get(&3), Some(30));
1216 }
1217 _ => panic!("expected Vacant"),
1218 }
1219 }
1220
1221 #[test]
1223 fn test_entry_weight_tracking() {
1224 #[derive(Clone)]
1225 struct StringWeighter;
1226 impl crate::Weighter<u64, String> for StringWeighter {
1227 fn weight(&self, _key: &u64, val: &String) -> u64 {
1228 val.len() as u64
1229 }
1230 }
1231
1232 let cache = Cache::with_weighter(100, 100_000, StringWeighter);
1233 cache.insert(1, "hello".to_string());
1234 cache.insert(2, "world".to_string());
1235 assert_eq!(cache.weight(), 10);
1236
1237 let result = cache.entry(&1, None, |_k, _v| EntryAction::Retain(()));
1239 assert!(matches!(result, EntryResult::Retained(())));
1240 assert_eq!(cache.weight(), 10);
1241
1242 let result = cache.entry(&1, None, |_k, v| {
1244 v.push_str(" world");
1245 EntryAction::Retain(())
1246 });
1247 assert!(matches!(result, EntryResult::Retained(())));
1248 assert_eq!(cache.weight(), 16); assert_eq!(cache.get(&1).unwrap(), "hello world");
1250
1251 let result = cache.entry(&1, None, |_k, v| {
1253 v.clear();
1254 EntryAction::Retain(())
1255 });
1256 assert!(matches!(result, EntryResult::Retained(())));
1257 assert_eq!(cache.weight(), 5); assert_eq!(cache.get(&1).unwrap(), "");
1259
1260 let result = cache.entry(&2, None, |_k, _v| EntryAction::<()>::Remove);
1262 assert!(matches!(result, EntryResult::Removed(2, _)));
1263 assert_eq!(cache.weight(), 0);
1264 assert_eq!(cache.len(), 1);
1265
1266 cache.insert(3, "hello".to_string());
1268 assert_eq!(cache.weight(), 5);
1269 let result = cache.entry(&3, None, |_k, _v| EntryAction::<()>::ReplaceWithGuard);
1270 match result {
1271 EntryResult::Replaced(g, _old) => {
1272 assert_eq!(cache.weight(), 0);
1273 let _ = g.insert("hello world!!".to_string());
1274 assert_eq!(cache.weight(), 13);
1275 }
1276 _ => panic!("expected Replaced"),
1277 }
1278 }
1279
1280 #[test]
1282 fn test_entry_eviction() {
1283 let cache = Cache::new(2);
1285 cache.insert(1, 10);
1286 cache.insert(2, 20);
1287 assert_eq!(cache.len(), 2);
1288
1289 let result = cache.entry(&3, None, |_k, v| EntryAction::Retain(*v));
1290 match result {
1291 EntryResult::Vacant(g) => {
1292 let _ = g.insert(30);
1293 assert!(cache.len() <= 2);
1294 assert_eq!(cache.get(&3), Some(30));
1295 }
1296 _ => panic!("expected Vacant"),
1297 }
1298
1299 let cache = Cache::new(0);
1301 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1302 match result {
1303 EntryResult::Vacant(g) => {
1304 let _ = g.insert(10);
1305 assert_eq!(cache.get(&1), None);
1306 }
1307 _ => panic!("expected Vacant"),
1308 }
1309 }
1310
1311 #[test]
1313 #[cfg_attr(miri, ignore)]
1314 fn test_entry_concurrent_placeholder_wait() {
1315 let cache = Arc::new(Cache::new(100));
1316 let barrier = Arc::new(Barrier::new(2));
1317
1318 let cache2 = cache.clone();
1320 let barrier2 = barrier.clone();
1321 let handle = thread::spawn(move || match cache2.get_value_or_guard(&1, None) {
1322 GuardResult::Guard(g) => {
1323 barrier2.wait();
1324 std::thread::sleep(Duration::from_millis(50));
1325 let _ = g.insert(42);
1326 }
1327 _ => panic!("expected guard"),
1328 });
1329
1330 barrier.wait();
1331 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1332 assert!(matches!(result, EntryResult::Retained(42)));
1333 handle.join().unwrap();
1334 }
1335
1336 #[test]
1338 #[cfg_attr(miri, ignore)]
1339 fn test_entry_concurrent_placeholder_guard_abandoned() {
1340 let cache = Arc::new(Cache::new(100));
1341 let barrier = Arc::new(Barrier::new(2));
1342
1343 let cache2 = cache.clone();
1344 let barrier2 = barrier.clone();
1345 let handle = thread::spawn(move || match cache2.get_value_or_guard(&1, None) {
1346 GuardResult::Guard(g) => {
1347 barrier2.wait();
1348 std::thread::sleep(Duration::from_millis(50));
1349 drop(g);
1350 }
1351 _ => panic!("expected guard"),
1352 });
1353
1354 barrier.wait();
1355 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1356 match result {
1357 EntryResult::Vacant(g) => {
1358 let _ = g.insert(99);
1359 assert_eq!(cache.get(&1), Some(99));
1360 }
1361 _ => panic!("expected Vacant after abandoned placeholder"),
1362 }
1363 handle.join().unwrap();
1364 }
1365
1366 #[test]
1368 #[cfg_attr(miri, ignore)]
1369 fn test_entry_timeout() {
1370 let cache = Cache::new(100);
1371
1372 let guard = match cache.get_value_or_guard(&1, None) {
1374 GuardResult::Guard(g) => g,
1375 _ => panic!("expected guard"),
1376 };
1377 let result = cache.entry(&1, Some(Duration::ZERO), |_k, v| EntryAction::Retain(*v));
1378 assert!(matches!(result, EntryResult::Timeout));
1379 let _ = guard.insert(1);
1380
1381 let cache = Arc::new(Cache::new(100));
1383 let barrier = Arc::new(Barrier::new(2));
1384 let cache2 = cache.clone();
1385 let barrier2 = barrier.clone();
1386 let holder = thread::spawn(move || {
1387 let guard = match cache2.get_value_or_guard(&1, None) {
1388 GuardResult::Guard(g) => g,
1389 _ => panic!("expected guard"),
1390 };
1391 barrier2.wait();
1392 std::thread::sleep(Duration::from_millis(200));
1393 let _ = guard.insert(1);
1394 });
1395
1396 barrier.wait();
1397 let result = cache.entry(&1, Some(Duration::from_millis(50)), |_k, v| {
1398 EntryAction::Retain(*v)
1399 });
1400 assert!(matches!(result, EntryResult::Timeout));
1401 holder.join().unwrap();
1402 }
1403
1404 #[test]
1406 #[cfg_attr(miri, ignore)]
1407 fn test_entry_concurrent_multiple_waiters() {
1408 let cache = Arc::new(Cache::new(100));
1409 let barrier = Arc::new(Barrier::new(4)); let cache1 = cache.clone();
1412 let barrier1 = barrier.clone();
1413 let loader = thread::spawn(move || match cache1.get_value_or_guard(&1, None) {
1414 GuardResult::Guard(g) => {
1415 barrier1.wait();
1416 std::thread::sleep(Duration::from_millis(50));
1417 let _ = g.insert(42);
1418 }
1419 _ => panic!("expected guard"),
1420 });
1421
1422 let mut waiters = Vec::new();
1423 for _ in 0..3 {
1424 let cache_c = cache.clone();
1425 let barrier_c = barrier.clone();
1426 waiters.push(thread::spawn(move || {
1427 barrier_c.wait();
1428 let result = cache_c.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1429 match result {
1430 EntryResult::Retained(v) => v,
1431 _ => panic!("expected Value"),
1432 }
1433 }));
1434 }
1435
1436 loader.join().unwrap();
1437 for w in waiters {
1438 assert_eq!(w.join().unwrap(), 42);
1439 }
1440 }
1441
1442 #[test]
1444 #[cfg_attr(miri, ignore)]
1445 fn test_entry_concurrent_action_after_wait() {
1446 let cache = Arc::new(Cache::new(100));
1448 let barrier = Arc::new(Barrier::new(2));
1449
1450 let cache1 = cache.clone();
1451 let barrier1 = barrier.clone();
1452 let loader = thread::spawn(move || match cache1.get_value_or_guard(&1, None) {
1453 GuardResult::Guard(g) => {
1454 barrier1.wait();
1455 std::thread::sleep(Duration::from_millis(50));
1456 let _ = g.insert(42);
1457 }
1458 _ => panic!("expected guard"),
1459 });
1460
1461 barrier.wait();
1462 let result = cache.entry(&1, None, |_k, _v| EntryAction::<()>::ReplaceWithGuard);
1463 match result {
1464 EntryResult::Replaced(g, old) => {
1465 assert_eq!(old, 42);
1466 let _ = g.insert(100);
1467 assert_eq!(cache.get(&1), Some(100));
1468 }
1469 _ => panic!("expected Replaced"),
1470 }
1471 loader.join().unwrap();
1472
1473 let cache = Arc::new(Cache::new(100));
1475 let barrier = Arc::new(Barrier::new(2));
1476
1477 let cache1 = cache.clone();
1478 let barrier1 = barrier.clone();
1479 let loader = thread::spawn(move || match cache1.get_value_or_guard(&1, None) {
1480 GuardResult::Guard(g) => {
1481 barrier1.wait();
1482 std::thread::sleep(Duration::from_millis(50));
1483 let _ = g.insert(42);
1484 }
1485 _ => panic!("expected guard"),
1486 });
1487
1488 barrier.wait();
1489 let result = cache.entry(&1, None, |_k, _v| EntryAction::<()>::Remove);
1490 assert!(matches!(result, EntryResult::Removed(1, 42)));
1491 assert_eq!(cache.get(&1), None);
1492 loader.join().unwrap();
1493 }
1494
1495 #[test]
1497 #[cfg_attr(miri, ignore)]
1498 fn test_entry_concurrent_stress() {
1499 const N_THREADS: usize = 8;
1500 const N_KEYS: usize = 50;
1501 const N_OPS: usize = 500;
1502
1503 let cache = Arc::new(Cache::new(1000));
1504 let barrier = Arc::new(Barrier::new(N_THREADS));
1505
1506 let mut handles = Vec::new();
1507 for t in 0..N_THREADS {
1508 let cache = cache.clone();
1509 let barrier = barrier.clone();
1510 handles.push(thread::spawn(move || {
1511 barrier.wait();
1512 for i in 0..N_OPS {
1513 let key = (t * N_OPS + i) % N_KEYS;
1514 let result = cache.entry(&key, Some(Duration::from_millis(10)), |_k, v| {
1515 EntryAction::Retain(*v)
1516 });
1517 match result {
1518 EntryResult::Retained(_) => {}
1519 EntryResult::Vacant(g) => {
1520 let _ = g.insert(key * 10);
1521 }
1522 EntryResult::Replaced(g, _) => {
1523 let _ = g.insert(key * 10);
1524 }
1525 EntryResult::Timeout => {}
1526 EntryResult::Removed(_, _) => {}
1527 }
1528 }
1529 }));
1530 }
1531
1532 for h in handles {
1533 h.join().unwrap();
1534 }
1535
1536 assert!(cache.len() <= N_KEYS);
1537 for key in 0..N_KEYS {
1538 if let Some(v) = cache.get(&key) {
1539 assert_eq!(v, key * 10);
1540 }
1541 }
1542 }
1543
1544 #[tokio::test]
1548 async fn test_entry_async_actions() {
1549 let cache = Cache::new(100);
1550 cache.insert(1, 10);
1551 cache.insert(2, 20);
1552
1553 let result = cache.entry_async(&1, |_k, v| EntryAction::Retain(*v)).await;
1555 assert!(matches!(result, EntryResult::Retained(10)));
1556 assert_eq!(cache.get(&1), Some(10));
1557
1558 let result = cache
1560 .entry_async(&1, |_k, _v| EntryAction::<()>::Remove)
1561 .await;
1562 assert!(matches!(result, EntryResult::Removed(1, 10)));
1563 assert_eq!(cache.get(&1), None);
1564
1565 let result = cache
1567 .entry_async(&2, |_k, _v| EntryAction::<()>::ReplaceWithGuard)
1568 .await;
1569 match result {
1570 EntryResult::Replaced(g, old) => {
1571 assert_eq!(old, 20);
1572 let _ = g.insert(42);
1573 assert_eq!(cache.get(&2), Some(42));
1574 }
1575 _ => panic!("expected Replaced"),
1576 }
1577
1578 let result = cache.entry_async(&3, |_k, v| EntryAction::Retain(*v)).await;
1580 match result {
1581 EntryResult::Vacant(g) => {
1582 let _ = g.insert(99);
1583 assert_eq!(cache.get(&3), Some(99));
1584 }
1585 _ => panic!("expected Vacant"),
1586 }
1587 }
1588
1589 #[tokio::test(flavor = "multi_thread")]
1591 async fn test_entry_async_concurrent_wait() {
1592 let cache = Arc::new(Cache::new(100));
1593 let barrier = Arc::new(Barrier::new(2));
1594
1595 let cache1 = cache.clone();
1596 let barrier1 = barrier.clone();
1597 let holder = thread::spawn(move || {
1598 let guard = match cache1.get_value_or_guard(&1, None) {
1599 GuardResult::Guard(g) => g,
1600 _ => panic!("expected guard"),
1601 };
1602 barrier1.wait();
1603 std::thread::sleep(Duration::from_millis(50));
1604 let _ = guard.insert(42);
1605 });
1606
1607 barrier.wait();
1608 let result = cache.entry_async(&1, |_k, v| EntryAction::Retain(*v)).await;
1609 assert!(matches!(result, EntryResult::Retained(42)));
1610 holder.join().unwrap();
1611 }
1612
1613 #[tokio::test(flavor = "multi_thread")]
1615 async fn test_entry_async_concurrent_guard_abandoned() {
1616 let cache = Arc::new(Cache::new(100));
1617 let barrier = Arc::new(Barrier::new(2));
1618
1619 let cache1 = cache.clone();
1620 let barrier1 = barrier.clone();
1621 let holder = thread::spawn(move || {
1622 let guard = match cache1.get_value_or_guard(&1, None) {
1623 GuardResult::Guard(g) => g,
1624 _ => panic!("expected guard"),
1625 };
1626 barrier1.wait();
1627 std::thread::sleep(Duration::from_millis(50));
1628 drop(guard);
1629 });
1630
1631 barrier.wait();
1632 let result = cache.entry_async(&1, |_k, v| EntryAction::Retain(*v)).await;
1633 match result {
1634 EntryResult::Vacant(g) => {
1635 let _ = g.insert(99);
1636 }
1637 _ => panic!("expected Vacant after abandoned placeholder"),
1638 }
1639 assert_eq!(cache.get(&1), Some(99));
1640 holder.join().unwrap();
1641 }
1642
1643 #[tokio::test(flavor = "multi_thread")]
1645 #[cfg_attr(miri, ignore)]
1646 async fn test_entry_async_concurrent_stress() {
1647 const N_TASKS: usize = 16;
1648 const N_KEYS: usize = 50;
1649 const N_OPS: usize = 200;
1650
1651 let cache = Arc::new(Cache::new(1000));
1652 let barrier = Arc::new(tokio::sync::Barrier::new(N_TASKS));
1653
1654 let mut handles = Vec::new();
1655 for t in 0..N_TASKS {
1656 let cache = cache.clone();
1657 let barrier = barrier.clone();
1658 handles.push(tokio::spawn(async move {
1659 barrier.wait().await;
1660 for i in 0..N_OPS {
1661 let key = (t * N_OPS + i) % N_KEYS;
1662 let _ = cache
1665 .get_or_insert_async(&key, async { Ok::<_, ()>(key * 10) })
1666 .await;
1667 }
1668 }));
1669 }
1670
1671 for h in handles {
1672 h.await.unwrap();
1673 }
1674
1675 assert!(cache.len() <= N_KEYS);
1676 for key in 0..N_KEYS {
1677 if let Some(v) = cache.get(&key) {
1678 assert_eq!(v, key * 10);
1679 }
1680 }
1681 }
1682
1683 #[test]
1685 fn test_try_contains_key() {
1686 let cache = Cache::new(100);
1687 cache.insert(1, 10);
1688
1689 assert!(cache.try_contains_key(&1).is_ok_and(|v| v));
1690 assert!(cache.try_contains_key(&2).is_ok_and(|v| !v));
1691 }
1692
1693 #[test]
1694 fn test_try_contains_key_contended() {
1695 let cache = Cache::new(100);
1696 cache.insert(1, 10);
1697 let _guards: Vec<_> = cache.shards.iter().map(|s| s.write()).collect();
1699 assert!(cache.try_contains_key(&1).is_err());
1700 }
1701
1702 #[test]
1703 fn test_try_get() {
1704 let cache = Cache::new(100);
1705 cache.insert(1, 10);
1706
1707 assert!(cache.try_get(&1).is_ok_and(|v| matches!(v, Some(10))));
1708 assert!(cache.try_get(&2).is_ok_and(|v| v.is_none()));
1709 }
1710
1711 #[test]
1712 fn test_try_get_contended() {
1713 let cache = Cache::new(100);
1714 cache.insert(1, 10);
1715 let _guards: Vec<_> = cache.shards.iter().map(|s| s.write()).collect();
1716 assert!(cache.try_get(&1).is_err());
1717 }
1718
1719 #[test]
1720 fn test_try_peek() {
1721 let cache = Cache::new(100);
1722 cache.insert(1, 10);
1723
1724 assert!(cache.try_peek(&1).is_ok_and(|v| matches!(v, Some(10))));
1725 assert!(cache.try_peek(&2).is_ok_and(|v| v.is_none()));
1726 }
1727
1728 #[test]
1729 fn test_try_peek_contended() {
1730 let cache = Cache::new(100);
1731 cache.insert(1, 10);
1732 let _guards: Vec<_> = cache.shards.iter().map(|s| s.write()).collect();
1733 assert!(cache.try_peek(&1).is_err());
1734 }
1735
1736 #[cfg(feature = "stats")]
1737 #[test]
1738 fn test_item_stats() {
1739 let cache = Cache::new(100);
1740 assert!(cache.item_stats(&1).is_none());
1742
1743 cache.insert(1, 10);
1744 assert_eq!(cache.item_stats(&1).map(|s| s.access_count), Some(0));
1746
1747 cache.get(&1);
1749 cache.get(&1);
1750 cache.get(&1);
1751 assert_eq!(cache.item_stats(&1).map(|s| s.access_count), Some(3));
1752
1753 cache.peek(&1);
1755 let _ = cache.item_stats(&1);
1756 assert_eq!(cache.item_stats(&1).map(|s| s.access_count), Some(3));
1757 }
1758
1759 #[test]
1760 fn test_try_remove() {
1761 let cache = Cache::new(100);
1762 cache.insert(1, 10);
1763
1764 assert!(cache
1765 .try_remove(&1)
1766 .is_ok_and(|v| matches!(v, Some((1, 10)))));
1767 assert!(cache.try_remove(&1).is_ok_and(|v| v.is_none()));
1768 assert!(cache.try_remove(&99).is_ok_and(|v| v.is_none()));
1769 }
1770
1771 #[test]
1772 fn test_try_remove_contended() {
1773 let cache = Cache::new(100);
1774 cache.insert(1, 10);
1775 let guards: Vec<_> = cache.shards.iter().map(|s| s.read()).collect();
1777 assert!(cache.try_remove(&1).is_err());
1778 drop(guards);
1779 assert_eq!(cache.get(&1), Some(10));
1781 }
1782
1783 #[test]
1784 fn test_try_insert() {
1785 let cache = Cache::new(100);
1786
1787 assert_eq!(cache.try_insert(1, 10), Ok(()));
1788 assert_eq!(cache.get(&1), Some(10));
1789
1790 assert_eq!(cache.try_insert(1, 20), Ok(()));
1792 assert_eq!(cache.get(&1), Some(20));
1793 }
1794
1795 #[test]
1796 fn test_try_insert_contended() {
1797 let cache = Cache::new(100);
1798 let guards: Vec<_> = cache.shards.iter().map(|s| s.read()).collect();
1799 assert_eq!(cache.try_insert(1, 10), Err((1, 10)));
1800 drop(guards);
1801 assert_eq!(cache.get(&1), None);
1802 }
1803
1804 #[test]
1805 fn test_try_insert_with_lifecycle() {
1806 let cache = Cache::new(100);
1807 let mut lcs = Default::default();
1808
1809 assert_eq!(cache.try_insert_with_lifecycle(1, 10, &mut lcs), Ok(()));
1811 assert_eq!(cache.get(&1), Some(10));
1812
1813 assert_eq!(cache.try_insert_with_lifecycle(2, 20, &mut lcs), Ok(()));
1815 assert_eq!(cache.get(&2), Some(20));
1816
1817 let guards: Vec<_> = cache.shards.iter().map(|s| s.read()).collect();
1819 assert_eq!(
1820 cache.try_insert_with_lifecycle(3, 30, &mut lcs),
1821 Err((3, 30))
1822 );
1823 drop(guards);
1824 assert_eq!(cache.get(&3), None);
1825 }
1826
1827 #[test]
1828 fn test_guard_leak() {
1829 let cache: Cache<i32, i32> = Cache::new(8);
1830 let guard1 = match cache.get_value_or_guard(&1, None) {
1831 GuardResult::Guard(g) => g,
1832 _ => panic!("expected guard"),
1833 };
1834 let idx1 = guard1.shared().idx();
1835 drop(guard1);
1836 let guard2 = match cache.get_value_or_guard(&1, None) {
1837 GuardResult::Guard(g) => g,
1838 _ => panic!("expected guard"),
1839 };
1840 let idx2 = guard2.shared().idx();
1841 drop(guard2);
1842 assert_eq!(idx1, idx2);
1843 }
1844
1845 #[test]
1849 fn test_guard_drop_after_overwrite_insert() {
1850 let cache: Cache<i32, i32> = Cache::new(8);
1851 let guard = match cache.get_value_or_guard(&1, None) {
1852 GuardResult::Guard(g) => g,
1853 _ => panic!("expected guard"),
1854 };
1855 cache.insert(1, 100);
1856 assert_eq!(cache.get(&1), Some(100));
1857 drop(guard);
1858 assert_eq!(cache.get(&1), Some(100));
1859 }
1860
1861 #[test]
1865 fn test_guard_drop_after_remove_and_reuse() {
1866 let cache: Cache<i32, i32> = Cache::new(8);
1867 let guard = match cache.get_value_or_guard(&1, None) {
1868 GuardResult::Guard(g) => g,
1869 _ => panic!("expected guard"),
1870 };
1871 cache.remove(&1);
1872 cache.insert(2, 222);
1873 assert_eq!(cache.get(&2), Some(222));
1874 drop(guard);
1875 assert_eq!(cache.get(&2), Some(222));
1876 }
1877}