fix(tauri): ensure sidecar process is killed when app window is closed
Previously, the app only sent an HTTP shutdown request to the Python sidecar with no timeout and no fallback. If the request failed, the sidecar process became orphaned, causing memory leaks. - Add 3-second timeout to the HTTP shutdown request - Add fallback to force-kill the sidecar by PID (taskkill /F /T on Windows, kill -9 on Unix) after HTTP shutdown attempt - Store child PID in AppState for use in both window close handler and Tauri command shutdown Fixes #944fix/sidecar-process-cleanup-on-close
parent
47234f9a64
commit
b408d253a1
|
|
@ -7,6 +7,8 @@ use std::fs::OpenOptions;
|
|||
use std::io::prelude::*;
|
||||
use std::io::Error;
|
||||
use std::io::Write;
|
||||
use std::process::Stdio;
|
||||
use std::time::Duration;
|
||||
use tauri::api::process::Command;
|
||||
use tauri::api::process::CommandEvent;
|
||||
use tauri::WindowEvent;
|
||||
|
|
@ -20,6 +22,7 @@ fn greet(name: &str) -> String {
|
|||
}
|
||||
struct AppState {
|
||||
port: u16,
|
||||
child_pid: u32,
|
||||
}
|
||||
#[derive(serde::Serialize)]
|
||||
struct AppConf {
|
||||
|
|
@ -42,17 +45,44 @@ fn read_config_file(path: &str) -> Result<String, Error> {
|
|||
Ok(contents)
|
||||
}
|
||||
|
||||
fn shutdown_api_server(port: u16) {
|
||||
fn shutdown_api_server(port: u16, child_pid: u32) {
|
||||
let url = format!("http://127.0.0.1:{}/infinite_image_browsing/shutdown", port);
|
||||
let res = reqwest::blocking::Client::new().post(url).send();
|
||||
let client = reqwest::blocking::Client::builder()
|
||||
.timeout(Duration::from_secs(3))
|
||||
.build();
|
||||
if let Ok(client) = client {
|
||||
let res = client.post(&url).send();
|
||||
if let Err(e) = res {
|
||||
eprintln!("{}", e);
|
||||
eprintln!("HTTP shutdown request failed: {}", e);
|
||||
}
|
||||
}
|
||||
// Fallback: force kill the process by PID to prevent orphaned processes
|
||||
kill_process_by_pid(child_pid);
|
||||
}
|
||||
|
||||
fn kill_process_by_pid(pid: u32) {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let _ = std::process::Command::new("taskkill")
|
||||
.args(&["/F", "/T", "/PID", &pid.to_string()])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn();
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let _ = std::process::Command::new("kill")
|
||||
.args(&["-9", &pid.to_string()])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn();
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn shutdown_api_server_command(state: tauri::State<'_, AppState>) {
|
||||
shutdown_api_server(state.port);
|
||||
shutdown_api_server(state.port, state.child_pid);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
@ -67,11 +97,14 @@ fn main() {
|
|||
args.push("--sd_webui_dir");
|
||||
args.push(&conf.sdwebui_dir);
|
||||
}
|
||||
let (mut rx, _child) = Command::new_sidecar("iib_api_server")
|
||||
let (mut rx, child) = Command::new_sidecar("iib_api_server")
|
||||
.expect("failed to create `iib_api_server` binary command")
|
||||
.args(args)
|
||||
.spawn()
|
||||
.expect("Failed to spawn sidecar");
|
||||
let child_pid = child.pid();
|
||||
// child handle is intentionally dropped here; we use the PID to kill the process on shutdown
|
||||
drop(child);
|
||||
let log_file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
|
|
@ -101,14 +134,14 @@ fn main() {
|
|||
}
|
||||
});
|
||||
tauri::Builder::default()
|
||||
.manage(AppState { port })
|
||||
.manage(AppState { port, child_pid })
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
greet,
|
||||
get_tauri_conf,
|
||||
shutdown_api_server_command
|
||||
])
|
||||
.on_window_event(move |event| match event.event() {
|
||||
WindowEvent::CloseRequested { .. } => shutdown_api_server(port),
|
||||
WindowEvent::CloseRequested { .. } => shutdown_api_server(port, child_pid),
|
||||
_ => (),
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
|
|
|
|||
Loading…
Reference in New Issue