Skip to content

Commit c5aa313

Browse files
authored
Unrolled build for #154973
Rollup merge of #154973 - Zoxc:cycle-single, r=petrochenkov Break a single query cycle in the deadlock handler This simplifies the query cycle handling by only breaking a single query cycle each time the deadlock handler is called.
2 parents 7659cec + 690d193 commit c5aa313

3 files changed

Lines changed: 27 additions & 70 deletions

File tree

compiler/rustc_interface/src/util.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals<
184184

185185
use rustc_data_structures::defer;
186186
use rustc_middle::ty::tls;
187-
use rustc_query_impl::break_query_cycles;
187+
use rustc_query_impl::break_query_cycle;
188188

189189
let thread_stack_size = init_stack_size(thread_builder_diag);
190190

@@ -260,7 +260,7 @@ internal compiler error: query cycle handler thread panicked, aborting process";
260260
)
261261
},
262262
);
263-
break_query_cycles(job_map, &registry);
263+
break_query_cycle(job_map, &registry);
264264
})
265265
})
266266
});

compiler/rustc_query_impl/src/job.rs

Lines changed: 24 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -303,29 +303,17 @@ fn process_cycle<'tcx>(job_map: &QueryJobMap<'tcx>, stack: Vec<(Span, QueryJobId
303303
}
304304
}
305305

306-
/// Looks for a query cycle using the last query in `jobs`.
307-
/// If a cycle is found, all queries in the cycle is removed from `jobs` and
308-
/// the function return true.
309-
/// If a cycle was not found, the starting query is removed from `jobs` and
310-
/// the function returns false.
311-
fn remove_cycle<'tcx>(
306+
/// Looks for a query cycle starting at `query`.
307+
/// Returns a waiter to resume if a cycle is found.
308+
fn find_and_process_cycle<'tcx>(
312309
job_map: &QueryJobMap<'tcx>,
313-
jobs: &mut Vec<QueryJobId>,
314-
wakelist: &mut Vec<Arc<QueryWaiter<'tcx>>>,
315-
) -> bool {
310+
query: QueryJobId,
311+
) -> Option<Arc<QueryWaiter<'tcx>>> {
316312
let mut visited = FxHashSet::default();
317313
let mut stack = Vec::new();
318-
// Look for a cycle starting with the last query in `jobs`
319314
if let ControlFlow::Break(resumable) =
320-
find_cycle(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited)
315+
find_cycle(job_map, query, DUMMY_SP, &mut stack, &mut visited)
321316
{
322-
// Remove the queries in our cycle from the list of jobs to look at
323-
for r in &stack {
324-
if let Some(pos) = jobs.iter().position(|j| j == &r.1) {
325-
jobs.remove(pos);
326-
}
327-
}
328-
329317
// Create the cycle error
330318
let error = process_cycle(job_map, stack);
331319

@@ -340,62 +328,31 @@ fn remove_cycle<'tcx>(
340328
*waiter.cycle.lock() = Some(error);
341329

342330
// Put the waiter on the list of things to resume
343-
wakelist.push(waiter);
344-
345-
true
331+
Some(waiter)
346332
} else {
347-
false
333+
None
348334
}
349335
}
350336

351337
/// Detects query cycles by using depth first search over all active query jobs.
352338
/// If a query cycle is found it will break the cycle by finding an edge which
353339
/// uses a query latch and then resuming that waiter.
354-
/// There may be multiple cycles involved in a deadlock, so this searches
355-
/// all active queries for cycles before finally resuming all the waiters at once.
356-
pub fn break_query_cycles<'tcx>(
357-
job_map: QueryJobMap<'tcx>,
358-
registry: &rustc_thread_pool::Registry,
359-
) {
360-
let mut wakelist = Vec::new();
361-
// It is OK per the comments:
362-
// - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932
363-
// - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392
364-
#[allow(rustc::potential_query_instability)]
365-
let mut jobs: Vec<QueryJobId> = job_map.map.keys().copied().collect();
366-
367-
let mut found_cycle = false;
368-
369-
while jobs.len() > 0 {
370-
if remove_cycle(&job_map, &mut jobs, &mut wakelist) {
371-
found_cycle = true;
372-
}
373-
}
374-
375-
// Check that a cycle was found. It is possible for a deadlock to occur without
376-
// a query cycle if a query which can be waited on uses Rayon to do multithreading
377-
// internally. Such a query (X) may be executing on 2 threads (A and B) and A may
378-
// wait using Rayon on B. Rayon may then switch to executing another query (Y)
379-
// which in turn will wait on X causing a deadlock. We have a false dependency from
380-
// X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
381-
// only considers the true dependency and won't detect a cycle.
382-
if !found_cycle {
383-
panic!(
384-
"deadlock detected as we're unable to find a query cycle to break\n\
385-
current query map:\n{job_map:#?}",
386-
);
387-
}
388-
389-
// Mark all the thread we're about to wake up as unblocked. This needs to be done before
390-
// we wake the threads up as otherwise Rayon could detect a deadlock if a thread we
391-
// resumed fell asleep and this thread had yet to mark the remaining threads as unblocked.
392-
for _ in 0..wakelist.len() {
393-
rustc_thread_pool::mark_unblocked(registry);
394-
}
395-
396-
for waiter in wakelist.into_iter() {
397-
waiter.condvar.notify_one();
398-
}
340+
///
341+
/// There may be multiple cycles involved in a deadlock, but this only breaks one at a time so
342+
/// there will be multiple rounds through the deadlock handler if multiple cycles are present.
343+
#[allow(rustc::potential_query_instability)]
344+
pub fn break_query_cycle<'tcx>(job_map: QueryJobMap<'tcx>, registry: &rustc_thread_pool::Registry) {
345+
// Look for a cycle starting at each query job
346+
let waiter = job_map
347+
.map
348+
.keys()
349+
.find_map(|query| find_and_process_cycle(&job_map, *query))
350+
.expect("unable to find a query cycle");
351+
352+
// Mark the thread we're about to wake up as unblocked.
353+
rustc_thread_pool::mark_unblocked(registry);
354+
355+
assert!(waiter.condvar.notify_one(), "unable to wake the waiter");
399356
}
400357

401358
pub fn print_query_stack<'tcx>(

compiler/rustc_query_impl/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_middle::ty::TyCtxt;
1717

1818
pub use crate::dep_kind_vtables::make_dep_kind_vtables;
1919
pub use crate::execution::{CollectActiveJobsKind, collect_active_query_jobs};
20-
pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack};
20+
pub use crate::job::{QueryJobMap, break_query_cycle, print_query_stack};
2121

2222
mod dep_kind_vtables;
2323
mod error;

0 commit comments

Comments
 (0)