@@ -56,20 +56,25 @@ where
5656 } ) ;
5757
5858 let ( new_value, mut completed_query) = match C :: CYCLE_STRATEGY {
59- CycleRecoveryStrategy :: Panic => Self :: execute_query (
60- db,
61- zalsa,
62- zalsa_local. push_query ( database_key_index, IterationCount :: initial ( ) ) ,
63- opt_old_memo,
64- ) ,
59+ CycleRecoveryStrategy :: Panic => {
60+ let ( new_value, active_query) = Self :: execute_query (
61+ db,
62+ zalsa,
63+ zalsa_local. push_query ( database_key_index, IterationCount :: initial ( ) ) ,
64+ opt_old_memo,
65+ ) ;
66+ ( new_value, active_query. pop ( ) )
67+ }
6568 CycleRecoveryStrategy :: FallbackImmediate => {
66- let ( mut new_value, mut completed_query ) = Self :: execute_query (
69+ let ( mut new_value, active_query ) = Self :: execute_query (
6770 db,
6871 zalsa,
6972 zalsa_local. push_query ( database_key_index, IterationCount :: initial ( ) ) ,
7073 opt_old_memo,
7174 ) ;
7275
76+ let mut completed_query = active_query. pop ( ) ;
77+
7378 if let Some ( cycle_heads) = completed_query. revisions . cycle_heads_mut ( ) {
7479 // Did the new result we got depend on our own provisional value, in a cycle?
7580 if cycle_heads. contains ( & database_key_index) {
@@ -196,9 +201,10 @@ where
196201
197202 let _poison_guard =
198203 PoisonProvisionalIfPanicking :: new ( self , zalsa, id, memo_ingredient_index) ;
199- let mut active_query = zalsa_local. push_query ( database_key_index, iteration_count) ;
200204
201205 let ( new_value, completed_query) = loop {
206+ let active_query = zalsa_local. push_query ( database_key_index, iteration_count) ;
207+
202208 // Tracked struct ids that existed in the previous revision
203209 // but weren't recreated in the last iteration. It's important that we seed the next
204210 // query with these ids because the query might re-create them as part of the next iteration.
@@ -207,21 +213,22 @@ where
207213 // if they aren't recreated when reaching the final iteration.
208214 active_query. seed_tracked_struct_ids ( & last_stale_tracked_ids) ;
209215
210- let ( mut new_value, mut completed_query ) = Self :: execute_query (
216+ let ( mut new_value, mut active_query ) = Self :: execute_query (
211217 db,
212218 zalsa,
213219 active_query,
214220 last_provisional_memo. or ( opt_old_memo) ,
215221 ) ;
216222
217- // If there are no cycle heads, break out of the loop (`cycle_heads_mut` returns `None` if the cycle head list is empty)
218- let Some ( cycle_heads) = completed_query. revisions . cycle_heads_mut ( ) else {
223+ // Take the cycle heads to not-fight-rust's-borrow-checker.
224+ let mut cycle_heads = active_query. take_cycle_heads ( ) ;
225+
226+ // If there are no cycle heads, break out of the loop.
227+ if cycle_heads. is_empty ( ) {
219228 claim_guard. set_release_mode ( ReleaseMode :: SelfOnly ) ;
220- break ( new_value, completed_query ) ;
221- } ;
229+ break ( new_value, active_query . pop ( ) ) ;
230+ }
222231
223- // Take the cycle heads to not-fight-rust's-borrow-checker.
224- let mut cycle_heads = std:: mem:: take ( cycle_heads) ;
225232 let mut missing_heads: SmallVec < [ ( DatabaseKeyIndex , IterationCount ) ; 1 ] > =
226233 SmallVec :: new_const ( ) ;
227234 let mut max_iteration_count = iteration_count;
@@ -252,6 +259,10 @@ where
252259 . provisional_status ( zalsa, head. database_key_index . key_index ( ) )
253260 . expect ( "cycle head memo must have been created during the execution" ) ;
254261
262+ // A cycle head isn't allowed to be final because that would mean that this query
263+ // dependent on the initial value (or last provisional)
264+ assert ! ( provisional_status. is_provisional( ) ) ;
265+
255266 for nested_head in provisional_status. cycle_heads ( ) {
256267 let nested_as_tuple = (
257268 nested_head. database_key_index ,
@@ -288,6 +299,8 @@ where
288299 claim_guard. set_release_mode ( ReleaseMode :: SelfOnly ) ;
289300 }
290301
302+ let mut completed_query = active_query. pop ( ) ;
303+ * completed_query. revisions . verified_final . get_mut ( ) = false ;
291304 completed_query. revisions . set_cycle_heads ( cycle_heads) ;
292305 break ( new_value, completed_query) ;
293306 }
@@ -359,8 +372,17 @@ where
359372 this_converged = C :: values_equal ( & new_value, last_provisional_value) ;
360373 }
361374 }
375+
376+ let new_cycle_heads = active_query. take_cycle_heads ( ) ;
377+ for head in new_cycle_heads {
378+ if !cycle_heads. contains ( & head. database_key_index ) {
379+ panic ! ( "Cycle recovery function for {database_key_index:?} introduced a cycle, depending on {:?}. This is not allowed." , head. database_key_index) ;
380+ }
381+ }
362382 }
363383
384+ let mut completed_query = active_query. pop ( ) ;
385+
364386 if let Some ( outer_cycle) = outer_cycle {
365387 tracing:: info!(
366388 "Detected nested cycle {database_key_index:?}, iterate it as part of the outer cycle {outer_cycle:?}"
@@ -371,6 +393,7 @@ where
371393 completed_query
372394 . revisions
373395 . set_cycle_converged ( this_converged) ;
396+ * completed_query. revisions . verified_final . get_mut ( ) = false ;
374397
375398 // Transfer ownership of this query to the outer cycle, so that it can claim it
376399 // and other threads don't compete for the same lock.
@@ -409,9 +432,9 @@ where
409432 }
410433
411434 * completed_query. revisions . verified_final . get_mut ( ) = true ;
412-
413435 break ( new_value, completed_query) ;
414436 }
437+ * completed_query. revisions . verified_final . get_mut ( ) = false ;
415438
416439 // The fixpoint iteration hasn't converged. Iterate again...
417440 iteration_count = iteration_count. increment ( ) . unwrap_or_else ( || {
@@ -465,7 +488,6 @@ where
465488 last_provisional_memo = Some ( new_memo) ;
466489
467490 last_stale_tracked_ids = completed_query. stale_tracked_structs ;
468- active_query = zalsa_local. push_query ( database_key_index, iteration_count) ;
469491
470492 continue ;
471493 } ;
@@ -484,7 +506,7 @@ where
484506 zalsa : & ' db Zalsa ,
485507 active_query : ActiveQueryGuard < ' db > ,
486508 opt_old_memo : Option < & Memo < ' db , C > > ,
487- ) -> ( C :: Output < ' db > , CompletedQuery ) {
509+ ) -> ( C :: Output < ' db > , ActiveQueryGuard < ' db > ) {
488510 if let Some ( old_memo) = opt_old_memo {
489511 // If we already executed this query once, then use the tracked-struct ids from the
490512 // previous execution as the starting point for the new one.
@@ -509,7 +531,7 @@ where
509531 C :: id_to_input ( zalsa, active_query. database_key_index . key_index ( ) ) ,
510532 ) ;
511533
512- ( new_value, active_query. pop ( ) )
534+ ( new_value, active_query)
513535 }
514536}
515537
0 commit comments