Skip to content

Commit d7a01d1

Browse files
committed
Improve thread safety
1 parent 78eedc2 commit d7a01d1

File tree

1 file changed

+70
-12
lines changed

1 file changed

+70
-12
lines changed

src/sync.rs

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,9 @@ where
482482

483483
/// Applies a function to all values in the cache.
484484
///
485-
/// This method marks all entries as visited.
485+
/// This method safely processes values by collecting them with the lock held,
486+
/// then releasing the lock before applying the function to each value individually.
487+
/// If the function modifies the values, the changes are saved back to the cache.
486488
///
487489
/// # Examples
488490
///
@@ -500,17 +502,37 @@ where
500502
/// assert_eq!(cache.get(&"key1".to_string()), Some("value1_updated".to_string()));
501503
/// assert_eq!(cache.get(&"key2".to_string()), Some("value2_updated".to_string()));
502504
/// ```
503-
pub fn for_each_value<F>(&self, f: F)
505+
pub fn for_each_value<F>(&self, mut f: F)
504506
where
505507
F: FnMut(&mut V),
508+
V: Clone,
506509
{
507-
let mut guard = self.locked_cache();
508-
guard.values_mut().for_each(f);
510+
// First, safely collect all keys and values while holding the lock
511+
let entries = self.with_lock(|inner_cache| {
512+
inner_cache
513+
.iter()
514+
.map(|(k, v)| (k.clone(), v.clone()))
515+
.collect::<Vec<(K, V)>>()
516+
});
517+
518+
// Process each value outside the lock
519+
let mut updated_entries = Vec::new();
520+
for (key, mut value) in entries {
521+
f(&mut value);
522+
updated_entries.push((key, value));
523+
}
524+
525+
// Update any changed values back to the cache
526+
for (key, value) in updated_entries {
527+
self.insert(key, value);
528+
}
509529
}
510530

511531
/// Applies a function to all key-value pairs in the cache.
512532
///
513-
/// This method marks all entries as visited.
533+
/// This method safely processes key-value pairs by collecting them with the lock held,
534+
/// then releasing the lock before applying the function to each pair individually.
535+
/// If the function modifies the values, the changes are saved back to the cache.
514536
///
515537
/// # Examples
516538
///
@@ -530,12 +552,31 @@ where
530552
/// assert_eq!(cache.get(&"key1".to_string()), Some("value1_special".to_string()));
531553
/// assert_eq!(cache.get(&"key2".to_string()), Some("value2".to_string()));
532554
/// ```
533-
pub fn for_each_entry<F>(&self, f: F)
555+
pub fn for_each_entry<F>(&self, mut f: F)
534556
where
535557
F: FnMut((&K, &mut V)),
558+
V: Clone,
536559
{
537-
let mut guard = self.locked_cache();
538-
guard.iter_mut().for_each(f);
560+
// First, safely collect all keys and values while holding the lock
561+
let entries = self.with_lock(|inner_cache| {
562+
inner_cache
563+
.iter()
564+
.map(|(k, v)| (k.clone(), v.clone()))
565+
.collect::<Vec<(K, V)>>()
566+
});
567+
568+
// Process each entry outside the lock
569+
let mut updated_entries = Vec::new();
570+
for (key, mut value) in entries {
571+
let key_ref = &key;
572+
f((key_ref, &mut value));
573+
updated_entries.push((key, value));
574+
}
575+
576+
// Update any changed values back to the cache
577+
for (key, value) in updated_entries {
578+
self.insert(key, value);
579+
}
539580
}
540581

541582
/// Gets exclusive access to the underlying cache to perform multiple operations atomically.
@@ -570,7 +611,9 @@ where
570611
///
571612
/// Removes all entries for which the provided function returns `false`.
572613
/// The elements are visited in arbitrary, unspecified order.
573-
/// This operation acquires a lock on the entire cache.
614+
/// This operation first collects entries while holding the lock, then evaluates
615+
/// the predicate on those entries outside the lock, and finally acquires the lock
616+
/// again only for each removal operation.
574617
///
575618
/// # Examples
576619
///
@@ -589,12 +632,27 @@ where
589632
/// assert!(cache.contains_key(&"key2".to_string()));
590633
/// assert!(cache.contains_key(&"key3".to_string()));
591634
/// ```
592-
pub fn retain<F>(&self, f: F)
635+
pub fn retain<F>(&self, mut f: F)
593636
where
594637
F: FnMut(&K, &V) -> bool,
638+
V: Clone,
595639
{
596-
let mut guard = self.locked_cache();
597-
guard.retain(f);
640+
// First, safely collect all entries while holding the lock
641+
let entries = self.with_lock(|inner_cache| {
642+
inner_cache
643+
.iter()
644+
.map(|(k, v)| (k.clone(), v.clone()))
645+
.collect::<Vec<(K, V)>>()
646+
});
647+
648+
// Now check each entry against the predicate without holding the lock
649+
for (key, value) in entries {
650+
if !f(&key, &value) {
651+
// Remove entries that don't match the predicate
652+
// This acquires the lock for each removal operation
653+
self.remove(&key);
654+
}
655+
}
598656
}
599657
}
600658

0 commit comments

Comments
 (0)