Skip to content

Commit c672380

Browse files
dalkiaclaude
andcommitted
fix(abgen): never adopt an orphaned server; reap the child on crash (Windows)
With the fixed default port an abgen orphaned by a crashed session owns the endpoint the next session expects; the health check accepted any listener, so a stale server (old version, other workspace's realm) was silently adopted while our own child died on bind. StartAsync now verifies our child is alive after the health check and fails fast otherwise. On Windows (player and editor) every child is additionally assigned to a kill-on-close Job Object, so the kernel reaps it when the explorer dies for any reason — crash included. macOS keeps cooperative teardown; a parent-pid watchdog in abgen is the upstream complement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 521efb6 commit c672380

1 file changed

Lines changed: 28 additions & 1 deletion

File tree

docs/abgen-sidecar.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ uses `CreateProcessW` with `CREATE_NO_WINDOW`, macOS/Linux the `DclProcesses` na
5858
`kill(pid, 0)`), not the managed `Exited` event. Only the editor keeps the managed `Process`
5959
path (with drained stdout/stderr pipes).
6060

61+
**Orphan protection**: teardown is cooperative (Dispose kills the child), so a hard crash of the
62+
explorer used to leave the server running — and with the fixed default port an orphan *owns* the
63+
endpoint the next session expects. Two defenses: on Windows (player and editor) every child is
64+
assigned to a kill-on-close Job Object, so the kernel reaps it when the explorer dies for any
65+
reason; and `StartAsync` refuses to adopt a foreign listener — after the health check passes it
66+
verifies our own child is still alive, and if the port is answered by anything else (orphan from a
67+
crashed macOS session, unrelated process) it fails fast with an explicit milestone instead of
68+
silently serving stale bundles. macOS has no job-object equivalent; a parent-pid watchdog in abgen
69+
is the tracked upstream complement.
70+
6171
Measured (Linux x86_64, CPU encoder): cold whole-entity JIT 0.8s (2-GLB scene) / 5.3s (24-GLB,
6272
12MB); warm disk-cache hits <1ms; server RSS ~16MB idle, 130–435MB peak during converts. v0.16.0's
6373
per-file parallelization cut cold whole-scene conversion by ~30 s on an M-series Mac against a real
@@ -83,13 +93,30 @@ failure). First run therefore enters the world with bundles already served; outs
8393
LSD + `--local-ab` the task is pre-completed and boot is unaffected. The wait is absorbed under
8494
the splash screen, before the authentication screen.
8595

96+
**Clean fallback when the sidecar can't be had**: the readiness task resolves to a bool — false
97+
when the server never came up — and `MainSceneLoader` then drops the optimized-assets override
98+
(`DecentralandUrlsSource.ClearOptimizedAssetsOverride`, which also evicts the flag-dependent URL
99+
cache) before any optimized-asset request has resolved. The whole session — scene bundles,
100+
wearables, emotes, LODs, and the registry-composed profile/entities endpoints — falls back to the
101+
production hosts exactly as if `--local-ab` had not been passed, instead of hitting the dead
102+
loopback port and recovering per request. A server that turns healthy and dies later keeps the
103+
override (bundles JIT per request; supervision restarts it up to 3×).
104+
86105
## Visibility
87106

88107
The scene dev console's AB tab mirrors `/progress/{entity}` live: the summary shows the server's
89108
authoritative `converted/total` counter, per-file rows show whatever the 500 ms poll catches
90109
(backfilled to the full census when the manifest lands), and milestone rows mark every lifecycle
91110
moment — download progress, installed, warm-up started, READY in Ns, already-warm, server-side
92-
failures (manifest exitCode), sidecar failed. The sidebar AB button pulses while conversion runs
111+
failures (manifest exitCode), sidecar failed. Content-edit reconversions are mirrored too: the LSD
112+
reload path (`LocalSceneDevelopmentController`, which already receives the preview server's edit
113+
message — including the changed model's path) raises a consumable signal on
114+
`AbgenConversionMetrics`; the sidecar's session-long watcher (`WatchReconversionsAsync`) consumes it
115+
and re-runs the manifest lane, which coalesces with (or triggers) the server's rebuild — the panel
116+
flips back to converting, names the edited file, tracks the rebuild and settles to READY with a
117+
"reconverted in Ns" milestone, accurate even when the rebuild outpaces the progress poll (texture
118+
edits arrive as unnamed whole-scene updates — sdk-commands only names `.glb/.gltf` changes).
119+
The sidebar AB button pulses while conversion runs
93120
and stays lit after unseen failures. The panel **opens itself** when long-running work starts
94121
(consumable open-request on `AbgenConversionMetrics`, consumed by `DebugMenuController`) and
95122
**closes itself** a few seconds after a clean READY — any failure keeps it open, and a manual

0 commit comments

Comments
 (0)