next.js
ef9b21f4 - turbo-tasks-backend: store reverse dependency edges as lists, not hash sets

Commit
128 days ago
turbo-tasks-backend: store reverse dependency edges as lists, not hash sets The reverse dependency edges — output_dependent, cell_dependents, cell_dependents_hashed — were AutoSet (hash sets). Every reverse-edge insert is now gated on the reader's forward edge being newly added (need_reverse), so the same edge is never inserted twice, and nothing ever queries the reverse sets for membership (no *_dependents_contains). The hash set's dedup and hashing are dead weight. Replace them with an unordered SmallVec-backed list via a new `storage = "list"` kind in the task_storage proc macro: push (append, no dedup) / remove (linear position + swap_remove) / iter, no contains accessor. A debug-only assertion in the generated `push_*` continuously checks the no-duplicate invariant, so any future ungated insert is caught in debug/test builds at zero release cost. The reverse inserts in the read paths (try_read_task_cell / try_read_task_output) stay under task_pair — both the target and reader are locked together, so the forward and reverse edges are visible atomically — but the reverse push is now gated on the forward add reporting a genuinely new edge so the list never gets a duplicate. Removing the hashbrown table + load-factor slack would have shrunk the boxed TaskStorage from 128 to 120 bytes, but TaskStorage is always heap-allocated and mimalloc rounds 120 up to its 128-byte size class — so the inline output_dependent list is sized List<TaskId, 6> to fill the class exactly, spending the slack on inline dependent capacity (6 before spilling to heap) instead of leaving it idle. 187 tests pass, including test_all_in_one (the lost/doubled-invalidation canary). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
Committer
Parents
Loading