tauri icon indicating copy to clipboard operation
tauri copied to clipboard

Add kill-tree helper and runtime sidecar PID registry

Open sakina1303 opened this issue 1 month ago • 12 comments

Adds a best-effort process-tree killer and a lightweight runtime sidecar PID registry, ensuring that descendant processes spawned by sidecars are properly terminated when their parent sidecar is killed.

This resolves the core issue described in #14360 by introducing both API and runtime cleanup mechanisms for sidecar process management.

Details:

  • tauri::process::kill_process_tree(pid: u32) — POSIX shell & PowerShell based, best-effort.
  • Sidecar PID registry via AppHandle::register_sidecar(pid) / unregister_sidecar(pid) for cleanup on exit.
  • App::cleanup_before_exit() drains registry and calls the kill helper.
  • CLI dev-run now attempts process-tree cleanup when stopping dev children.

Files Changed:

  • process.rs: Added kill_process_tree() (POSIX + PowerShell).
  • mod.rs: Added sidecar PID tracking + helper methods.
  • app.rs: Added register/unregister APIs; updated cleanup.
  • desktop.rs: Added kill-tree logic in dev-run path.
  • CHANGELOG.md / PR_DRAFT.md: Updated with notes.

Testing:

  • [ ✅ ] cargo test -p tauri, all tests passed.
  • [ ✅ ] Manual: verified registry and cleanup invoke kill helper.
  • Runtime cleanup is opt-in: Call app_handle.register_sidecar(child.id() as u32) after spawning.

Fixes #14360

sakina1303 avatar Nov 10 '25 12:11 sakina1303

Package Changes Through ed8d624281fa8f9fce9c056dd5d9a1b8ad673b4a

There are 1 changes which include @tauri-apps/api with patch

Planned Package Versions

The following package releases are the planned based on the context of changes in this pull request.

package current next
@tauri-apps/api 2.9.0 2.9.1

Add another change file through the GitHub UI by following this link.


Read about change files or the docs at github.com/jbolda/covector

github-actions[bot] avatar Nov 10 '25 13:11 github-actions[bot]

I think we have now gathered a few different problems around this now:

  • Only the side car process is killed on app exit, not the entire process tree
    • Just from testing, it seems like some of the processes are killed while some others are not???
  • tauri dev doesn't kill side cars (it kills the app forcefully by sending SIGKILL on mac and linux, TerminateProcess on Windows to cargo)
    • It seems to me that it does kill the side car but not always the entire process tree???
  • NSIS installers don't kill side cars, they kill the app forcefully through TerminateProcess (~~the Wix .msi installers do, they send WM_QUERYENDSESSION to the app which is now handled after https://github.com/tauri-apps/tao/pull/1126~~ no, I thought they do but what actually happened is just it killed the tray icon by sending WM_CLOSE)
  • Updater plugin exits the app through std::process::exit which doesn't trigger the Exit event so side cars are not killed

My brain is basically fried right now, no more processes 😭

Legend-Master avatar Nov 13 '25 14:11 Legend-Master

  1. and perhaps 2) should imo be fixed via https://github.com/tauri-apps/plugins-workspace/issues/1332 and not by trying to kill a process tree. prob doesn't help with the rest though.

FabianLars avatar Nov 13 '25 14:11 FabianLars

I think we have now gathered a few different problems around this now:

  • Only the side car process is killed on app exit, not the entire process tree

    • Just from testing, it seems like some of the processes are killed while some others are not???
  • tauri dev doesn't kill side cars (it kills the app forcefully by sending SIGKILL on mac and linux, TerminateProcess on Windows to cargo)

    • It seems to me that it does kill the side car but not always the entire process tree???
  • NSIS installers don't kill side cars, they kill the app forcefully through TerminateProcess (~the Wix .msi installers do, they send WM_QUERYENDSESSION to the app which is now handled after fix(windows): emit LoopDestroyed on WM_ENDSESSION tao#1126~ no, I thought they do but what actually happened is just it killed the tray icon by sending WM_CLOSE)

  • Updater plugin exits the app through std::process::exit which doesn't trigger the Exit event so side cars are not killed

My brain is basically fried right now, no more processes 😭

I fixed point 1, 2 in my(our) project. 3 and 4 not test on my machine but has no community report related. With sidecar, I hold a custom strcture to wrap ProcessChild and give impl Drop for it to handle kill when droping. The entire app(which is main process) listen on system signal and excute tauri exiting and custom behavior, it will kill itself and its sidecar. They works well both in tauri dev and release build.

Tunglies avatar Dec 02 '25 19:12 Tunglies

