fix(queue): rename queued_ids to active_ids, document circular-dep caveat

This commit is contained in:
damocles 2026-06-04 17:35:56 +02:00 committed by mara
commit 5f05caee31
2 changed files with 14 additions and 4 deletions

View file

@ -96,6 +96,11 @@ each entry finishes. When a dep entry transitions to terminal, the loop re-evalu
the queue immediately, so downstream entries are unblocked with no extra wakeup. No the queue immediately, so downstream entries are unblocked with no extra wakeup. No
additional `notify_one()` call is needed. additional `notify_one()` call is needed.
**Circular-dep caveat**: if A depends on B and B depends on A, neither entry ever
becomes runnable — the worker skips both indefinitely with no error. Callers must
ensure acyclic dep graphs. Cycle detection is deferred to a future iteration (when
parallel workers make a stuck queue more visible).
--- ---
## Container view ## Container view

View file

@ -468,9 +468,10 @@ impl RebuildQueue {
.filter(|e| e.state.is_terminal()) .filter(|e| e.state.is_terminal())
.map(|e| e.id) .map(|e| e.id)
.collect(); .collect();
// All queued ids — used to distinguish "not yet terminal" from // Active (non-terminal) ids: Queued + Running. Named `active_ids`
// "evicted (= resolved)". // rather than `queued_ids` because Running entries are included;
let queued_ids: std::collections::HashSet<u64> = inner // used to distinguish "still in flight" from "evicted (= resolved)".
let active_ids: std::collections::HashSet<u64> = inner
.entries .entries
.iter() .iter()
.filter(|e| !e.state.is_terminal()) .filter(|e| !e.state.is_terminal())
@ -480,7 +481,11 @@ impl RebuildQueue {
e.state == QueueState::Queued e.state == QueueState::Queued
&& e.depends_on.iter().all(|dep_id| { && e.depends_on.iter().all(|dep_id| {
// Resolved if terminal in queue OR not in queue at all. // Resolved if terminal in queue OR not in queue at all.
terminal_ids.contains(dep_id) || !queued_ids.contains(dep_id) // Note: circular deps (A depends on B, B depends on A)
// silently deadlock — neither entry ever becomes runnable.
// Not a problem in v1 (no callers yet), but callers must
// ensure acyclic dep graphs.
terminal_ids.contains(dep_id) || !active_ids.contains(dep_id)
}) })
})?; })?;
let entry = &mut inner.entries[pos]; let entry = &mut inner.entries[pos];