@@ -102,10 +102,31 @@ 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}
108116
117+ impl CycleHead {
118+ pub const fn new (
119+ database_key_index : DatabaseKeyIndex ,
120+ iteration_count : IterationCount ,
121+ ) -> Self {
122+ Self {
123+ database_key_index,
124+ iteration_count : AtomicIterationCount ( AtomicU8 :: new ( iteration_count. 0 ) ) ,
125+ removed : AtomicBool :: new ( false ) ,
126+ }
127+ }
128+ }
129+
109130impl Clone for CycleHead {
110131 fn clone ( & self ) -> Self {
111132 Self {
@@ -130,10 +151,19 @@ impl IterationCount {
130151 self . 0 == 0
131152 }
132153
154+ /// Iteration count reserved for panicked cycles.
155+ ///
156+ /// Using a special iteration count ensures that `validate_same_iteration` and `validate_provisional`
157+ /// return `false` for queries depending on this panicked cycle, because the iteration count is guaranteed
158+ /// to be different (which isn't guaranteed if the panicked memo uses [`Self::initial`]).
133159 pub ( crate ) const fn panicked ( ) -> Self {
134160 Self ( u8:: MAX )
135161 }
136162
163+ pub ( crate ) const fn is_panicked ( self ) -> bool {
164+ self . 0 == u8:: MAX
165+ }
166+
137167 pub ( crate ) const fn increment ( self ) -> Option < Self > {
138168 let next = Self ( self . 0 + 1 ) ;
139169 if next. 0 <= MAX_ITERATIONS . 0 {
@@ -150,7 +180,7 @@ impl IterationCount {
150180
151181impl std:: fmt:: Display for IterationCount {
152182 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
153- write ! ( f , "iteration={}" , self . 0 )
183+ self . 0 . fmt ( f )
154184 }
155185}
156186
@@ -162,6 +192,10 @@ impl AtomicIterationCount {
162192 IterationCount ( self . 0 . load ( Ordering :: Relaxed ) )
163193 }
164194
195+ pub ( crate ) fn load_mut ( & mut self ) -> IterationCount {
196+ IterationCount ( * self . 0 . get_mut ( ) )
197+ }
198+
165199 pub ( crate ) fn store ( & self , value : IterationCount ) {
166200 self . 0 . store ( value. 0 , Ordering :: Release ) ;
167201 }
@@ -214,10 +248,13 @@ impl CycleHeads {
214248 self . 0 . is_empty ( )
215249 }
216250
217- pub ( crate ) fn initial ( database_key_index : DatabaseKeyIndex ) -> Self {
251+ pub ( crate ) fn initial (
252+ database_key_index : DatabaseKeyIndex ,
253+ iteration_count : IterationCount ,
254+ ) -> Self {
218255 Self ( thin_vec ! [ CycleHead {
219256 database_key_index,
220- iteration_count: IterationCount :: initial ( ) . into( ) ,
257+ iteration_count: iteration_count . into( ) ,
221258 removed: false . into( )
222259 } ] )
223260 }
@@ -228,22 +265,35 @@ impl CycleHeads {
228265 }
229266 }
230267
268+ /// Iterates over all cycle heads that aren't equal to `own`.
269+ pub ( crate ) fn iter_not_eq ( & self , own : DatabaseKeyIndex ) -> impl Iterator < Item = & CycleHead > {
270+ self . iter ( )
271+ . filter ( move |head| head. database_key_index != own)
272+ }
273+
231274 pub ( crate ) fn contains ( & self , value : & DatabaseKeyIndex ) -> bool {
232275 self . into_iter ( )
233276 . any ( |head| head. database_key_index == * value && !head. removed . load ( Ordering :: Relaxed ) )
234277 }
235278
236- pub ( crate ) fn clear_except ( & self , except : DatabaseKeyIndex ) {
279+ /// Removes all cycle heads except `except` by marking them as removed.
280+ ///
281+ /// Note that the heads aren't actually removed. They're only marked as removed and will be
282+ /// skipped when iterating. This is because we might not have a mutable reference.
283+ pub ( crate ) fn remove_all_except ( & self , except : DatabaseKeyIndex ) {
237284 for head in self . 0 . iter ( ) {
238285 if head. database_key_index == except {
239286 continue ;
240287 }
241288
242- // TODO: verify ordering
243289 head. removed . store ( true , Ordering :: Release ) ;
244290 }
245291 }
246292
293+ /// Updates the iteration count for the head `cycle_head_index` to `new_iteration_count`.
294+ ///
295+ /// Unlike [`update_iteration_count`], this method takes a `&mut self` reference. It should
296+ /// be preferred if possible, as it avoids atomic operations.
247297 pub ( crate ) fn update_iteration_count_mut (
248298 & mut self ,
249299 cycle_head_index : DatabaseKeyIndex ,
@@ -258,6 +308,9 @@ impl CycleHeads {
258308 }
259309 }
260310
311+ /// Updates the iteration count for the head `cycle_head_index` to `new_iteration_count`.
312+ ///
313+ /// Unlike [`update_iteration_count_mut`], this method takes a `&self` reference.
261314 pub ( crate ) fn update_iteration_count (
262315 & self ,
263316 cycle_head_index : DatabaseKeyIndex ,
@@ -277,15 +330,20 @@ impl CycleHeads {
277330 self . 0 . reserve ( other. 0 . len ( ) ) ;
278331
279332 for head in other {
280- self . insert ( head) ;
333+ debug_assert ! ( !head. removed. load( Ordering :: Relaxed ) ) ;
334+ self . insert ( head. database_key_index , head. iteration_count . load ( ) ) ;
281335 }
282336 }
283337
284- pub ( crate ) fn insert ( & mut self , head : & CycleHead ) -> bool {
338+ pub ( crate ) fn insert (
339+ & mut self ,
340+ database_key_index : DatabaseKeyIndex ,
341+ iteration_count : IterationCount ,
342+ ) -> bool {
285343 if let Some ( existing) = self
286344 . 0
287345 . iter_mut ( )
288- . find ( |candidate| candidate. database_key_index == head . database_key_index )
346+ . find ( |candidate| candidate. database_key_index == database_key_index)
289347 {
290348 let removed = existing. removed . get_mut ( ) ;
291349
@@ -294,23 +352,19 @@ impl CycleHeads {
294352
295353 true
296354 } else {
297- let existing_count = existing. iteration_count . load ( ) ;
298- let head_count = head. iteration_count . load ( ) ;
355+ let existing_count = existing. iteration_count . load_mut ( ) ;
299356
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).
303357 assert_eq ! (
304- existing_count, head_count ,
305- "Can't merge cycle heads {:?} with different iteration counts ({existing_count:?}, {head_count :?})" ,
358+ existing_count, iteration_count ,
359+ "Can't merge cycle heads {:?} with different iteration counts ({existing_count:?}, {iteration_count :?})" ,
306360 existing. database_key_index
307361 ) ;
308362
309363 false
310364 }
311365 } else {
312- debug_assert ! ( !head . removed . load ( Ordering :: Relaxed ) ) ;
313- self . 0 . push ( head . clone ( ) ) ;
366+ self . 0
367+ . push ( CycleHead :: new ( database_key_index , iteration_count ) ) ;
314368 true
315369 }
316370 }
0 commit comments