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 lifecycle: L,
63}
64
65impl<Key: Eq + Hash, Val: Clone> Cache<Key, Val> {
66 pub fn new(items_capacity: usize) -> Self {
68 Self::with(
69 items_capacity,
70 items_capacity as u64,
71 Default::default(),
72 Default::default(),
73 Default::default(),
74 )
75 }
76}
77
78impl<Key: Eq + Hash, Val: Clone, We: Weighter<Key, Val> + Clone> Cache<Key, Val, We> {
79 pub fn with_weighter(
80 estimated_items_capacity: usize,
81 weight_capacity: u64,
82 weighter: We,
83 ) -> Self {
84 Self::with(
85 estimated_items_capacity,
86 weight_capacity,
87 weighter,
88 Default::default(),
89 Default::default(),
90 )
91 }
92}
93
94impl<
95 Key: Eq + Hash,
96 Val: Clone,
97 We: Weighter<Key, Val> + Clone,
98 B: BuildHasher + Clone,
99 L: Lifecycle<Key, Val> + Clone,
100 > Cache<Key, Val, We, B, L>
101{
102 pub fn with(
106 estimated_items_capacity: usize,
107 weight_capacity: u64,
108 weighter: We,
109 hash_builder: B,
110 lifecycle: L,
111 ) -> Self {
112 Self::with_options(
113 OptionsBuilder::new()
114 .estimated_items_capacity(estimated_items_capacity)
115 .weight_capacity(weight_capacity)
116 .build()
117 .unwrap(),
118 weighter,
119 hash_builder,
120 lifecycle,
121 )
122 }
123
124 pub fn with_options(options: Options, weighter: We, hash_builder: B, lifecycle: L) -> Self {
143 let mut num_shards = options.shards.next_power_of_two() as u64;
144 let estimated_items_capacity = options.estimated_items_capacity as u64;
145 let weight_capacity = options.weight_capacity;
146 let mut shard_items_cap =
147 estimated_items_capacity.saturating_add(num_shards - 1) / num_shards;
148 let mut shard_weight_cap =
149 options.weight_capacity.saturating_add(num_shards - 1) / num_shards;
150 while shard_items_cap < 32 && num_shards > 1 {
152 num_shards /= 2;
153 shard_items_cap = estimated_items_capacity.saturating_add(num_shards - 1) / num_shards;
154 shard_weight_cap = weight_capacity.saturating_add(num_shards - 1) / num_shards;
155 }
156 let shards = (0..num_shards)
157 .map(|_| {
158 RwLock::new(CacheShard::new(
159 options.hot_allocation,
160 options.ghost_allocation,
161 shard_items_cap as usize,
162 shard_weight_cap,
163 weighter.clone(),
164 hash_builder.clone(),
165 lifecycle.clone(),
166 ))
167 })
168 .collect::<Vec<_>>();
169 Self {
170 shards: shards.into_boxed_slice(),
171 hash_builder,
172 shards_mask: num_shards - 1,
173 lifecycle,
174 }
175 }
176
177 #[cfg(fuzzing)]
178 pub fn validate(&self) {
179 for s in &*self.shards {
180 s.read().validate(false)
181 }
182 }
183
184 pub fn is_empty(&self) -> bool {
186 self.shards.iter().all(|s| s.read().len() == 0)
187 }
188
189 pub fn len(&self) -> usize {
191 self.shards.iter().map(|s| s.read().len()).sum()
192 }
193
194 pub fn weight(&self) -> u64 {
196 self.shards.iter().map(|s| s.read().weight()).sum()
197 }
198
199 pub fn capacity(&self) -> u64 {
203 self.shards.iter().map(|s| s.read().capacity()).sum()
204 }
205
206 pub fn shard_capacity(&self) -> u64 {
208 self.shards[0].read().capacity()
209 }
210
211 pub fn num_shards(&self) -> usize {
213 self.shards.len()
214 }
215
216 #[cfg(feature = "stats")]
218 pub fn misses(&self) -> u64 {
219 self.shards.iter().map(|s| s.read().misses()).sum()
220 }
221
222 #[cfg(feature = "stats")]
224 pub fn hits(&self) -> u64 {
225 self.shards.iter().map(|s| s.read().hits()).sum()
226 }
227
228 #[inline]
229 fn compute_shard_index(&self, hash: u64) -> u64 {
230 hash.rotate_right(usize::BITS / 2) & self.shards_mask
235 }
236
237 #[inline]
254 pub fn shard_index<Q: Hash + Equivalent<Key> + ?Sized>(&self, key: &Q) -> usize {
255 let hash = self.hash_builder.hash_one(key);
256 self.compute_shard_index(hash) as usize
257 }
258
259 #[inline]
260 fn shard_for<Q>(
261 &self,
262 key: &Q,
263 ) -> Option<(
264 &RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>,
265 u64,
266 )>
267 where
268 Q: Hash + Equivalent<Key> + ?Sized,
269 {
270 let hash = self.hash_builder.hash_one(key);
271 let shard_idx = self.compute_shard_index(hash) as usize;
272 self.shards.get(shard_idx).map(|s| (s, hash))
273 }
274
275 pub fn reserve(&self, additional: usize) {
278 let additional_per_shard =
279 additional.saturating_add(self.shards.len() - 1) / self.shards.len();
280 for s in &*self.shards {
281 s.write().reserve(additional_per_shard);
282 }
283 }
284
285 pub fn contains_key<Q>(&self, key: &Q) -> bool
287 where
288 Q: Hash + Equivalent<Key> + ?Sized,
289 {
290 self.shard_for(key)
291 .is_some_and(|(shard, hash)| shard.read().contains(hash, key))
292 }
293
294 pub fn try_contains_key<Q>(&self, key: &Q) -> Result<bool, LockContention>
298 where
299 Q: Hash + Equivalent<Key> + ?Sized,
300 {
301 let Some((shard, hash)) = self.shard_for(key) else {
302 return Ok(false);
303 };
304
305 match shard.try_read() {
306 Some(guard) => Ok(guard.contains(hash, key)),
307 None => Err(LockContention),
308 }
309 }
310
311 pub fn get<Q>(&self, key: &Q) -> Option<Val>
313 where
314 Q: Hash + Equivalent<Key> + ?Sized,
315 {
316 let (shard, hash) = self.shard_for(key)?;
317 shard.read().get(hash, key).cloned()
318 }
319
320 pub fn try_get<Q>(&self, key: &Q) -> Result<Option<Val>, LockContention>
324 where
325 Q: Hash + Equivalent<Key> + ?Sized,
326 {
327 let Some((shard, hash)) = self.shard_for(key) else {
328 return Ok(None);
329 };
330
331 match shard.try_read() {
332 Some(guard) => Ok(guard.get(hash, key).cloned()),
333 None => Err(LockContention),
334 }
335 }
336
337 pub fn peek<Q>(&self, key: &Q) -> Option<Val>
340 where
341 Q: Hash + Equivalent<Key> + ?Sized,
342 {
343 let (shard, hash) = self.shard_for(key)?;
344 shard.read().peek(hash, key).cloned()
345 }
346
347 pub fn try_peek<Q>(&self, key: &Q) -> Result<Option<Val>, LockContention>
352 where
353 Q: Hash + Equivalent<Key> + ?Sized,
354 {
355 let Some((shard, hash)) = self.shard_for(key) else {
356 return Ok(None);
357 };
358 match shard.try_read() {
359 Some(guard) => Ok(guard.peek(hash, key).cloned()),
360 None => Err(LockContention),
361 }
362 }
363
364 pub fn remove<Q>(&self, key: &Q) -> Option<(Key, Val)>
367 where
368 Q: Hash + Equivalent<Key> + ?Sized,
369 {
370 let (shard, hash) = self.shard_for(key).unwrap();
371 shard.write().remove(hash, key)
372 }
373
374 pub fn try_remove<Q>(&self, key: &Q) -> Result<Option<(Key, Val)>, LockContention>
378 where
379 Q: Hash + Equivalent<Key> + ?Sized,
380 {
381 let Some((shard, hash)) = self.shard_for(key) else {
382 return Ok(None);
383 };
384
385 match shard.try_write() {
386 Some(mut guard) => Ok(guard.remove(hash, key)),
387 None => Err(LockContention),
388 }
389 }
390
391 pub fn remove_if<Q, F>(&self, key: &Q, f: F) -> Option<(Key, Val)>
396 where
397 Q: Hash + Equivalent<Key> + ?Sized,
398 F: FnOnce(&Val) -> bool,
399 {
400 let (shard, hash) = self.shard_for(key).unwrap();
401 shard.write().remove_if(hash, key, f)
402 }
403
404 pub fn replace(&self, key: Key, value: Val, soft: bool) -> Result<(), (Key, Val)> {
410 let lcs = self.replace_with_lifecycle(key, value, soft)?;
411 self.lifecycle.end_request(lcs);
412 Ok(())
413 }
414
415 pub fn replace_with_lifecycle(
421 &self,
422 key: Key,
423 value: Val,
424 soft: bool,
425 ) -> Result<L::RequestState, (Key, Val)> {
426 let mut lcs = self.lifecycle.begin_request();
427 let (shard, hash) = self.shard_for(&key).unwrap();
428 shard
429 .write()
430 .insert(&mut lcs, hash, key, value, InsertStrategy::Replace { soft })?;
431 Ok(lcs)
432 }
433
434 pub fn retain<F>(&self, f: F)
438 where
439 F: Fn(&Key, &Val) -> bool,
440 {
441 for s in self.shards.iter() {
442 s.write().retain(&f);
443 }
444 }
445
446 pub fn insert(&self, key: Key, value: Val) {
448 let lcs = self.insert_with_lifecycle(key, value);
449 self.lifecycle.end_request(lcs);
450 }
451
452 pub fn try_insert(&self, key: Key, value: Val) -> Result<(), (Key, Val)> {
457 let lcs = self.try_insert_with_lifecycle(key, value)?;
458 self.lifecycle.end_request(lcs);
459 Ok(())
460 }
461
462 pub fn insert_with_lifecycle(&self, key: Key, value: Val) -> L::RequestState {
464 let mut lcs = self.lifecycle.begin_request();
465 let (shard, hash) = self.shard_for(&key).unwrap();
466 let result = shard
467 .write()
468 .insert(&mut lcs, hash, key, value, InsertStrategy::Insert);
469 debug_assert!(result.is_ok());
471 lcs
472 }
473
474 pub fn try_insert_with_lifecycle(
480 &self,
481 key: Key,
482 value: Val,
483 ) -> Result<L::RequestState, (Key, Val)> {
484 let mut lcs = self.lifecycle.begin_request();
487 let (shard, hash) = self.shard_for(&key).unwrap();
488
489 match shard.try_write() {
490 Some(mut shard) => {
491 let result = shard.insert(&mut lcs, hash, key, value, InsertStrategy::Insert);
492 debug_assert!(result.is_ok());
494 Ok(lcs)
495 }
496 _ => Err((key, value)),
497 }
498 }
499
500 pub fn clear(&self) {
502 for s in self.shards.iter() {
503 s.write().clear();
504 }
505 }
506
507 pub fn iter(&self) -> Iter<'_, Key, Val, We, B, L>
513 where
514 Key: Clone,
515 {
516 Iter {
517 shards: &self.shards,
518 current_shard: 0,
519 last: None,
520 }
521 }
522
523 pub fn drain(&self) -> Drain<'_, Key, Val, We, B, L> {
534 Drain {
535 shards: &self.shards,
536 current_shard: 0,
537 last: None,
538 }
539 }
540
541 pub fn set_capacity(&self, new_weight_capacity: u64) {
547 let shard_weight_cap = new_weight_capacity.saturating_add(self.shards.len() as u64 - 1)
548 / self.shards.len() as u64;
549 for shard in &*self.shards {
550 shard.write().set_capacity(shard_weight_cap);
551 }
552 }
553
554 pub fn get_value_or_guard<Q>(
566 &self,
567 key: &Q,
568 timeout: Option<Duration>,
569 ) -> GuardResult<'_, Key, Val, We, B, L>
570 where
571 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
572 {
573 let (shard, hash) = self.shard_for(key).unwrap();
574 if let Some(v) = shard.read().get(hash, key) {
575 return GuardResult::Value(v.clone());
576 }
577 PlaceholderGuard::join(&self.lifecycle, shard, hash, key, timeout)
578 }
579
580 pub fn get_or_insert_with<Q, E>(
584 &self,
585 key: &Q,
586 with: impl FnOnce() -> Result<Val, E>,
587 ) -> Result<Val, E>
588 where
589 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
590 {
591 match self.get_value_or_guard(key, None) {
592 GuardResult::Value(v) => Ok(v),
593 GuardResult::Guard(g) => {
594 let v = with()?;
595 let _ = g.insert(v.clone());
596 Ok(v)
597 }
598 GuardResult::Timeout => unsafe { unreachable_unchecked() },
599 }
600 }
601
602 pub async fn get_value_or_guard_async<'a, Q>(
610 &'a self,
611 key: &Q,
612 ) -> Result<Val, PlaceholderGuard<'a, 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 loop {
618 if let Some(v) = shard.read().get(hash, key) {
619 return Ok(v.clone());
620 }
621 match JoinFuture::new(&self.lifecycle, shard, hash, key).await {
622 JoinResult::Filled(Some(shared)) => {
623 return Ok(unsafe { shared.value().unwrap_unchecked().clone() });
625 }
626 JoinResult::Filled(None) => continue,
627 JoinResult::Guard(g) => return Err(g),
628 JoinResult::Timeout => unsafe { unreachable_unchecked() },
629 }
630 }
631 }
632
633 pub async fn get_or_insert_async<Q, E>(
635 &self,
636 key: &Q,
637 with: impl Future<Output = Result<Val, E>>,
638 ) -> Result<Val, E>
639 where
640 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
641 {
642 match self.get_value_or_guard_async(key).await {
643 Ok(v) => Ok(v),
644 Err(g) => {
645 let v = with.await?;
646 let _ = g.insert(v.clone());
647 Ok(v)
648 }
649 }
650 }
651
652 pub fn entry<Q, T>(
702 &self,
703 key: &Q,
704 timeout: Option<Duration>,
705 on_occupied: impl FnOnce(&Key, &mut Val) -> EntryAction<T>,
706 ) -> EntryResult<'_, Key, Val, We, B, L, T>
707 where
708 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
709 {
710 let (shard, hash) = self.shard_for(key).unwrap();
711 let mut on_occupied = Some(on_occupied);
716 let mut callback = |k: &Key, v: &mut Val| on_occupied.take().unwrap()(k, v);
717 let mut deadline = timeout.map(Ok);
718
719 loop {
720 let mut shard_guard = shard.write();
721 match shard_guard.entry_or_placeholder(hash, key, &mut callback) {
722 EntryOrPlaceholder::Kept(t) => return EntryResult::Retained(t),
723 EntryOrPlaceholder::Removed(k, v) => return EntryResult::Removed(k, v),
724 EntryOrPlaceholder::Replaced(shared, old_val) => {
725 drop(shard_guard);
726 return EntryResult::Replaced(
727 PlaceholderGuard::start_loading(&self.lifecycle, shard, shared),
728 old_val,
729 );
730 }
731 EntryOrPlaceholder::NewPlaceholder(shared) => {
732 drop(shard_guard);
733 return EntryResult::Vacant(PlaceholderGuard::start_loading(
734 &self.lifecycle,
735 shard,
736 shared,
737 ));
738 }
739 EntryOrPlaceholder::ExistingPlaceholder(shared) => {
740 match PlaceholderGuard::wait_for_placeholder(
741 &self.lifecycle,
742 shard,
743 shard_guard,
744 shared,
745 deadline.as_mut(),
746 ) {
747 JoinResult::Filled(_) => continue,
748 JoinResult::Guard(g) => return EntryResult::Vacant(g),
749 JoinResult::Timeout => return EntryResult::Timeout,
750 }
751 }
752 }
753 }
754 }
755
756 pub async fn entry_async<'a, Q, T>(
763 &'a self,
764 key: &Q,
765 on_occupied: impl FnOnce(&Key, &mut Val) -> EntryAction<T>,
766 ) -> EntryResult<'a, Key, Val, We, B, L, T>
767 where
768 Q: Hash + Equivalent<Key> + ToOwned<Owned = Key> + ?Sized,
769 {
770 let (shard, hash) = self.shard_for(key).unwrap();
771 let mut on_occupied = Some(on_occupied);
773 let mut callback = |k: &Key, v: &mut Val| on_occupied.take().unwrap()(k, v);
774
775 loop {
776 let result = {
779 let mut shard_guard = shard.write();
780 match shard_guard.entry_or_placeholder(hash, key, &mut callback) {
781 EntryOrPlaceholder::Kept(t) => Ok(EntryResult::Retained(t)),
782 EntryOrPlaceholder::Removed(k, v) => Ok(EntryResult::Removed(k, v)),
783 EntryOrPlaceholder::Replaced(shared, old_val) => {
784 drop(shard_guard);
785 Ok(EntryResult::Replaced(
786 PlaceholderGuard::start_loading(&self.lifecycle, shard, shared),
787 old_val,
788 ))
789 }
790 EntryOrPlaceholder::NewPlaceholder(shared) => {
791 drop(shard_guard);
792 Ok(EntryResult::Vacant(PlaceholderGuard::start_loading(
793 &self.lifecycle,
794 shard,
795 shared,
796 )))
797 }
798 EntryOrPlaceholder::ExistingPlaceholder(_) => Err(()),
799 }
800 };
801 match result {
802 Ok(entry_result) => return entry_result,
803 Err(()) => match JoinFuture::new(&self.lifecycle, shard, hash, key).await {
804 JoinResult::Filled(_) => continue,
805 JoinResult::Guard(g) => return EntryResult::Vacant(g),
806 JoinResult::Timeout => unsafe { unreachable_unchecked() },
807 },
808 }
809 }
810 }
811
812 pub fn memory_used(&self) -> MemoryUsed {
817 let mut total = MemoryUsed { entries: 0, map: 0 };
818 self.shards.iter().for_each(|shard| {
819 let shard_memory = shard.read().memory_used();
820 total.entries += shard_memory.entries;
821 total.map += shard_memory.map;
822 });
823 total
824 }
825}
826
827impl<Key, Val, We, B, L> std::fmt::Debug for Cache<Key, Val, We, B, L> {
828 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
829 f.debug_struct("Cache").finish_non_exhaustive()
830 }
831}
832
833pub struct Iter<'a, Key, Val, We, B, L> {
837 shards: &'a [RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>],
838 current_shard: usize,
839 last: Option<Token>,
840}
841
842impl<Key, Val, We, B, L> Iterator for Iter<'_, Key, Val, We, B, L>
843where
844 Key: Clone,
845 Val: Clone,
846{
847 type Item = (Key, Val);
848
849 fn next(&mut self) -> Option<Self::Item> {
850 while self.current_shard < self.shards.len() {
851 let shard = &self.shards[self.current_shard];
852 let lock = shard.read();
853 if let Some((new_last, key, val)) = lock.iter_from(self.last).next() {
854 self.last = Some(new_last);
855 return Some((key.clone(), val.clone()));
856 }
857 self.last = None;
858 self.current_shard += 1;
859 }
860 None
861 }
862}
863
864impl<Key, Val, We, B, L> std::fmt::Debug for Iter<'_, Key, Val, We, B, L> {
865 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
866 f.debug_struct("Iter").finish_non_exhaustive()
867 }
868}
869
870pub struct Drain<'a, Key, Val, We, B, L> {
874 shards: &'a [RwLock<CacheShard<Key, Val, We, B, L, SharedPlaceholder<Val>>>],
875 current_shard: usize,
876 last: Option<Token>,
877}
878
879impl<Key, Val, We, B, L> Iterator for Drain<'_, Key, Val, We, B, L>
880where
881 Key: Hash + Eq,
882 We: Weighter<Key, Val>,
883 B: BuildHasher,
884 L: Lifecycle<Key, Val>,
885{
886 type Item = (Key, Val);
887
888 fn next(&mut self) -> Option<Self::Item> {
889 while self.current_shard < self.shards.len() {
890 let shard = &self.shards[self.current_shard];
891 let mut lock = shard.write();
892 if let Some((new_last, key, value)) = lock.remove_next(self.last) {
893 self.last = Some(new_last);
894 return Some((key, value));
895 }
896 self.last = None;
897 self.current_shard += 1;
898 }
899 None
900 }
901}
902
903impl<Key, Val, We, B, L> std::fmt::Debug for Drain<'_, Key, Val, We, B, L> {
904 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
905 f.debug_struct("Drain").finish_non_exhaustive()
906 }
907}
908
909pub struct DefaultLifecycle<Key, Val>(std::marker::PhantomData<(Key, Val)>);
913
914impl<Key, Val> std::fmt::Debug for DefaultLifecycle<Key, Val> {
915 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
916 f.debug_tuple("DefaultLifecycle").finish()
917 }
918}
919
920impl<Key, Val> Default for DefaultLifecycle<Key, Val> {
921 #[inline]
922 fn default() -> Self {
923 Self(Default::default())
924 }
925}
926impl<Key, Val> Clone for DefaultLifecycle<Key, Val> {
927 #[inline]
928 fn clone(&self) -> Self {
929 Self(Default::default())
930 }
931}
932
933impl<Key, Val> Lifecycle<Key, Val> for DefaultLifecycle<Key, Val> {
934 type RequestState = [Option<(Key, Val)>; 2];
940
941 #[inline]
942 fn begin_request(&self) -> Self::RequestState {
943 [None, None]
944 }
945
946 #[inline]
947 fn on_evict(&self, state: &mut Self::RequestState, key: Key, val: Val) {
948 if std::mem::needs_drop::<(Key, Val)>() {
949 if state[0].is_none() {
950 state[0] = Some((key, val));
951 } else if state[1].is_none() {
952 state[1] = Some((key, val));
953 }
954 }
955 }
956}
957
958#[cfg(test)]
959mod tests {
960 use super::*;
961 use std::{
962 sync::{Arc, Barrier},
963 thread,
964 };
965
966 #[test]
967 #[cfg_attr(miri, ignore)]
968 fn test_multiple_threads() {
969 const N_THREAD_PAIRS: usize = 8;
970 const N_ROUNDS: usize = 1_000;
971 const ITEMS_PER_THREAD: usize = 1_000;
972 let mut threads = Vec::new();
973 let barrier = Arc::new(Barrier::new(N_THREAD_PAIRS * 2));
974 let cache = Arc::new(Cache::new(N_THREAD_PAIRS * ITEMS_PER_THREAD / 10));
975 for t in 0..N_THREAD_PAIRS {
976 let barrier = barrier.clone();
977 let cache = cache.clone();
978 let handle = thread::spawn(move || {
979 let start = ITEMS_PER_THREAD * t;
980 barrier.wait();
981 for _round in 0..N_ROUNDS {
982 for i in start..start + ITEMS_PER_THREAD {
983 cache.insert(i, i);
984 }
985 }
986 });
987 threads.push(handle);
988 }
989 for t in 0..N_THREAD_PAIRS {
990 let barrier = barrier.clone();
991 let cache = cache.clone();
992 let handle = thread::spawn(move || {
993 let start = ITEMS_PER_THREAD * t;
994 barrier.wait();
995 for _round in 0..N_ROUNDS {
996 for i in start..start + ITEMS_PER_THREAD {
997 if let Some(cached) = cache.get(&i) {
998 assert_eq!(cached, i);
999 }
1000 }
1001 }
1002 });
1003 threads.push(handle);
1004 }
1005 for t in threads {
1006 t.join().unwrap();
1007 }
1008 }
1009
1010 #[test]
1011 fn test_iter() {
1012 let capacity = if cfg!(miri) { 100 } else { 100000 };
1013 let options = OptionsBuilder::new()
1014 .estimated_items_capacity(capacity)
1015 .weight_capacity(capacity as u64)
1016 .shards(2)
1017 .build()
1018 .unwrap();
1019 let cache = Cache::with_options(
1020 options,
1021 UnitWeighter,
1022 DefaultHashBuilder::default(),
1023 DefaultLifecycle::default(),
1024 );
1025 let items = capacity / 2;
1026 for i in 0..items {
1027 cache.insert(i, i);
1028 }
1029 assert_eq!(cache.len(), items);
1030 let mut iter_collected = cache.iter().collect::<Vec<_>>();
1031 assert_eq!(iter_collected.len(), items);
1032 iter_collected.sort();
1033 for (i, v) in iter_collected.into_iter().enumerate() {
1034 assert_eq!((i, i), v);
1035 }
1036 }
1037
1038 #[test]
1039 fn test_drain() {
1040 let capacity = if cfg!(miri) { 100 } else { 100000 };
1041 let options = OptionsBuilder::new()
1042 .estimated_items_capacity(capacity)
1043 .weight_capacity(capacity as u64)
1044 .shards(2)
1045 .build()
1046 .unwrap();
1047 let cache = Cache::with_options(
1048 options,
1049 UnitWeighter,
1050 DefaultHashBuilder::default(),
1051 DefaultLifecycle::default(),
1052 );
1053 let items = capacity / 2;
1054 for i in 0..items {
1055 cache.insert(i, i);
1056 }
1057 assert_eq!(cache.len(), items);
1058 let mut drain_collected = cache.drain().collect::<Vec<_>>();
1059 assert_eq!(cache.len(), 0);
1060 assert_eq!(drain_collected.len(), items);
1061 drain_collected.sort();
1062 for (i, v) in drain_collected.into_iter().enumerate() {
1063 assert_eq!((i, i), v);
1064 }
1065 }
1066
1067 #[test]
1068 fn test_set_capacity() {
1069 let cache = Cache::new(100);
1070 for i in 0..80 {
1071 cache.insert(i, i);
1072 }
1073 let initial_len = cache.len();
1074 assert!(initial_len <= 80);
1075
1076 cache.set_capacity(50);
1078 assert!(cache.len() <= 50);
1079 assert!(cache.weight() <= 50);
1080
1081 cache.set_capacity(200);
1083 assert_eq!(cache.capacity(), 200);
1084
1085 for i in 100..180 {
1087 cache.insert(i, i);
1088 }
1089 assert!(cache.len() <= 180);
1090 assert!(cache.weight() <= 200);
1091 }
1092
1093 #[test]
1094 fn test_remove_if() {
1095 let cache = Cache::new(100);
1096
1097 cache.insert(1, 10);
1099 cache.insert(2, 20);
1100 cache.insert(3, 30);
1101
1102 let removed = cache.remove_if(&2, |v| *v == 20);
1104 assert_eq!(removed, Some((2, 20)));
1105 assert_eq!(cache.get(&2), None);
1106
1107 let not_removed = cache.remove_if(&3, |v| *v == 999);
1109 assert_eq!(not_removed, None);
1110 assert_eq!(cache.get(&3), Some(30));
1111
1112 let not_found = cache.remove_if(&999, |_| true);
1114 assert_eq!(not_found, None);
1115 }
1116
1117 #[test]
1119 fn test_entry_actions() {
1120 let cache = Cache::new(100);
1121 cache.insert(1, 10);
1122 cache.insert(2, 20);
1123
1124 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1126 assert!(matches!(result, EntryResult::Retained(10)));
1127 assert_eq!(cache.get(&1), Some(10));
1128
1129 let result = cache.entry(&1, None, |_k, v| {
1131 *v += 5;
1132 EntryAction::Retain(())
1133 });
1134 assert!(matches!(result, EntryResult::Retained(())));
1135 assert_eq!(cache.get(&1), Some(15));
1136
1137 let result = cache.entry(&1, None, |_k, _v| EntryAction::<()>::Remove);
1139 assert!(matches!(result, EntryResult::Removed(1, 15)));
1140 assert_eq!(cache.get(&1), None);
1141
1142 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1144 match result {
1145 EntryResult::Vacant(g) => {
1146 let _ = g.insert(99);
1147 assert_eq!(cache.get(&1), Some(99));
1148 }
1149 _ => panic!("expected Vacant for removed key"),
1150 }
1151
1152 let mut old_val = 0;
1154 let result = cache.entry(&2, None, |_k, v| {
1155 old_val = *v;
1156 EntryAction::<()>::ReplaceWithGuard
1157 });
1158 assert_eq!(old_val, 20);
1159 match result {
1160 EntryResult::Replaced(g, old) => {
1161 assert_eq!(old, 20);
1162 let _ = g.insert(old_val + 100);
1163 assert_eq!(cache.get(&2), Some(120));
1164 }
1165 _ => panic!("expected Replaced"),
1166 }
1167
1168 let result = cache.entry(&2, None, |_k, _v| EntryAction::<()>::ReplaceWithGuard);
1170 match result {
1171 EntryResult::Replaced(g, _old) => {
1172 drop(g);
1173 assert_eq!(cache.get(&2), None);
1174 }
1175 _ => panic!("expected Replaced"),
1176 }
1177
1178 let result = cache.entry(&3, None, |_k, v| EntryAction::Retain(*v));
1180 match result {
1181 EntryResult::Vacant(g) => {
1182 let _ = g.insert(30);
1183 assert_eq!(cache.get(&3), Some(30));
1184 }
1185 _ => panic!("expected Vacant"),
1186 }
1187 }
1188
1189 #[test]
1191 fn test_entry_weight_tracking() {
1192 #[derive(Clone)]
1193 struct StringWeighter;
1194 impl crate::Weighter<u64, String> for StringWeighter {
1195 fn weight(&self, _key: &u64, val: &String) -> u64 {
1196 val.len() as u64
1197 }
1198 }
1199
1200 let cache = Cache::with_weighter(100, 100_000, StringWeighter);
1201 cache.insert(1, "hello".to_string());
1202 cache.insert(2, "world".to_string());
1203 assert_eq!(cache.weight(), 10);
1204
1205 let result = cache.entry(&1, None, |_k, _v| EntryAction::Retain(()));
1207 assert!(matches!(result, EntryResult::Retained(())));
1208 assert_eq!(cache.weight(), 10);
1209
1210 let result = cache.entry(&1, None, |_k, v| {
1212 v.push_str(" world");
1213 EntryAction::Retain(())
1214 });
1215 assert!(matches!(result, EntryResult::Retained(())));
1216 assert_eq!(cache.weight(), 16); assert_eq!(cache.get(&1).unwrap(), "hello world");
1218
1219 let result = cache.entry(&1, None, |_k, v| {
1221 v.clear();
1222 EntryAction::Retain(())
1223 });
1224 assert!(matches!(result, EntryResult::Retained(())));
1225 assert_eq!(cache.weight(), 5); assert_eq!(cache.get(&1).unwrap(), "");
1227
1228 let result = cache.entry(&2, None, |_k, _v| EntryAction::<()>::Remove);
1230 assert!(matches!(result, EntryResult::Removed(2, _)));
1231 assert_eq!(cache.weight(), 0);
1232 assert_eq!(cache.len(), 1);
1233
1234 cache.insert(3, "hello".to_string());
1236 assert_eq!(cache.weight(), 5);
1237 let result = cache.entry(&3, None, |_k, _v| EntryAction::<()>::ReplaceWithGuard);
1238 match result {
1239 EntryResult::Replaced(g, _old) => {
1240 assert_eq!(cache.weight(), 0);
1241 let _ = g.insert("hello world!!".to_string());
1242 assert_eq!(cache.weight(), 13);
1243 }
1244 _ => panic!("expected Replaced"),
1245 }
1246 }
1247
1248 #[test]
1250 fn test_entry_eviction() {
1251 let cache = Cache::new(2);
1253 cache.insert(1, 10);
1254 cache.insert(2, 20);
1255 assert_eq!(cache.len(), 2);
1256
1257 let result = cache.entry(&3, None, |_k, v| EntryAction::Retain(*v));
1258 match result {
1259 EntryResult::Vacant(g) => {
1260 let _ = g.insert(30);
1261 assert!(cache.len() <= 2);
1262 assert_eq!(cache.get(&3), Some(30));
1263 }
1264 _ => panic!("expected Vacant"),
1265 }
1266
1267 let cache = Cache::new(0);
1269 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1270 match result {
1271 EntryResult::Vacant(g) => {
1272 let _ = g.insert(10);
1273 assert_eq!(cache.get(&1), None);
1274 }
1275 _ => panic!("expected Vacant"),
1276 }
1277 }
1278
1279 #[test]
1281 #[cfg_attr(miri, ignore)]
1282 fn test_entry_concurrent_placeholder_wait() {
1283 let cache = Arc::new(Cache::new(100));
1284 let barrier = Arc::new(Barrier::new(2));
1285
1286 let cache2 = cache.clone();
1288 let barrier2 = barrier.clone();
1289 let handle = thread::spawn(move || match cache2.get_value_or_guard(&1, None) {
1290 GuardResult::Guard(g) => {
1291 barrier2.wait();
1292 std::thread::sleep(Duration::from_millis(50));
1293 let _ = g.insert(42);
1294 }
1295 _ => panic!("expected guard"),
1296 });
1297
1298 barrier.wait();
1299 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1300 assert!(matches!(result, EntryResult::Retained(42)));
1301 handle.join().unwrap();
1302 }
1303
1304 #[test]
1306 #[cfg_attr(miri, ignore)]
1307 fn test_entry_concurrent_placeholder_guard_abandoned() {
1308 let cache = Arc::new(Cache::new(100));
1309 let barrier = Arc::new(Barrier::new(2));
1310
1311 let cache2 = cache.clone();
1312 let barrier2 = barrier.clone();
1313 let handle = thread::spawn(move || match cache2.get_value_or_guard(&1, None) {
1314 GuardResult::Guard(g) => {
1315 barrier2.wait();
1316 std::thread::sleep(Duration::from_millis(50));
1317 drop(g);
1318 }
1319 _ => panic!("expected guard"),
1320 });
1321
1322 barrier.wait();
1323 let result = cache.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1324 match result {
1325 EntryResult::Vacant(g) => {
1326 let _ = g.insert(99);
1327 assert_eq!(cache.get(&1), Some(99));
1328 }
1329 _ => panic!("expected Vacant after abandoned placeholder"),
1330 }
1331 handle.join().unwrap();
1332 }
1333
1334 #[test]
1336 #[cfg_attr(miri, ignore)]
1337 fn test_entry_timeout() {
1338 let cache = Cache::new(100);
1339
1340 let guard = match cache.get_value_or_guard(&1, None) {
1342 GuardResult::Guard(g) => g,
1343 _ => panic!("expected guard"),
1344 };
1345 let result = cache.entry(&1, Some(Duration::ZERO), |_k, v| EntryAction::Retain(*v));
1346 assert!(matches!(result, EntryResult::Timeout));
1347 let _ = guard.insert(1);
1348
1349 let cache = Arc::new(Cache::new(100));
1351 let barrier = Arc::new(Barrier::new(2));
1352 let cache2 = cache.clone();
1353 let barrier2 = barrier.clone();
1354 let holder = thread::spawn(move || {
1355 let guard = match cache2.get_value_or_guard(&1, None) {
1356 GuardResult::Guard(g) => g,
1357 _ => panic!("expected guard"),
1358 };
1359 barrier2.wait();
1360 std::thread::sleep(Duration::from_millis(200));
1361 let _ = guard.insert(1);
1362 });
1363
1364 barrier.wait();
1365 let result = cache.entry(&1, Some(Duration::from_millis(50)), |_k, v| {
1366 EntryAction::Retain(*v)
1367 });
1368 assert!(matches!(result, EntryResult::Timeout));
1369 holder.join().unwrap();
1370 }
1371
1372 #[test]
1374 #[cfg_attr(miri, ignore)]
1375 fn test_entry_concurrent_multiple_waiters() {
1376 let cache = Arc::new(Cache::new(100));
1377 let barrier = Arc::new(Barrier::new(4)); let cache1 = cache.clone();
1380 let barrier1 = barrier.clone();
1381 let loader = thread::spawn(move || match cache1.get_value_or_guard(&1, None) {
1382 GuardResult::Guard(g) => {
1383 barrier1.wait();
1384 std::thread::sleep(Duration::from_millis(50));
1385 let _ = g.insert(42);
1386 }
1387 _ => panic!("expected guard"),
1388 });
1389
1390 let mut waiters = Vec::new();
1391 for _ in 0..3 {
1392 let cache_c = cache.clone();
1393 let barrier_c = barrier.clone();
1394 waiters.push(thread::spawn(move || {
1395 barrier_c.wait();
1396 let result = cache_c.entry(&1, None, |_k, v| EntryAction::Retain(*v));
1397 match result {
1398 EntryResult::Retained(v) => v,
1399 _ => panic!("expected Value"),
1400 }
1401 }));
1402 }
1403
1404 loader.join().unwrap();
1405 for w in waiters {
1406 assert_eq!(w.join().unwrap(), 42);
1407 }
1408 }
1409
1410 #[test]
1412 #[cfg_attr(miri, ignore)]
1413 fn test_entry_concurrent_action_after_wait() {
1414 let cache = Arc::new(Cache::new(100));
1416 let barrier = Arc::new(Barrier::new(2));
1417
1418 let cache1 = cache.clone();
1419 let barrier1 = barrier.clone();
1420 let loader = thread::spawn(move || match cache1.get_value_or_guard(&1, None) {
1421 GuardResult::Guard(g) => {
1422 barrier1.wait();
1423 std::thread::sleep(Duration::from_millis(50));
1424 let _ = g.insert(42);
1425 }
1426 _ => panic!("expected guard"),
1427 });
1428
1429 barrier.wait();
1430 let result = cache.entry(&1, None, |_k, _v| EntryAction::<()>::ReplaceWithGuard);
1431 match result {
1432 EntryResult::Replaced(g, old) => {
1433 assert_eq!(old, 42);
1434 let _ = g.insert(100);
1435 assert_eq!(cache.get(&1), Some(100));
1436 }
1437 _ => panic!("expected Replaced"),
1438 }
1439 loader.join().unwrap();
1440
1441 let cache = Arc::new(Cache::new(100));
1443 let barrier = Arc::new(Barrier::new(2));
1444
1445 let cache1 = cache.clone();
1446 let barrier1 = barrier.clone();
1447 let loader = thread::spawn(move || match cache1.get_value_or_guard(&1, None) {
1448 GuardResult::Guard(g) => {
1449 barrier1.wait();
1450 std::thread::sleep(Duration::from_millis(50));
1451 let _ = g.insert(42);
1452 }
1453 _ => panic!("expected guard"),
1454 });
1455
1456 barrier.wait();
1457 let result = cache.entry(&1, None, |_k, _v| EntryAction::<()>::Remove);
1458 assert!(matches!(result, EntryResult::Removed(1, 42)));
1459 assert_eq!(cache.get(&1), None);
1460 loader.join().unwrap();
1461 }
1462
1463 #[test]
1465 #[cfg_attr(miri, ignore)]
1466 fn test_entry_concurrent_stress() {
1467 const N_THREADS: usize = 8;
1468 const N_KEYS: usize = 50;
1469 const N_OPS: usize = 500;
1470
1471 let cache = Arc::new(Cache::new(1000));
1472 let barrier = Arc::new(Barrier::new(N_THREADS));
1473
1474 let mut handles = Vec::new();
1475 for t in 0..N_THREADS {
1476 let cache = cache.clone();
1477 let barrier = barrier.clone();
1478 handles.push(thread::spawn(move || {
1479 barrier.wait();
1480 for i in 0..N_OPS {
1481 let key = (t * N_OPS + i) % N_KEYS;
1482 let result = cache.entry(&key, Some(Duration::from_millis(10)), |_k, v| {
1483 EntryAction::Retain(*v)
1484 });
1485 match result {
1486 EntryResult::Retained(_) => {}
1487 EntryResult::Vacant(g) => {
1488 let _ = g.insert(key * 10);
1489 }
1490 EntryResult::Replaced(g, _) => {
1491 let _ = g.insert(key * 10);
1492 }
1493 EntryResult::Timeout => {}
1494 EntryResult::Removed(_, _) => {}
1495 }
1496 }
1497 }));
1498 }
1499
1500 for h in handles {
1501 h.join().unwrap();
1502 }
1503
1504 assert!(cache.len() <= N_KEYS);
1505 for key in 0..N_KEYS {
1506 if let Some(v) = cache.get(&key) {
1507 assert_eq!(v, key * 10);
1508 }
1509 }
1510 }
1511
1512 #[tokio::test]
1516 async fn test_entry_async_actions() {
1517 let cache = Cache::new(100);
1518 cache.insert(1, 10);
1519 cache.insert(2, 20);
1520
1521 let result = cache.entry_async(&1, |_k, v| EntryAction::Retain(*v)).await;
1523 assert!(matches!(result, EntryResult::Retained(10)));
1524 assert_eq!(cache.get(&1), Some(10));
1525
1526 let result = cache
1528 .entry_async(&1, |_k, _v| EntryAction::<()>::Remove)
1529 .await;
1530 assert!(matches!(result, EntryResult::Removed(1, 10)));
1531 assert_eq!(cache.get(&1), None);
1532
1533 let result = cache
1535 .entry_async(&2, |_k, _v| EntryAction::<()>::ReplaceWithGuard)
1536 .await;
1537 match result {
1538 EntryResult::Replaced(g, old) => {
1539 assert_eq!(old, 20);
1540 let _ = g.insert(42);
1541 assert_eq!(cache.get(&2), Some(42));
1542 }
1543 _ => panic!("expected Replaced"),
1544 }
1545
1546 let result = cache.entry_async(&3, |_k, v| EntryAction::Retain(*v)).await;
1548 match result {
1549 EntryResult::Vacant(g) => {
1550 let _ = g.insert(99);
1551 assert_eq!(cache.get(&3), Some(99));
1552 }
1553 _ => panic!("expected Vacant"),
1554 }
1555 }
1556
1557 #[tokio::test(flavor = "multi_thread")]
1559 async fn test_entry_async_concurrent_wait() {
1560 let cache = Arc::new(Cache::new(100));
1561 let barrier = Arc::new(Barrier::new(2));
1562
1563 let cache1 = cache.clone();
1564 let barrier1 = barrier.clone();
1565 let holder = thread::spawn(move || {
1566 let guard = match cache1.get_value_or_guard(&1, None) {
1567 GuardResult::Guard(g) => g,
1568 _ => panic!("expected guard"),
1569 };
1570 barrier1.wait();
1571 std::thread::sleep(Duration::from_millis(50));
1572 let _ = guard.insert(42);
1573 });
1574
1575 barrier.wait();
1576 let result = cache.entry_async(&1, |_k, v| EntryAction::Retain(*v)).await;
1577 assert!(matches!(result, EntryResult::Retained(42)));
1578 holder.join().unwrap();
1579 }
1580
1581 #[tokio::test(flavor = "multi_thread")]
1583 async fn test_entry_async_concurrent_guard_abandoned() {
1584 let cache = Arc::new(Cache::new(100));
1585 let barrier = Arc::new(Barrier::new(2));
1586
1587 let cache1 = cache.clone();
1588 let barrier1 = barrier.clone();
1589 let holder = thread::spawn(move || {
1590 let guard = match cache1.get_value_or_guard(&1, None) {
1591 GuardResult::Guard(g) => g,
1592 _ => panic!("expected guard"),
1593 };
1594 barrier1.wait();
1595 std::thread::sleep(Duration::from_millis(50));
1596 drop(guard);
1597 });
1598
1599 barrier.wait();
1600 let result = cache.entry_async(&1, |_k, v| EntryAction::Retain(*v)).await;
1601 match result {
1602 EntryResult::Vacant(g) => {
1603 let _ = g.insert(99);
1604 }
1605 _ => panic!("expected Vacant after abandoned placeholder"),
1606 }
1607 assert_eq!(cache.get(&1), Some(99));
1608 holder.join().unwrap();
1609 }
1610
1611 #[tokio::test(flavor = "multi_thread")]
1613 #[cfg_attr(miri, ignore)]
1614 async fn test_entry_async_concurrent_stress() {
1615 const N_TASKS: usize = 16;
1616 const N_KEYS: usize = 50;
1617 const N_OPS: usize = 200;
1618
1619 let cache = Arc::new(Cache::new(1000));
1620 let barrier = Arc::new(tokio::sync::Barrier::new(N_TASKS));
1621
1622 let mut handles = Vec::new();
1623 for t in 0..N_TASKS {
1624 let cache = cache.clone();
1625 let barrier = barrier.clone();
1626 handles.push(tokio::spawn(async move {
1627 barrier.wait().await;
1628 for i in 0..N_OPS {
1629 let key = (t * N_OPS + i) % N_KEYS;
1630 let _ = cache
1633 .get_or_insert_async(&key, async { Ok::<_, ()>(key * 10) })
1634 .await;
1635 }
1636 }));
1637 }
1638
1639 for h in handles {
1640 h.await.unwrap();
1641 }
1642
1643 assert!(cache.len() <= N_KEYS);
1644 for key in 0..N_KEYS {
1645 if let Some(v) = cache.get(&key) {
1646 assert_eq!(v, key * 10);
1647 }
1648 }
1649 }
1650
1651 #[test]
1653 fn test_try_contains_key() {
1654 let cache = Cache::new(100);
1655 cache.insert(1, 10);
1656
1657 assert!(cache.try_contains_key(&1).is_ok_and(|v| v));
1658 assert!(cache.try_contains_key(&2).is_ok_and(|v| !v));
1659 }
1660
1661 #[test]
1662 fn test_try_contains_key_contended() {
1663 let cache = Cache::new(100);
1664 cache.insert(1, 10);
1665 let _guards: Vec<_> = cache.shards.iter().map(|s| s.write()).collect();
1667 assert!(cache.try_contains_key(&1).is_err());
1668 }
1669
1670 #[test]
1671 fn test_try_get() {
1672 let cache = Cache::new(100);
1673 cache.insert(1, 10);
1674
1675 assert!(cache.try_get(&1).is_ok_and(|v| matches!(v, Some(10))));
1676 assert!(cache.try_get(&2).is_ok_and(|v| v.is_none()));
1677 }
1678
1679 #[test]
1680 fn test_try_get_contended() {
1681 let cache = Cache::new(100);
1682 cache.insert(1, 10);
1683 let _guards: Vec<_> = cache.shards.iter().map(|s| s.write()).collect();
1684 assert!(cache.try_get(&1).is_err());
1685 }
1686
1687 #[test]
1688 fn test_try_peek() {
1689 let cache = Cache::new(100);
1690 cache.insert(1, 10);
1691
1692 assert!(cache.try_peek(&1).is_ok_and(|v| matches!(v, Some(10))));
1693 assert!(cache.try_peek(&2).is_ok_and(|v| v.is_none()));
1694 }
1695
1696 #[test]
1697 fn test_try_peek_contended() {
1698 let cache = Cache::new(100);
1699 cache.insert(1, 10);
1700 let _guards: Vec<_> = cache.shards.iter().map(|s| s.write()).collect();
1701 assert!(cache.try_peek(&1).is_err());
1702 }
1703
1704 #[test]
1705 fn test_try_remove() {
1706 let cache = Cache::new(100);
1707 cache.insert(1, 10);
1708
1709 assert!(cache
1710 .try_remove(&1)
1711 .is_ok_and(|v| matches!(v, Some((1, 10)))));
1712 assert!(cache.try_remove(&1).is_ok_and(|v| v.is_none()));
1713 assert!(cache.try_remove(&99).is_ok_and(|v| v.is_none()));
1714 }
1715
1716 #[test]
1717 fn test_try_remove_contended() {
1718 let cache = Cache::new(100);
1719 cache.insert(1, 10);
1720 let guards: Vec<_> = cache.shards.iter().map(|s| s.read()).collect();
1722 assert!(cache.try_remove(&1).is_err());
1723 drop(guards);
1724 assert_eq!(cache.get(&1), Some(10));
1726 }
1727
1728 #[test]
1729 fn test_try_insert() {
1730 let cache = Cache::new(100);
1731
1732 assert_eq!(cache.try_insert(1, 10), Ok(()));
1733 assert_eq!(cache.get(&1), Some(10));
1734
1735 assert_eq!(cache.try_insert(1, 20), Ok(()));
1737 assert_eq!(cache.get(&1), Some(20));
1738 }
1739
1740 #[test]
1741 fn test_try_insert_contended() {
1742 let cache = Cache::new(100);
1743 let guards: Vec<_> = cache.shards.iter().map(|s| s.read()).collect();
1744 assert_eq!(cache.try_insert(1, 10), Err((1, 10)));
1745 drop(guards);
1746 assert_eq!(cache.get(&1), None);
1747 }
1748
1749 #[test]
1750 fn test_try_insert_with_lifecycle() {
1751 let cache = Cache::new(100);
1752
1753 let result = cache.try_insert_with_lifecycle(1, 10);
1755 assert!(result.is_ok());
1756 let lcs = result.ok().unwrap();
1757 cache.lifecycle.end_request(lcs);
1758 assert_eq!(cache.get(&1), Some(10));
1759
1760 let guards: Vec<_> = cache.shards.iter().map(|s| s.read()).collect();
1762 assert_eq!(cache.try_insert_with_lifecycle(2, 20), Err((2, 20)));
1763 drop(guards);
1764 assert_eq!(cache.get(&2), None);
1765 }
1766}