Skip to content

Commit

Permalink
feat(core): kill sidecar child processes on App drop, closes #1896 (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Jun 1, 2021
1 parent 9ddd9a9 commit 4bdc406
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 15 deletions.
5 changes: 5 additions & 0 deletions .changes/child-process-cleanup.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Kill child processes spawned with `tauri::api::process::Command` on `tauri::App` drop. Can be skipped with `tauri::Builder#skip_cleanup_on_drop`.
21 changes: 20 additions & 1 deletion core/tauri/src/api/process/command.rs
Expand Up @@ -7,7 +7,7 @@ use std::{
io::{BufRead, BufReader, Write},
path::PathBuf,
process::{Command as StdCommand, Stdio},
sync::Arc,
sync::{Arc, Mutex},
};

#[cfg(unix)]
Expand All @@ -24,6 +24,22 @@ use serde::Serialize;
use shared_child::SharedChild;
use tauri_utils::platform;

type ChildStore = Arc<Mutex<HashMap<u32, Arc<SharedChild>>>>;

fn commands() -> &'static ChildStore {
use once_cell::sync::Lazy;
static STORE: Lazy<ChildStore> = Lazy::new(Default::default);
&STORE
}

/// Kill all child process created with [`Command`].
/// By default it's called before the [`crate::App`] exits.
pub fn kill_children() {
for child in commands().lock().unwrap().values() {
let _ = child.kill();
}
}

/// Payload for the `Terminated` command event.
#[derive(Debug, Clone, Serialize)]
pub struct TerminatedPayload {
Expand Down Expand Up @@ -220,6 +236,8 @@ impl Command {
let child_ = child.clone();
let guard = Arc::new(RwLock::new(()));

commands().lock().unwrap().insert(child.id(), child.clone());

let (tx, rx) = channel(1);

let tx_ = tx.clone();
Expand Down Expand Up @@ -252,6 +270,7 @@ impl Command {
let _ = match child_.wait() {
Ok(status) => {
guard.write().await;
commands().lock().unwrap().remove(&child_.id());
tx.send(CommandEvent::Terminated(TerminatedPayload {
code: status.code(),
#[cfg(windows)]
Expand Down
64 changes: 50 additions & 14 deletions core/tauri/src/app.rs
Expand Up @@ -128,8 +128,21 @@ crate::manager::default_args! {
///
/// This type implements [`Manager`] which allows for manipulation of global application items.
pub struct App<P: Params> {
runtime: P::Runtime,
runtime: Option<P::Runtime>,
manager: WindowManager<P>,
#[cfg(shell_execute)]
cleanup_on_drop: bool,
}
}

impl<P: Params> Drop for App<P> {
fn drop(&mut self) {
#[cfg(shell_execute)]
{
if self.cleanup_on_drop {
crate::api::process::kill_children();
}
}
}
}

Expand All @@ -140,7 +153,7 @@ impl<P: Params> ManagerBase<P> for App<P> {
}

fn runtime(&self) -> RuntimeOrDispatch<'_, P> {
RuntimeOrDispatch::Runtime(&self.runtime)
RuntimeOrDispatch::Runtime(self.runtime.as_ref().unwrap())
}
}

Expand Down Expand Up @@ -180,7 +193,7 @@ impl<P: Params> App<P> {
/// Gets a handle to the application instance.
pub fn handle(&self) -> AppHandle<P> {
AppHandle {
runtime_handle: self.runtime.handle(),
runtime_handle: self.runtime.as_ref().unwrap().handle(),
manager: self.manager.clone(),
}
}
Expand All @@ -202,7 +215,7 @@ impl<P: Params> App<P> {
/// }
#[cfg(any(target_os = "windows", target_os = "macos"))]
pub fn run_iteration(&mut self) -> crate::runtime::RunIteration {
self.runtime.run_iteration()
self.runtime.as_mut().unwrap().run_iteration()
}
}

Expand Down Expand Up @@ -315,6 +328,9 @@ where
/// System tray event handlers.
#[cfg(feature = "system-tray")]
system_tray_event_listeners: Vec<SystemTrayEventListener<Args<E, L, MID, TID, A, R>>>,

#[cfg(shell_execute)]
cleanup_on_drop: bool,
}

impl<E, L, MID, TID, A, R> Builder<E, L, MID, TID, A, R>
Expand Down Expand Up @@ -345,6 +361,8 @@ where
system_tray: Vec::new(),
#[cfg(feature = "system-tray")]
system_tray_event_listeners: Vec::new(),
#[cfg(shell_execute)]
cleanup_on_drop: true,
}
}

Expand Down Expand Up @@ -572,6 +590,15 @@ where
self
}

/// Skips Tauri cleanup on [`App`] drop. Useful if your application has multiple [`App`] instances.
///
/// The cleanup calls [`crate::api::process::kill_children`] so you may want to call that function before exiting the application.
#[cfg(shell_execute)]
pub fn skip_cleanup_on_drop(mut self) -> Self {
self.cleanup_on_drop = false;
self
}

/// Builds the application.
#[allow(clippy::type_complexity)]
pub fn build(mut self, context: Context<A>) -> crate::Result<App<Args<E, L, MID, TID, A, R>>> {
Expand Down Expand Up @@ -630,8 +657,10 @@ where
}

let mut app = App {
runtime: R::new()?,
runtime: Some(R::new()?),
manager,
#[cfg(shell_execute)]
cleanup_on_drop: self.cleanup_on_drop,
};

app.manager.initialize_plugins(&app)?;
Expand All @@ -647,7 +676,7 @@ where

for pending in self.pending_windows {
let pending = app.manager.prepare_window(pending, &pending_labels)?;
let detached = app.runtime.create_window(pending)?;
let detached = app.runtime.as_ref().unwrap().create_window(pending)?;
let _window = app.manager.attach_window(detached);
#[cfg(feature = "updater")]
if main_window.is_none() {
Expand All @@ -665,6 +694,8 @@ where
let ids = get_menu_ids(&self.system_tray);
app
.runtime
.as_ref()
.unwrap()
.system_tray(
system_tray_icon.expect("tray icon not found; please configure it on tauri.conf.json"),
self.system_tray,
Expand All @@ -674,14 +705,18 @@ where
let app_handle = app.handle();
let ids = ids.clone();
let listener = Arc::new(std::sync::Mutex::new(listener));
app.runtime.on_system_tray_event(move |event| {
let app_handle = app_handle.clone();
let menu_item_id = ids.get(&event.menu_item_id).unwrap().clone();
let listener = listener.clone();
crate::async_runtime::spawn(async move {
listener.lock().unwrap()(&app_handle, SystemTrayEvent { menu_item_id });
app
.runtime
.as_mut()
.unwrap()
.on_system_tray_event(move |event| {
let app_handle = app_handle.clone();
let menu_item_id = ids.get(&event.menu_item_id).unwrap().clone();
let listener = listener.clone();
crate::async_runtime::spawn(async move {
listener.lock().unwrap()(&app_handle, SystemTrayEvent { menu_item_id });
});
});
});
}
}

Expand All @@ -690,7 +725,8 @@ where

/// Runs the configured Tauri application.
pub fn run(self, context: Context<A>) -> crate::Result<()> {
self.build(context)?.runtime.run();
let mut app = self.build(context)?;
app.runtime.take().unwrap().run();
Ok(())
}
}
Expand Down

0 comments on commit 4bdc406

Please sign in to comment.