Skip to content

Commit

Permalink
notify only one waiter at a deadlock hanlder
Browse files Browse the repository at this point in the history
  • Loading branch information
root authored and root committed Feb 27, 2025
1 parent 979f18e commit f82d4f9
Showing 1 changed file with 8 additions and 6 deletions.
14 changes: 8 additions & 6 deletions compiler/rustc_query_system/src/query/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ where
fn remove_cycle(
query_map: &QueryMap,
jobs: &mut Vec<QueryJobId>,
wakelist: &mut Vec<Arc<QueryWaiter>>,
wakelist: &Mutex<Vec<Arc<QueryWaiter>>>,
) -> bool {
let mut visited = FxHashSet::default();
let mut stack = Vec::new();
Expand Down Expand Up @@ -466,7 +466,7 @@ fn remove_cycle(
*waiter.cycle.lock() = Some(error);

// Put the waiter on the list of things to resume
wakelist.push(waiter);
wakelist.lock().push(waiter);

true
} else {
Expand All @@ -480,25 +480,27 @@ fn remove_cycle(
/// There may be multiple cycles involved in a deadlock, so this searches
/// all active queries for cycles before finally resuming all the waiters at once.
pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) {
let mut wakelist = Vec::new();
static WAKELIST: Mutex<Vec<Arc<QueryWaiter>>> = Mutex::new(Vec::new());
let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect();

let mut found_cycle = false;

while jobs.len() > 0 {
if remove_cycle(&query_map, &mut jobs, &mut wakelist) {
if remove_cycle(&query_map, &mut jobs, &WAKELIST) {
found_cycle = true;
}
}

let mut wake = WAKELIST.lock();

// Check that a cycle was found. It is possible for a deadlock to occur without
// a query cycle if a query which can be waited on uses Rayon to do multithreading
// internally. Such a query (X) may be executing on 2 threads (A and B) and A may
// wait using Rayon on B. Rayon may then switch to executing another query (Y)
// which in turn will wait on X causing a deadlock. We have a false dependency from
// X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
// only considers the true dependency and won't detect a cycle.
if !found_cycle {
if !found_cycle && wake.is_empty() {
panic!(
"deadlock detected as we're unable to find a query cycle to break\n\
current query map:\n{:#?}",
Expand All @@ -507,7 +509,7 @@ pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry)
}

// FIXME: Ensure this won't cause a deadlock before we return
for waiter in wakelist.into_iter() {
if let Some(waiter) = wake.pop() {
waiter.notify(registry);
}
}
Expand Down

0 comments on commit f82d4f9

Please sign in to comment.