Skip to content

Commit a1dcfca

Browse files
committed
Explicitly separate state locks and awaits
1 parent 9f39700 commit a1dcfca

4 files changed

Lines changed: 82 additions & 71 deletions

File tree

.bazelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,16 @@ build --@rules_rust//:clippy_flag=-Wclippy::nursery
102102
build --@rules_rust//:clippy_flag=-Wclippy::pedantic
103103
build --@rules_rust//:clippy_flag=-Dclippy::alloc_instead_of_core
104104
build --@rules_rust//:clippy_flag=-Dclippy::as_underscore
105+
build --@rules_rust//:clippy_flag=-Dclippy::await_holding_lock
105106
build --@rules_rust//:clippy_flag=-Wclippy::dbg_macro
106107
build --@rules_rust//:clippy_flag=-Wclippy::decimal_literal_representation
107108
build --@rules_rust//:clippy_flag=-Dclippy::elidable_lifetime_names
109+
build --@rules_rust//:clippy_flag=-Dclippy::explicit_into_iter_loop
108110
build --@rules_rust//:clippy_flag=-Aclippy::get_unwrap
109111
build --@rules_rust//:clippy_flag=-Dclippy::missing_const_for_fn
110112
build --@rules_rust//:clippy_flag=-Aclippy::missing_docs_in_private_items
111113
build --@rules_rust//:clippy_flag=-Wclippy::print_stdout
114+
build --@rules_rust//:clippy_flag=-Dclippy::redundant_closure_for_method_calls
112115
build --@rules_rust//:clippy_flag=-Dclippy::semicolon_if_nothing_returned
113116
build --@rules_rust//:clippy_flag=-Dclippy::std_instead_of_core
114117
build --@rules_rust//:clippy_flag=-Dclippy::todo

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,10 @@ pedantic = { level = "warn", priority = -1 }
139139
# Restriction Denies with default priority
140140
alloc-instead-of-core = "deny"
141141
as-underscore = "deny"
142+
await-holding-lock = "deny"
142143
elidable-lifetime-names = "deny"
144+
explicit-into-iter-loop = "deny"
145+
redundant-closure-for-method-calls = "deny"
143146
semicolon-if-nothing-returned = "deny"
144147
std-instead-of-core = "deny"
145148
todo = "deny"

