Skip to content

fix(browser): reap browser-local-exit when its launching Runtime dies - #24

Merged
irzhywau merged 1 commit into
review/collaboration-candidatefrom
hotfix/browser-local-exit-orphan-reaping
Aug 27, 2026
Merged

irzhywau merged 1 commit into
review/collaboration-candidatefrom
hotfix/browser-local-exit-orphan-reaping

Conversation

@irzhywau

Copy link
Copy Markdown
Collaborator

Problem

browser-local-exit accumulated 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.

HostHelperProcess kills the helper only in Drop (api/server.rs:91). Three paths skip Drop entirely:

Trigger Drop runs?
SIGKILL / crash No
Panic (helper crate is panic = "abort") No
std::process::exit(75) in spawn_installed_binary_supersession_watch (host_lock.rs:177) No

The third is the dominant one: it is installed by both serve and gateway, 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 (no setpgid, no signal handler) the helper survives that too.

Compounding factor: replace_existing_socket: true made 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:

  • kqueue NOTE_EXIT — rejected: macOS-only, but components.json ships linux-amd64/arm64.
  • getppid() polling — matches the existing install_gateway_terminal_parent_watchdog idiom, but needs a new libc dep, polls forever, and carries a PID-reuse race.
  • pipe EOF — instant, race-free, no new dependencies. Chosen.

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_socket now 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.sh pins so the lifetime contract can't silently regress. Wired the smoke script and the helper crate's own fmt/clippy/test into just verify — the helper carries its own [workspace], so cargo test --workspace was 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.mjs all 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

WIP backup of the orphan-helper fix as developed on top of main (d358ded),
before rebasing onto review/collaboration-candidate.
@irzhywau
irzhywau requested a review from andersalm August 21, 2026 11:07
@irzhywau

Copy link
Copy Markdown
Collaborator Author

@andersalm I identified this bug on my machine, the browser-local-exit did want to stop even after kill -9. I identified it during a monitoring session I did on my machine. As you know more this feature than me I've asked for a PR review.

Thank you

@irzhywau irzhywau added bug Something isn't working enhancement New feature or request labels Aug 21, 2026
@irzhywau irzhywau self-assigned this Aug 21, 2026
@irzhywau irzhywau added this to the 0.7.x milestone Aug 21, 2026
@irzhywau
irzhywau merged commit 46e51a7 into review/collaboration-candidate Aug 27, 2026
@irzhywau
irzhywau deleted the hotfix/browser-local-exit-orphan-reaping branch August 27, 2026 11:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant