Skip to content

fix(macos): prevent zombie children from that_detached - #130

Merged
Byron merged 2 commits into
Byron:mainfrom
s00d:fix/macos-that-detached-zombies
Aug 24, 2026
Merged

Byron merged 2 commits into
Byron:mainfrom
s00d:fix/macos-that-detached-zombies

Conversation

@s00d

@s00d s00d commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Problem

On macOS, that_detached() still uses spawn_detached(), which extra-forks and _exit(0)s without waitpid. The intermediate child becomes a zombie (Z / <defunct>).

This is the same class of bug as #119, but #119 only changed with_detached(). The default-open path (that_detached) was left on the old double-fork.

/usr/bin/open already detaches via LaunchServices (LSOpenCFURLRef / NSWorkspace). The launched app is not a child of the caller. Extra fork+setsid is unnecessary on macOS and is what produces the zombies.

Reproducer

open::that_detached("https://example.com")?;
// ps -o pid,ppid,stat,command -p <parent>
// β†’ short-lived /usr/bin/open child with STAT=Z

Seen in a long-lived Tauri app: each URL open left a zombie. After many opens the process table filled with defunct /usr/bin/open children.

Tracked from the consumer side: tauri-apps/plugins-workspace#3544
(tauri-plugin-opener calls open::that_detached() when with is None.)

Why a bump of 5.3.4 / 5.4.1 is not enough

API macOS after #119 (5.3.4+)
with_detached() waits for /usr/bin/open (.status())
that_detached() still spawn_detached() β†’ zombies

Verified locally against open 5.4.1: calling that_detached("https://example.com") five times left 5 zombie children. After this patch: 0.

Fix

Mirror with_detached():

  • macOS: that_detached() β†’ that() (wait for /usr/bin/open to return). The opened app still outlives the caller because LaunchServices owns it.
  • other platforms: keep spawn_detached() so launchers that stay attached (e.g. some xdg-open setups) do not die with the parent (tauri-apps/tauri#6849).

Tests

macos::tests::that_detached_does_not_leave_zombie_children opens https://example.com several times and asserts the parent’s zombie child count does not increase.

Local: cargo test and cargo clippy -- -D warnings pass on macOS.

Note on #6849

Waiting for /usr/bin/open does not re-bind the launched app as a child. SIGKILL of the caller after open returns leaves TextEdit / the browser running. Confirmed locally and on GitHub Actions macos-14 / macos-15.

Made with Cursor

s00d and others added 2 commits August 24, 2026 10:46
On macOS /usr/bin/open already detaches via LaunchServices. spawn_detached()
adds another fork and leaves zombie children. Align that_detached with
with_detached (PR Byron#119).

Co-authored-by: Cursor <cursoragent@cursor.com>
@Byron
Byron force-pushed the fix/macos-that-detached-zombies branch from 25f7a7c to 351bb54 Compare August 24, 2026 09:01
@Byron

Byron commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Thanks a lot! I could validate that the test catches the problem, and that the fix works.
Will create a new release momentarily.

@Byron
Byron merged commit 7468760 into Byron:main Aug 24, 2026
7 checks passed
@Byron

Byron commented Aug 24, 2026

Copy link
Copy Markdown
Owner

And here it is: https://github.com/Byron/open-rs/releases/tag/v5.4.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants