@@ -102,6 +102,14 @@ pub enum CycleRecoveryStrategy {
102102pub struct CycleHead {
103103 pub ( crate ) database_key_index : DatabaseKeyIndex ,
104104 pub ( crate ) iteration_count : AtomicIterationCount ,
105+
106+ /// Marks a cycle head as removed within its `CycleHeads` container.
107+ ///
108+ /// Cycle heads are marked as removed when the memo from the last iteration (a provisional memo)
109+ /// is used as the initial value for the next iteration. It's necessary to remove all but its own
110+ /// head from the `CycleHeads` container, because the query might now depend on fewer cycles
111+ /// (in case of conditional dependencies). However, we can't actually remove the cycle head
112+ /// within `fetch_cold_cycle` because we only have a readonly memo. That's what `removed` is used for.
105113 #[ cfg_attr( feature = "persistence" , serde( skip) ) ]
106114 removed : AtomicBool ,
107115}
@@ -130,6 +138,11 @@ impl IterationCount {
130138 self . 0 == 0
131139 }
132140
141+ /// Iteration count reserved for panicked cycles.
142+ ///
143+ /// Using a special iteration count ensures that `validate_same_iteration` and `validate_provisional`
144+ /// return `false` for queries depending on this panicked cycle, because the iteration count is guaranteed
145+ /// to be different (which isn't guaranteed if the panicked memo uses [`Self::initial`]).
133146 pub ( crate ) const fn panicked ( ) -> Self {
134147 Self ( u8:: MAX )
135148 }
@@ -150,7 +163,7 @@ impl IterationCount {
150163
151164impl std:: fmt:: Display for IterationCount {
152165 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
153- write ! ( f , "iteration={}" , self . 0 )
166+ self . 0 . fmt ( f )
154167 }
155168}
156169
@@ -162,6 +175,10 @@ impl AtomicIterationCount {
162175 IterationCount ( self . 0 . load ( Ordering :: Relaxed ) )
163176 }
164177
178+ pub ( crate ) fn load_mut ( & mut self ) -> IterationCount {
179+ IterationCount ( * self . 0 . get_mut ( ) )
180+ }
181+
165182 pub ( crate ) fn store ( & self , value : IterationCount ) {
166183 self . 0 . store ( value. 0 , Ordering :: Release ) ;
167184 }
@@ -214,10 +231,13 @@ impl CycleHeads {
214231 self . 0 . is_empty ( )
215232 }
216233
217- pub ( crate ) fn initial ( database_key_index : DatabaseKeyIndex ) -> Self {
234+ pub ( crate ) fn initial (
235+ database_key_index : DatabaseKeyIndex ,
236+ iteration_count : IterationCount ,
237+ ) -> Self {
218238 Self ( thin_vec ! [ CycleHead {
219239 database_key_index,
220- iteration_count: IterationCount :: initial ( ) . into( ) ,
240+ iteration_count: iteration_count . into( ) ,
221241 removed: false . into( )
222242 } ] )
223243 }
@@ -233,17 +253,24 @@ impl CycleHeads {
233253 . any ( |head| head. database_key_index == * value && !head. removed . load ( Ordering :: Relaxed ) )
234254 }
235255
236- pub ( crate ) fn clear_except ( & self , except : DatabaseKeyIndex ) {
256+ /// Removes all cycle heads except `except` by marking them as removed.
257+ ///
258+ /// Note that the heads aren't actually removed. They're only marked as removed and will be
259+ /// skipped when iterating. This is because we might not have a mutable reference.
260+ pub ( crate ) fn remove_all_except ( & self , except : DatabaseKeyIndex ) {
237261 for head in self . 0 . iter ( ) {
238262 if head. database_key_index == except {
239263 continue ;
240264 }
241265
242- // TODO: verify ordering
243266 head. removed . store ( true , Ordering :: Release ) ;
244267 }
245268 }
246269
270+ /// Updates the iteration count for the head `cycle_head_index` to `new_iteration_count`.
271+ ///
272+ /// Unlike [`update_iteration_count`], this method takes a `&mut self` reference. It should
273+ /// be preferred if possible, as it avoids atomic operations.
247274 pub ( crate ) fn update_iteration_count_mut (
248275 & mut self ,
249276 cycle_head_index : DatabaseKeyIndex ,
@@ -258,6 +285,9 @@ impl CycleHeads {
258285 }
259286 }
260287
288+ /// Updates the iteration count for the head `cycle_head_index` to `new_iteration_count`.
289+ ///
290+ /// Unlike [`update_iteration_count_mut`], this method takes a `&self` reference.
261291 pub ( crate ) fn update_iteration_count (
262292 & self ,
263293 cycle_head_index : DatabaseKeyIndex ,
@@ -282,6 +312,8 @@ impl CycleHeads {
282312 }
283313
284314 pub ( crate ) fn insert ( & mut self , head : & CycleHead ) -> bool {
315+ debug_assert ! ( !head. removed. load( Ordering :: Relaxed ) ) ;
316+
285317 if let Some ( existing) = self
286318 . 0
287319 . iter_mut ( )
@@ -294,12 +326,9 @@ impl CycleHeads {
294326
295327 true
296328 } else {
297- let existing_count = existing. iteration_count . load ( ) ;
329+ let existing_count = existing. iteration_count . load_mut ( ) ;
298330 let head_count = head. iteration_count . load ( ) ;
299331
300- // It's now possible that a query can depend on different iteration counts of the same query
301- // This because some queries (inner) read the provisional value of the last iteration
302- // while outer queries read the value from the last iteration (which is i+1 if the head didn't converge).
303332 assert_eq ! (
304333 existing_count, head_count,
305334 "Can't merge cycle heads {:?} with different iteration counts ({existing_count:?}, {head_count:?})" ,
@@ -309,7 +338,6 @@ impl CycleHeads {
309338 false
310339 }
311340 } else {
312- debug_assert ! ( !head. removed. load( Ordering :: Relaxed ) ) ;
313341 self . 0 . push ( head. clone ( ) ) ;
314342 true
315343 }
0 commit comments