nativelink-store/src/ref_store.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl RefStore {
8888
.err_tip(|| "Store manager is gone")?;
8989
if let Some(store) = store_manager.get_store(&self.name) {
9090
let remove_callbacks = self.remove_callbacks.lock().clone();
91-
for callback in remove_callbacks.into_iter() {
91+
for callback in remove_callbacks {
9292
store.register_remove_callback(callback)?;
9393
}
9494
unsafe {

nativelink-util/src/evicting_map.rs

Lines changed: 75 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -383,49 +383,51 @@ where
383383
// to be able to borrow a `Q`.
384384
R: Borrow<Q> + Send,
385385
{
386-
let mut state = self.state.lock();
386+
let (mut callbacks, data_to_unref) = {
387+
let mut state = self.state.lock();
387388

388-
let lru_len = state.lru.len();
389-
let mut data_to_unref = Vec::new();
390-
let mut removal_futures = Vec::new();
391-
for (key, result) in keys.into_iter().zip(results.iter_mut()) {
392-
let maybe_entry = if peek {
393-
state.lru.peek_mut(key.borrow())
394-
} else {
395-
state.lru.get_mut(key.borrow())
396-
};
397-
match maybe_entry {
398-
Some(entry) => {
399-
// Note: We need to check eviction because the item might be expired
400-
// based on the current time. In such case, we remove the item while
401-
// we are here.
402-
if self.should_evict(lru_len, entry, 0, u64::MAX) {
403-
*result = None;
404-
if let Some((key, eviction_item)) = state.lru.pop_entry(key.borrow()) {
405-
info!(?key, "Item expired, evicting");
406-
let (data, futures) = state.remove(key.borrow(), &eviction_item, false);
407-
// Store data for later unref - we can't drop state here as we're still iterating
408-
data_to_unref.push(data);
409-
removal_futures.extend(futures.into_iter());
389+
let lru_len = state.lru.len();
390+
let mut data_to_unref = Vec::new();
391+
let mut removal_futures = Vec::new();
392+
for (key, result) in keys.into_iter().zip(results.iter_mut()) {
393+
let maybe_entry = if peek {
394+
state.lru.peek_mut(key.borrow())
395+
} else {
396+
state.lru.get_mut(key.borrow())
397+
};
398+
match maybe_entry {
399+
Some(entry) => {
400+
// Note: We need to check eviction because the item might be expired
401+
// based on the current time. In such case, we remove the item while
402+
// we are here.
403+
if self.should_evict(lru_len, entry, 0, u64::MAX) {
404+
*result = None;
405+
if let Some((key, eviction_item)) = state.lru.pop_entry(key.borrow()) {
406+
info!(?key, "Item expired, evicting");
407+
let (data, futures) =
408+
state.remove(key.borrow(), &eviction_item, false);
409+
// Store data for later unref - we can't drop state here as we're still iterating
410+
data_to_unref.push(data);
411+
removal_futures.extend(futures.into_iter());
412+
}
413+
} else {
414+
if !peek {
415+
entry.seconds_since_anchor =
416+
self.anchor_time.elapsed().as_secs() as i32;
417+
}
418+
*result = Some(entry.data.len());
410419
}
411-
} else {
412-
if !peek {
413-
entry.seconds_since_anchor =
414-
self.anchor_time.elapsed().as_secs() as i32;
415-
}
416-
*result = Some(entry.data.len());
417420
}
421+
None => *result = None,
418422
}
419-
None => *result = None,
420423
}
421-
}
424+
let callbacks: FuturesUnordered<_> = removal_futures.into_iter().collect();
425+
(callbacks, data_to_unref)
426+
};
422427

423-
// Drop the state and perform the async callbacks.
424-
drop(state);
425-
let mut callbacks: FuturesUnordered<_> = removal_futures.into_iter().collect();
426428
while callbacks.next().await.is_some() {}
427429
let mut callbacks: FuturesUnordered<_> =
428-
data_to_unref.iter().map(|item| item.unref()).collect();
430+
data_to_unref.iter().map(LenEntry::unref).collect();
429431
while callbacks.next().await.is_some() {}
430432
}
431433

@@ -455,7 +457,7 @@ where
455457
let mut callbacks: FuturesUnordered<_> = removal_futures.into_iter().collect();
456458
while callbacks.next().await.is_some() {}
457459
let mut callbacks: FuturesUnordered<_> =
458-
items_to_unref.iter().map(|item| item.unref()).collect();
460+
items_to_unref.iter().map(LenEntry::unref).collect();
459461
while callbacks.next().await.is_some() {}
460462
}
461463

@@ -567,11 +569,11 @@ where
567569

568570
// Perform eviction after all insertions
569571
let (items_to_unref, futures) = self.evict_items(state);
570-
removal_futures.extend(futures.into_iter());
572+
removal_futures.extend(futures);
571573

572574
// Note: We cannot drop the state lock here since we're borrowing it,
573575
// but the caller will handle unreffing these items after releasing the lock
574-
replaced_items.extend(items_to_unref.into_iter());
576+
replaced_items.extend(items_to_unref);
575577

576578
(replaced_items, removal_futures)
577579
}
@@ -600,7 +602,7 @@ where
600602

601603
// Unref evicted items outside of lock
602604
let mut callbacks: FuturesUnordered<_> =
603-
items_to_unref.iter().map(|item| item.unref()).collect();
605+
items_to_unref.iter().map(LenEntry::unref).collect();
604606
while callbacks.next().await.is_some() {}
605607

606608
// Unref removed item if any
@@ -618,41 +620,44 @@ where
618620
where
619621
F: FnOnce(&T) -> bool + Send,
620622
{
621-
let mut state = self.state.lock();
622-
if let Some(entry) = state.lru.get(key.borrow()) {
623-
if !cond(&entry.data) {
624-
return false;
625-
}
626-
// First perform eviction
627-
let (evicted_items, mut removal_futures) = self.evict_items(&mut state);
628-
629-
// Then try to remove the requested item
630-
let removed_item = if let Some(entry) = state.lru.pop(key.borrow()) {
631-
let (item, more_removal_futures) = state.remove(key, &entry, false);
632-
removal_futures.extend(more_removal_futures.into_iter());
633-
Some(item)
623+
let (evicted_items, mut removal_futures, removed_item) = {
624+
let mut state = self.state.lock();
625+
if let Some(entry) = state.lru.get(key.borrow()) {
626+
if !cond(&entry.data) {
627+
return false;
628+
}
629+
// First perform eviction
630+
let (evicted_items, mut removal_futures) = self.evict_items(&mut state);
631+
632+
// Then try to remove the requested item
633+
let removed_item = if let Some(entry) = state.lru.pop(key.borrow()) {
634+
let (item, more_removal_futures) = state.remove(key, &entry, false);
635+
removal_futures.extend(more_removal_futures.into_iter());
636+
Some(item)
637+
} else {
638+
None
639+
};
640+
641+
let removal_futures: FuturesUnordered<Pin<Box<dyn Future<Output = ()> + Send>>> =
642+
removal_futures.into_iter().collect();
643+
644+
(evicted_items, removal_futures, removed_item)
634645
} else {
635-
None
636-
};
637-
638-
// Drop the lock before unref operations
639-
drop(state);
646+
(vec![], vec![].into_iter().collect(), None)
647+
}
648+
};
640649

641-
let mut removal_futures: FuturesUnordered<_> = removal_futures.into_iter().collect();
642-
while removal_futures.next().await.is_some() {}
650+
while removal_futures.next().await.is_some() {}
643651

644-
// Unref evicted items
645-
let mut callbacks: FuturesUnordered<_> =
646-
evicted_items.iter().map(|item| item.unref()).collect();
647-
while callbacks.next().await.is_some() {}
652+
// Unref evicted items
653+
let mut callbacks: FuturesUnordered<_> =
654+
evicted_items.iter().map(LenEntry::unref).collect();
655+
while callbacks.next().await.is_some() {}
648656

649-
// Unref removed item if any
650-
if let Some(item) = removed_item {
651-
item.unref().await;
652-
true
653-
} else {
654-
false
655-
}
657+
// Unref removed item if any
658+
if let Some(item) = removed_item {
659+
item.unref().await;
660+
true
656661
} else {
657662
false
658663
}

0 commit comments

Comments
 (0)