Repository navigation
fix(browser): reap browser-local-exit when its launching Runtime dies - #24
Merged
irzhywau merged 1 commit intoAug 27, 2026
Conversation
WIP backup of the orphan-helper fix as developed on top of main (d358ded), before rebasing onto review/collaboration-candidate.
Collaborator
Author
|
@andersalm I identified this bug on my machine, the browser-local-exit did want to stop even after Thank you |
6 of 9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
browser-local-exitaccumulated as orphaned processes — 15 of them at PPID=1 over ~7 days on one dev machine, all attached to the same relay socket, one leaked per dev-server launch.HostHelperProcesskills the helper only inDrop(api/server.rs:91). Three paths skipDropentirely:Dropruns?panic = "abort")std::process::exit(75)inspawn_installed_binary_supersession_watch(host_lock.rs:177)The third is the dominant one: it is installed by both
serveandgateway, and fires every time the binary is rebuilt — which matches the observed one-per-launch leak rate.Separately,
terminate_managed_chat_runtime(runtime_control.rs:572) SIGTERMs the server pid rather than the process group, so on the interactive path (nosetpgid, no signal handler) the helper survives that too.Compounding factor:
replace_existing_socket: truemade each launch blindly unlink the predecessor's socket. The stranded helper kept serving an unlinked, unreachable socket forever — which is why helpers stacked up on a single path instead of colliding visibly.Fix
Bind the helper's lifetime to its launcher via pipe EOF. The Runtime spawns it with a piped stdin it holds open and sets
ELASTOS_BROWSER_LOCAL_EXIT_PARENT_EOF=1; the helper watches that pipe and reaps itself on EOF. However the Runtime dies, the write end closes — so all three trigger paths collapse into one signal.Alternatives considered:
NOTE_EXIT— rejected: macOS-only, butcomponents.jsonships linux-amd64/arm64.getppid()polling — matches the existinginstall_gateway_terminal_parent_watchdogidiom, but needs a newlibcdep, polls forever, and carries a PID-reuse race.Gated on the env var so standalone launches (stdin at
/dev/null) aren't killed by an immediate EOF.Ownership-scoped teardown. Helper teardown is pinned by inode identity to the socket it actually bound, so a stranded helper can never unlink a successor's relay socket at the same path. Same ownership-scoping class as
fix(vz): scope TURN cleanup to launch ownership.Liveness-aware socket reclaim.
remove_existing_browser_local_exit_socketnow probes for a listener, waits briefly for an incumbent to reap itself, unlinks genuinely stale sockets, and refuses rather than silently stealing a path a live helper is still serving.Tests
scripts/browser-local-exit-orphan-cleanup-smoke.sh— failed before the fix, passes after. Three cases: orphan reaping after a hard kill, successor-socket protection, and standalone launches unaffected.Also added 3 helper unit tests + 2 server unit tests, and 4
check-wci-alignment.shpins so the lifetime contract can't silently regress. Wired the smoke script and the helper crate's own fmt/clippy/test intojust verify— the helper carries its own[workspace], socargo test --workspacewas never covering it.Verification: server suite 1421 + 135 + 4 passed / 0 failed; helper 15 passed;
cargo fmt --all --check,clippy -p elastos-server -D warnings,check-wci-alignment.sh,browser-entropy-check.mjsall clean.Note for reviewers
The installed helper binary is checksum-pinned to the old build, so a true production end-to-end run needs a reinstall through the normal install flow. The spawn contract here is verified by unit tests plus a faithful stand-in launcher in the smoke script, not against a reinstalled binary.
🤖 Generated with Claude Code