I think we have now gathered a few different problems around this now:

  • Only the side car process is killed on app exit, not the entire process tree

    • Just from testing, it seems like some of the processes are killed while some others are not???
  • tauri dev doesn't kill side cars (it kills the app forcefully by sending SIGKILL on mac and linux, TerminateProcess on Windows to cargo)

    • It seems to me that it does kill the side car but not always the entire process tree???
  • NSIS installers don't kill side cars, they kill the app forcefully through TerminateProcess (~the Wix .msi installers do, they send WM_QUERYENDSESSION to the app which is now handled after fix(windows): emit LoopDestroyed on WM_ENDSESSION tao#1126~ no, I thought they do but what actually happened is just it killed the tray icon by sending WM_CLOSE)

  • Updater plugin exits the app through std::process::exit which doesn't trigger the Exit event so side cars are not killed

My brain is basically fried right now, no more processes 😭

I fixed point 1, 2 in my(our) project. 3 and 4 not test on my machine but has no community report related. With sidecar, I hold a custom strcture to wrap ProcessChild and give impl Drop for it to handle kill when droping. The entire app(which is main process) listen on system signal and excute Apphandle::Exit, it will kill itself and its sidecar. They works well.

We found it's hard to fully trust tauri exit behavior, and external listen system signal mannuly in clash-verge-rev/crates/signal. But behavior of macOS system shutdown signal seems hooked by tauri, can only processing with tauri::RunEvent::Exit otherwise will skip signal hanlde, but ubuntu and other linux works fine.

Tunglies avatar Dec 02 '25 19:12 Tunglies

This PR using system shell. For Windows example in some case, user can not invoke system powershell due to their machine permission setting or even disable powershell usage. We might not want to handle PID with shell diretcly, and there are potential security problems whether if cross-platform.

Tunglies avatar Dec 02 '25 19:12 Tunglies

With sidecar, I hold a custom strcture to wrap ProcessChild and give impl Drop for it to handle kill when droping.

I think this is more or less something we should provide in shell plugin?

We found it's hard to fully trust tauri exit behavior

Could you explain about this a bit more?

This PR using system shell.

Not the biggest fan of this either

Legend-Master avatar Dec 03 '25 02:12 Legend-Master

I think this is more or less something we should provide in shell plugin?

Yes, and the auto-cleanup or likely behavior can be toggled via a field setting whether if spawn or runtime. We did not provide this behavior before, might destroy downstream program behavior if enable by default.

Could you explain about this a bit more?

Months ago, did not remember too much details.

  • The sidecar plugin's process will not be killed when exiting cli tauri dev from terminal.
  • We tried handle with RunEvent::ExitRequested and RunEvent::Exit to reset system network settings when system shudown. None of linux, macOS and windows tasks works as expected. Might just we did not learned the proper way.

More, hanlding with resue tauri::asyncruntime to excute exiting or system shutdown operations in Windows will produce more likehood to failed or skipped. With a new tokio spawn would be totally fixed on that and works fine across Windows, macOS and Linux. https://github.com/clash-verge-rev/clash-verge-rev/pull/5533#issuecomment-3562934643 (Chinese)

Tunglies avatar Dec 04 '25 01:12 Tunglies

We tried handle with RunEvent::ExitRequested and RunEvent::Exit to reset system network settings when system shudown. None of linux, macOS and windows tasks works as expected. Might just we did not learned the proper way.

RunEvent::Exit event should now fire on system shutdown on Windows (https://github.com/tauri-apps/tao/pull/1126), not on macOS and Linux yet though

More, hanlding with resue tauri::asyncruntime to excute exiting or system shutdown operations in Windows will produce more likehood to failed or skipped.

Hmm, that sounds weird, the tauri async runtime by default should be very much the same as using a tokio one directly

Legend-Master avatar Dec 04 '25 02:12 Legend-Master

should be very much the same as using a tokio one directly

Our program continuosly handle differents tauri commands, data-related processing, system setting processing. Might be a little more complex than genral program. The async_runtime::spawn The singleton async runtime used by Tauri and exposed to users. that we already used for spawn some inner tasks. A new tokio runtime would not be stuggle with tauri.

/// async_runtime::spawn
pub fn spawn<F>(task: F) -> JoinHandle<F::Output>
where
  F: Future + Send + 'static,
  F::Output: Send + 'static,
{
  let runtime = RUNTIME.get_or_init(default_runtime);
  runtime.spawn(task)
}

Tunglies avatar Dec 04 '25 02:12 Tunglies

not on macOS and Linux yet though

Wierd on my test on macOS, signal_hook can not handle shutdown signal, but SIGTERM, SIGTERM interminal or standalone were fine. Macos shutdown handle only works with

#[cfg(target_os = "macos")]
        tauri::RunEvent::Exit => async_runtime::block_on(async {
            some_clean_up()
        }),

Linux works with competely works with signal_hook. Seemed tauri hooked macOS shutdown signal and use tauri::RunEvent::Exit for case.

Tunglies avatar Dec 04 '25 02:12 Tunglies

Macos shutdown handle only works with

Not gonna lie, this sounds like something macOS would. Not sending posix signals in favor of the NSApplication delegate bullshit would be such an Apple move 🙃 I'm somewhat sure it's not us doing that.

FabianLars avatar Dec 04 '25 13:12 FabianLars