Skip to content

Commit bdab78a

Browse files
committed
Smaller clean-ups
1 parent 64f0311 commit bdab78a

File tree

14 files changed

+147
-165
lines changed

14 files changed

+147
-165
lines changed

src/active_query.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ impl ActiveQuery {
225225
active_tracked_structs,
226226
mem::take(cycle_heads),
227227
iteration_count,
228-
false,
229228
);
230229

231230
let revisions = QueryRevisions {
@@ -518,7 +517,7 @@ impl fmt::Display for Backtrace {
518517
}
519518
write!(
520519
fmt,
521-
"{:?} -> {}",
520+
"{:?} -> iteration = {}",
522521
head.database_key_index, head.iteration_count
523522
)?;
524523
}

src/cycle.rs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ pub enum CycleRecoveryStrategy {
102102
pub 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

151164
impl 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
}

src/function.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ where
410410
fn wait_for<'me>(&'me self, zalsa: &'me Zalsa, key_index: Id) -> WaitForResult<'me> {
411411
match self.sync_table.try_claim::<false>(zalsa, key_index) {
412412
ClaimResult::Running(blocked_on) => WaitForResult::Running(blocked_on),
413-
ClaimResult::Cycle { inner: nested } => WaitForResult::Cycle { inner: nested },
413+
ClaimResult::Cycle { inner } => WaitForResult::Cycle { inner },
414414
ClaimResult::Claimed(_) => WaitForResult::Available,
415415
}
416416
}

src/function/execute.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ where
6767
if let Some(cycle_heads) = completed_query.revisions.cycle_heads_mut() {
6868
// Did the new result we got depend on our own provisional value, in a cycle?
6969
if cycle_heads.contains(&database_key_index) {
70-
let id = database_key_index.key_index();
71-
7270
// Ignore the computed value, leave the fallback value there.
7371
let memo = self
7472
.get_memo_from_table_for(zalsa, id, memo_ingredient_index)
@@ -126,18 +124,16 @@ where
126124
self.diff_outputs(zalsa, database_key_index, old_memo, &completed_query);
127125
}
128126

129-
let new_memo = self.insert_memo(
127+
self.insert_memo(
130128
zalsa,
131-
database_key_index.key_index(),
129+
id,
132130
Memo::new(
133131
Some(new_value),
134132
zalsa.current_revision(),
135133
completed_query.revisions,
136134
),
137135
memo_ingredient_index,
138-
);
139-
140-
new_memo
136+
)
141137
}
142138

143139
fn execute_maybe_iterate<'db>(
@@ -512,9 +508,7 @@ impl<'a, C: Configuration> ClearCycleHeadIfPanicking<'a, C> {
512508
impl<C: Configuration> Drop for ClearCycleHeadIfPanicking<'_, C> {
513509
fn drop(&mut self) {
514510
if std::thread::panicking() {
515-
let mut revisions =
516-
QueryRevisions::fixpoint_initial(self.ingredient.database_key_index(self.id));
517-
revisions.update_iteration_count_mut(
511+
let revisions = QueryRevisions::fixpoint_initial(
518512
self.ingredient.database_key_index(self.id),
519513
IterationCount::panicked(),
520514
);

src/function/fetch.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ where
141141
let memo = self.get_memo_from_table_for(zalsa, id, memo_ingredient_index);
142142

143143
if let Some(memo) = memo {
144-
if memo.value.is_some() && memo.may_be_provisional() {
144+
if memo.value.is_some() {
145145
memo.block_on_heads(zalsa, zalsa_local);
146146
}
147147
}
@@ -230,12 +230,9 @@ where
230230
self.update_shallow(zalsa, database_key_index, memo, can_shallow_update);
231231

232232
if C::CYCLE_STRATEGY == CycleRecoveryStrategy::Fixpoint {
233-
// This feels strange. I feel like we need to preserve the cycle heads. Let's say a cycle head only sometimes participates in the cycle.
234-
// This doesn't mean that the value becomes final because of it. The query might as well be cyclic in the next iteration but
235-
// we then never re-executed that query because it was marked as `verified_final`.
236233
memo.revisions
237234
.cycle_heads()
238-
.clear_except(database_key_index);
235+
.remove_all_except(database_key_index);
239236
}
240237

241238
crate::tracing::debug!(
@@ -267,7 +264,8 @@ where
267264
"hit cycle at {database_key_index:#?}, \
268265
inserting and returning fixpoint initial value"
269266
);
270-
let revisions = QueryRevisions::fixpoint_initial(database_key_index);
267+
let revisions =
268+
QueryRevisions::fixpoint_initial(database_key_index, IterationCount::initial());
271269
let initial_value = C::cycle_initial(db, C::id_to_input(zalsa, id));
272270
self.insert_memo(
273271
zalsa,
@@ -286,7 +284,10 @@ where
286284
let mut completed_query = active_query.pop();
287285
completed_query
288286
.revisions
289-
.set_cycle_heads(CycleHeads::initial(database_key_index));
287+
.set_cycle_heads(CycleHeads::initial(
288+
database_key_index,
289+
IterationCount::initial(),
290+
));
290291
// We need this for `cycle_heads()` to work. We will unset this in the outer `execute()`.
291292
*completed_query.revisions.verified_final.get_mut() = false;
292293
self.insert_memo(

0 commit comments

Comments
 (0)