@@ -2,7 +2,7 @@ use rustc_hash::FxHashMap;
22
33#[ cfg( feature = "accumulator" ) ]
44use crate :: accumulator:: accumulated_map:: InputAccumulatedValues ;
5- use crate :: cycle:: { CycleRecoveryStrategy , ProvisionalStatus } ;
5+ use crate :: cycle:: { CycleHeads , CycleRecoveryStrategy , ProvisionalStatus } ;
66use crate :: function:: memo:: { Memo , TryClaimCycleHeadsIter , TryClaimHeadsResult } ;
77use crate :: function:: sync:: ClaimResult ;
88use crate :: function:: { Configuration , IngredientImpl } ;
@@ -453,6 +453,48 @@ where
453453 database_key_index : DatabaseKeyIndex ,
454454 memo : & Memo < ' _ , C > ,
455455 ) -> bool {
456+ #[ cold]
457+ #[ inline( never) ]
458+ fn validate_same_iteration_cold (
459+ zalsa : & Zalsa ,
460+ zalsa_local : & ZalsaLocal ,
461+ cycle_heads : & CycleHeads ,
462+ verified_at : Revision ,
463+ ) -> bool {
464+ let mut cycle_heads_iter = TryClaimCycleHeadsIter :: new ( zalsa, zalsa_local, cycle_heads) ;
465+
466+ while let Some ( cycle_head) = cycle_heads_iter. next ( ) {
467+ match cycle_head {
468+ TryClaimHeadsResult :: Cycle {
469+ head_iteration_count,
470+ current_iteration_count,
471+ verified_at : head_verified_at,
472+ } => {
473+ if head_verified_at != verified_at {
474+ return false ;
475+ }
476+
477+ if head_iteration_count != current_iteration_count {
478+ return false ;
479+ }
480+ }
481+ TryClaimHeadsResult :: Available ( available_cycle_head) => {
482+ // Check the cycle heads recursively
483+ if available_cycle_head. is_nested ( zalsa) {
484+ available_cycle_head. queue_cycle_heads ( & mut cycle_heads_iter) ;
485+ } else {
486+ return false ;
487+ }
488+ }
489+ TryClaimHeadsResult :: Finalized | TryClaimHeadsResult :: Running ( _) => {
490+ return false ;
491+ }
492+ }
493+ }
494+
495+ true
496+ }
497+
456498 crate :: tracing:: trace!(
457499 "{database_key_index:?}: validate_same_iteration(memo = {memo:#?})" ,
458500 memo = memo. tracing_debug( )
@@ -472,111 +514,7 @@ where
472514 return false ;
473515 }
474516
475- let mut cycle_heads_iter = TryClaimCycleHeadsIter :: new ( zalsa, zalsa_local, cycle_heads) ;
476-
477- while let Some ( cycle_head) = cycle_heads_iter. next ( ) {
478- match cycle_head {
479- TryClaimHeadsResult :: Cycle {
480- head_iteration_count,
481- current_iteration_count,
482- verified_at : head_verified_at,
483- } => {
484- if head_verified_at != verified_at {
485- return false ;
486- }
487-
488- if head_iteration_count != current_iteration_count {
489- return false ;
490- }
491- }
492- TryClaimHeadsResult :: Available ( available_cycle_head) => {
493- // Check the cycle heads recursively
494- if available_cycle_head. is_nested ( zalsa) {
495- available_cycle_head. queue_cycle_heads ( & mut cycle_heads_iter) ;
496- } else {
497- return false ;
498- }
499- }
500- TryClaimHeadsResult :: Finalized | TryClaimHeadsResult :: Running ( _) => {
501- return false ;
502- }
503- }
504-
505- // // If the cycle head isn't on our stack because:
506- // //
507- // // * another thread holds the lock on the cycle head (but it waits for the current query to complete)
508- // // * we're in `maybe_changed_after` because `maybe_changed_after` doesn't modify the cycle stack
509- // //
510- // // check if the latest memo has the same iteration count.
511-
512- // // However, we've to be careful to skip over fixpoint initial values:
513- // // If the head is the memo we're trying to validate, always return `None`
514- // // to force a re-execution of the query. This is necessary because the query
515- // // has obviously not completed its iteration yet.
516- // //
517- // // This should be rare but the `cycle_panic` test fails on some platforms (mainly GitHub actions)
518- // // without this check. What happens there is that:
519- // //
520- // // * query a blocks on query b
521- // // * query b tries to claim a, fails to do so and inserts the fixpoint initial value
522- // // * query b completes and has `a` as head. It returns its query result Salsa blocks query b from
523- // // exiting inside `block_on` (or the thread would complete before the cycle iteration is complete)
524- // // * query a resumes but panics because of the fixpoint iteration function
525- // // * query b resumes. It rexecutes its own query which then tries to fetch a (which depends on itself because it's a fixpoint initial value).
526- // // Without this check, `validate_same_iteration` would return `true` because the latest memo for `a` is the fixpoint initial value.
527- // // But it should return `false` so that query b's thread re-executes `a` (which then also causes the panic).
528- // //
529- // // That's why we always return `None` if the cycle head is the same as the current database key index.
530- // if cycle_head.database_key_index == database_key_index {
531- // return false;
532- // }
533-
534- // let wait_result = ingredient.wait_for(zalsa, cycle_head.database_key_index.key_index());
535-
536- // let provisional_status = match wait_result {
537- // WaitForResult::Running(_) => {
538- // // This Memo is guaranteed to be outdated because another thread
539- // // is computing a new value right now
540- // return None;
541- // }
542- // WaitForResult::Available(_claim_guard) => {
543- // // Nested cycles are released as soon as their query completes
544- // // and the outer queries are part of their `cycle_heads`.
545-
546- // let provisional_status = ingredient
547- // .provisional_status(zalsa, cycle_head.database_key_index.key_index())?;
548-
549- // if !provisional_status.nested() {
550- // return None;
551- // }
552-
553- // let cycle_heads =
554- // ingredient.cycle_heads(zalsa, cycle_head.database_key_index.key_index());
555-
556- // // This doesn't work, unless we need the same check in blocks-on etc.
557- // if !cycle_heads.contains(&database_key_index) {
558- // return None;
559- // }
560-
561- // provisional_status
562- // }
563- // WaitForResult::Cycle => {
564- // // The head is hold by the current thread or another thread waiting on the
565- // // result of this thread.
566- // ingredient
567- // .provisional_status(zalsa, cycle_head.database_key_index.key_index())?
568- // }
569- // };
570-
571- // if provisional_status.verified_at() == Some(verified_at) {
572- // provisional_status.iteration()
573- // } else {
574- // None
575- // }
576- //
577- }
578-
579- true
517+ validate_same_iteration_cold ( zalsa, zalsa_local, cycle_heads, verified_at)
580518 }
581519
582520 /// VerifyResult::Unchanged if the memo's value and `changed_at` time is up-to-date in the
0 commit comments