@@ -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
401358pub fn print_query_stack < ' tcx > (
0 commit comments