@@ -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) {
@@ -198,9 +203,10 @@ where
198203
199204 let _poison_guard =
200205 PoisonProvisionalIfPanicking :: new ( self , zalsa, id, memo_ingredient_index) ;
201- let mut active_query = zalsa_local. push_query ( database_key_index, iteration_count) ;
202206
203207 let ( new_value, completed_query) = loop {
208+ let active_query = zalsa_local. push_query ( database_key_index, iteration_count) ;
209+
204210 // Tracked struct ids that existed in the previous revision
205211 // but weren't recreated in the last iteration. It's important that we seed the next
206212 // query with these ids because the query might re-create them as part of the next iteration.
@@ -209,29 +215,32 @@ where
209215 // if they aren't recreated when reaching the final iteration.
210216 active_query. seed_tracked_struct_ids ( & last_stale_tracked_ids) ;
211217
212- let ( mut new_value, mut completed_query ) = Self :: execute_query (
218+ let ( mut new_value, mut active_query ) = Self :: execute_query (
213219 db,
214220 zalsa,
215221 active_query,
216222 last_provisional_memo. or ( opt_old_memo) ,
217223 ) ;
218224
219- // If there are no cycle heads, break out of the loop (`cycle_heads_mut` returns `None` if the cycle head list is empty)
220- let Some ( cycle_heads) = completed_query. revisions . cycle_heads_mut ( ) else {
225+ // Take the cycle heads to not-fight-rust's-borrow-checker.
226+ let mut cycle_heads = active_query. take_cycle_heads ( ) ;
227+
228+ // If there are no cycle heads, break out of the loop.
229+ if cycle_heads. is_empty ( ) {
221230 iteration_count = iteration_count. increment ( ) . unwrap_or_else ( || {
222231 tracing:: warn!( "{database_key_index:?}: execute: too many cycle iterations" ) ;
223232 panic ! ( "{database_key_index:?}: execute: too many cycle iterations" )
224233 } ) ;
234+
235+ let mut completed_query = active_query. pop ( ) ;
225236 completed_query
226237 . revisions
227238 . update_iteration_count_mut ( database_key_index, iteration_count) ;
228239
229240 claim_guard. set_release_mode ( ReleaseMode :: SelfOnly ) ;
230241 break ( new_value, completed_query) ;
231- } ;
242+ }
232243
233- // Take the cycle heads to not-fight-rust's-borrow-checker.
234- let mut cycle_heads = std:: mem:: take ( cycle_heads) ;
235244 let mut missing_heads: SmallVec < [ ( DatabaseKeyIndex , IterationCount ) ; 1 ] > =
236245 SmallVec :: new_const ( ) ;
237246 let mut max_iteration_count = iteration_count;
@@ -262,6 +271,11 @@ where
262271 . provisional_status ( zalsa, head. database_key_index . key_index ( ) )
263272 . expect ( "cycle head memo must have been created during the execution" ) ;
264273
274+ // A query should only ever depend on other heads that are provisional.
275+ // If this invariant is violated, it means that this query participates in a cycle,
276+ // but it wasn't executed in the last iteration of said cycle.
277+ assert ! ( provisional_status. is_provisional( ) ) ;
278+
265279 for nested_head in provisional_status. cycle_heads ( ) {
266280 let nested_as_tuple = (
267281 nested_head. database_key_index ,
@@ -298,6 +312,8 @@ where
298312 claim_guard. set_release_mode ( ReleaseMode :: SelfOnly ) ;
299313 }
300314
315+ let mut completed_query = active_query. pop ( ) ;
316+ * completed_query. revisions . verified_final . get_mut ( ) = false ;
301317 completed_query. revisions . set_cycle_heads ( cycle_heads) ;
302318
303319 iteration_count = iteration_count. increment ( ) . unwrap_or_else ( || {
@@ -378,8 +394,17 @@ where
378394 this_converged = C :: values_equal ( & new_value, last_provisional_value) ;
379395 }
380396 }
397+
398+ let new_cycle_heads = active_query. take_cycle_heads ( ) ;
399+ for head in new_cycle_heads {
400+ if !cycle_heads. contains ( & head. database_key_index ) {
401+ panic ! ( "Cycle recovery function for {database_key_index:?} introduced a cycle, depending on {:?}. This is not allowed." , head. database_key_index) ;
402+ }
403+ }
381404 }
382405
406+ let mut completed_query = active_query. pop ( ) ;
407+
383408 if let Some ( outer_cycle) = outer_cycle {
384409 tracing:: info!(
385410 "Detected nested cycle {database_key_index:?}, iterate it as part of the outer cycle {outer_cycle:?}"
@@ -390,6 +415,7 @@ where
390415 completed_query
391416 . revisions
392417 . set_cycle_converged ( this_converged) ;
418+ * completed_query. revisions . verified_final . get_mut ( ) = false ;
393419
394420 // Transfer ownership of this query to the outer cycle, so that it can claim it
395421 // and other threads don't compete for the same lock.
@@ -428,9 +454,9 @@ where
428454 }
429455
430456 * completed_query. revisions . verified_final . get_mut ( ) = true ;
431-
432457 break ( new_value, completed_query) ;
433458 }
459+ * completed_query. revisions . verified_final . get_mut ( ) = false ;
434460
435461 // The fixpoint iteration hasn't converged. Iterate again...
436462 iteration_count = iteration_count. increment ( ) . unwrap_or_else ( || {
@@ -484,7 +510,6 @@ where
484510 last_provisional_memo = Some ( new_memo) ;
485511
486512 last_stale_tracked_ids = completed_query. stale_tracked_structs ;
487- active_query = zalsa_local. push_query ( database_key_index, iteration_count) ;
488513
489514 continue ;
490515 } ;
@@ -503,7 +528,7 @@ where
503528 zalsa : & ' db Zalsa ,
504529 active_query : ActiveQueryGuard < ' db > ,
505530 opt_old_memo : Option < & Memo < ' db , C > > ,
506- ) -> ( C :: Output < ' db > , CompletedQuery ) {
531+ ) -> ( C :: Output < ' db > , ActiveQueryGuard < ' db > ) {
507532 if let Some ( old_memo) = opt_old_memo {
508533 // If we already executed this query once, then use the tracked-struct ids from the
509534 // previous execution as the starting point for the new one.
@@ -528,7 +553,7 @@ where
528553 C :: id_to_input ( zalsa, active_query. database_key_index . key_index ( ) ) ,
529554 ) ;
530555
531- ( new_value, active_query. pop ( ) )
556+ ( new_value, active_query)
532557 }
533558}
534559
0 commit comments