From 7417a5f9c79f2843b66fc69e8331d4373d65f7d4 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 27 Feb 2025 22:32:31 +0800 Subject: [PATCH] resume one waiter at once --- compiler/rustc_query_system/src/query/job.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index a8c2aa98cd083..2dc8ace22134f 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -378,7 +378,7 @@ where fn remove_cycle( query_map: &QueryMap, jobs: &mut Vec, - wakelist: &mut Vec>, + wakelist: &Mutex>>, ) -> bool { let mut visited = FxHashSet::default(); let mut stack = Vec::new(); @@ -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 { @@ -478,19 +478,19 @@ fn remove_cycle( /// If a query cycle is found it will break the cycle by finding an edge which /// uses a query latch and then resuming that waiter. /// 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. +/// all active queries for cycles. But only one waiter will be resumed at once. pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) { - let mut wakelist = Vec::new(); + static WAKELIST: Mutex>> = Mutex::new(Vec::new()); let mut jobs: Vec = 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 @@ -498,7 +498,7 @@ pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) // 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{:#?}", @@ -506,8 +506,9 @@ 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() { + // Only one waiter is resumed at a time to avoid waking up multiple + // waiters at the same time and causing deadlock due to thread grabbing. + if let Some(waiter) = wake.pop() { waiter.notify(registry); } }