turbo-tasks-backend: check access in track_modification and only track Data on serialization invalidation (#99581)
### What?
Turbo-tasks backend: when a task guard marks a category (Data or Meta)
of a persistent task as modified, it now checks that the guard was
acquired with that category. Serialization invalidation now marks only
Data as modified.
### Why?
Marking a category as modified means the next snapshot writes the
in-memory copy of that category to disk. If the category was never
restored, the in-memory copy is empty, and the snapshot overwrites the
real persisted data.
`invalidate_serialization` hit exactly this case:
- The backend acquires the task with Data access only.
- The guard then marked both Data and Meta as modified, bypassing any
access check.
- After an eviction, mutating a `State` stored in a cell therefore
persisted an empty Meta.
- On the next restore the task's output was gone, so the task silently
re-executed.
### How?
- **Access check.** `TaskGuardImpl::track_modification` runs the
existing debug access assertion before delegating for non-transient
tasks. Transient tasks are still skipped.
- Every caller is now covered, including the macro-generated accessors
and the hand-written cell-data mutators.
- Any future mismatch fails loudly in debug builds and tests instead of
corrupting persisted state.
- **Data only for serialization invalidation.** Serialization
invalidation concerns cell data (for example a `State` mutated in
place), so it now marks only Data, through the checked guard path rather
than the raw storage guard.
- **Audit of the remaining callers.** None of them track a category they
don't hold:
- GC tombstoning and resurrection acquire `All`.
- The cell-data mutators check Data.
- New-task initialization restores `All`.
- The generated accessors derive the check and the tracked category from
the same field category.
- **Regression test.** `tests/invalidate_serialization.rs` runs a task
whose cell holds a `State` and snapshots/evicts it (asserting that both
Data and Meta were evicted). It then mutates the state, snapshots again,
and asserts the task is served from cache without re-executing.
- The test deliberately has no readers of the state. A re-executing
dependent would restore the task's Meta before the snapshot and hide the
bug.
- With the old tracking it fails on every run; with the fix it passes.
Verified with `cargo test -p turbo-tasks-backend` (debug build, so the
access assertions are active), plus clippy and rustfmt.
<!-- NEXT_JS_LLM -->
<!-- fleet c78c9059-116c-46e3-8ab3-5adc412156f1 -->
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>