turbo-tasks-backend: anchor GC roots, gate on Data residency, wire up the app#95977
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Tests PassedCommit: bfd2a20 |
Stats from current PR🔴 1 regression, 1 improvement
📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📝 Changed Files (29 files)Files with changes:
View diffsapp-page-exp..ntime.dev.jsfailed to diffapp-page-exp..time.prod.jsfailed to diffapp-page-tur..ntime.dev.jsfailed to diffapp-page-tur..time.prod.jsfailed to diffapp-page-tur..ntime.dev.jsfailed to diffapp-page-tur..time.prod.jsfailed to diffapp-page.runtime.dev.jsfailed to diffapp-page.runtime.prod.jsfailed to diffapp-route-ex..ntime.dev.jsDiff too large to display app-route-ex..time.prod.jsDiff too large to display app-route-tu..ntime.dev.jsDiff too large to display app-route-tu..time.prod.jsDiff too large to display app-route-tu..ntime.dev.jsDiff too large to display app-route-tu..time.prod.jsDiff too large to display app-route.runtime.dev.jsDiff too large to display app-route.ru..time.prod.jsDiff too large to display pages-api-tu..ntime.dev.jsDiff too large to display pages-api-tu..time.prod.jsDiff too large to display pages-api.runtime.dev.jsDiff too large to display pages-api.ru..time.prod.jsDiff too large to display pages-turbo...ntime.dev.jsDiff too large to display pages-turbo...time.prod.jsDiff too large to display pages.runtime.dev.jsDiff too large to display pages.runtime.prod.jsDiff too large to display server.runtime.prod.jsDiff too large to display use-cache-pr..ntime.dev.jsDiff too large to display use-cache-pr..ntime.dev.jsDiff too large to display use-cache-pr..ntime.dev.jsDiff too large to display use-cache-pr..ntime.dev.jsDiff too large to display 📎 Tarball URLCommit: bfd2a20 |
a6f5c07 to
5d4aadd
Compare
3edcf11 to
f9cad98
Compare
5d4aadd to
d370353
Compare
f9cad98 to
1d92599
Compare
d370353 to
223e74f
Compare
1d92599 to
4096208
Compare
223e74f to
8bc08cb
Compare
4096208 to
d5b351e
Compare
8bc08cb to
388dd36
Compare
d5b351e to
f6bf401
Compare
388dd36 to
2839c72
Compare
f6bf401 to
8c41757
Compare
b9f23e7 to
0b657eb
Compare
fce4e76 to
9e36679
Compare
0b657eb to
b9a39db
Compare
… the app Makes GC correct and safe on a real application. - gc_root: a task created with no parent (parent == None) — spawned from outside the tracked graph (a turbo_tasks.run/NAPI entry, run_once, OperationVc::connect outside a task) — is marked a GC root (a persisted meta flag) and never collected. Without this, root operations reached parent_count 0 once their transient creator finished and GC collected them, cascading into their whole subgraph. Expands TaskFlags to u32 for the new flag. - Data-resident gate: gc_maybe_collectible also requires Data restored, so GC never selects a task whose type-bearing Data was evicted (which crashed at snapshot). Stop-gap with a TODO (real fix: restore-Data-in-GC or type-hash-in-Meta). - DetachedVc pins its OperationVc across the NAPI boundary; partial-evict pinned tasks rather than force full residence. - VersionedContentMap: serialization = "skip" + evict = "never" + pin/unpin the OperationVcs it stores, so the ops held in its State are anchored. Tests: gc_does_not_collect_parentless_root_task, the flag-layout tests, and the pinned/ root scenarios.
9e36679 to
bfd2a20
Compare
b9a39db to
f844d18
Compare

What
Makes GC correct and safe on a real application: anchors GC roots, refuses to collect tasks whose data isn't resident, and wires the anchoring into the app.
gc_root: a task created with no parent (parent == None) — spawned from outside the tracked graph (aturbo_tasks.run/NAPI entry,run_once, orOperationVc::connectoutside a task) — is marked a GC root (a persisted meta flag) and is never collected. Without this, root operations reachparent_count == 0once their transient creator finishes and GC collects them, cascading into their whole subgraph. ExpandsTaskFlagsfrom u16 to u32 to make room for the flag.gc_maybe_collectiblealso requires Data restored, so GC never selects a task whose type-bearing Data was evicted (which crashed at snapshot). Interim stop-gap with aTODO— the real fix is restore-Data-in-GC or moving the task-type-hash into Meta.DetachedVcpins itsOperationVcacross the NAPI boundary; pinned tasks are partially evicted rather than forced fully resident.VersionedContentMap:serialization = "skip"+evict = "never"+ pin/unpin of theOperationVcs it stores, so the ops held in itsStateare anchored (they live outside the task graph and would otherwise be collected).Why
The collection pass (PR below) is correct for graph-reachable tasks but over-collects anything held outside the tracked graph — the project container, per-request operations, and ops stashed in long-lived
State. This PR anchors those.Known tradeoff (interim)
gc_rootis a permanent pin, so request-scoped roots (e.g. a fresh source-map/trace operation per navigation) accumulate uncollected within a session. This is the conservative correctness-over-reclamation choice; a real root lifecycle is a follow-up.Verification
Backend GC suites (incl.
gc_does_not_collect_parentless_root_taskand the flag-layout tests), and a dev-app run withTURBO_ENGINE_GCenabled through navigation/HMR cycles — no crashes, dead leaves collected.Stack
Top of the parent_count GC stack; depends on all PRs below it. This is the complete feature.