Skip reader task locking for immutable Turbo Tasks reads (#96179)
## What changed
Tracked output and cell reads now acquire the target task first. If the
target is already immutable, the read skips locking the reader task
because no dependency edge can be added and the target cannot be
invalidated. Mutable targets drop the speculative target lock and
reacquire the target/reader pair together, preserving the existing
invalidation-race guarantee.
`verify_immutable` deliberately retains the pair-lock path. Listener
diagnostics also preserve the tracked reader ID when the fast path skips
its guard.
The separate current-task ID cache is in #96180. This PR is now
backend-only so the lock-ordering change can receive focused backend
review.
## Why
The previous path locked both the target and reader task for every
tracked read. That synchronization is necessary for mutable targets but
redundant once the target is immutable. Instrumentation found that
1,541,880 of 4,501,364 sampled tracked reads (34.254%) could use the
bypass.
## Performance evidence
Measured at `0950e8ae05db20c33302f02257114f5b857edabc` on a dedicated
8-vCPU/16-GiB Vercel Sandbox with frozen side-by-side binaries.
These measurements are directional evidence, not precise estimates.
Small percentage changes are difficult to resolve reliably. This
campaign and #96180's incremental campaign used separate run sets: the
absolute endpoint here does not match #96180's starting point, which
demonstrates cross-campaign noise. The values must not be compared
across PRs, and their percentages must not be added.
### Browser-driven HMR, 30-second B/C/C/B
- Evaluation: 20.771360 → 19.176645 ms (**-7.677%**)
- Commit: 21.714832 → 20.371425 ms (**-6.187%**)
### Production builds
- First 12-campaign matrix: **-1.931%**
- Complementary 12-campaign matrix: **-1.990%**
- Combined 24-campaign aggregate: **-1.959%**
Five of six balanced blocks favored the candidate. The conservative
six-block 95% interval was -4.042% to +0.248%, so the production
estimate is supporting evidence rather than a precise claim.
### Causal profiles
- HMR `Storage::access_pair_mut` self time fell from 3.19%/3.19% to
1.21%/1.18%.
- HMR voluntary context switches fell 32.898%.
- Production pair-access self time and voluntary context switches also
fell; production voluntary switches changed -14.552%.
- All accepted perf captures reported zero lost samples.
## Validation
On the exact backend-only head
(`9ccfd6753526f716466a9584fba8c8753b7d83bf`):
- Complete `turbo-tasks-backend` package tests, integrations, and
doctests
- Complete backend package matrix with `--features verify_immutable`
- `cargo fmt --check -p turbo-tasks-backend`
- `cargo clippy -p turbo-tasks-backend --all-targets -- -D warnings`
- `git diff --check`
- Conflict-free merge-tree check against current `canary` (`5cdcbbb05b`)