#Sidecar kill on exit

16 messages · Page 1 of 1 (latest)

rocky wigeon
#

What is the proper way to start a single sidecar and then kill it when the Tauri process ends (on window close). I have the following code, mostly AI gen, and there is an error on the attempt to kill the child process, noted in window event handler. Is there a better way to do this?
`....
let app_handle = app.handle();
match app_handle.shell().sidecar("server") {
Ok(command) => {
let (mut rx, child) = command.spawn()?;

                    // Store the child process in a thread-safe manner
                    let child = Arc::new(Mutex::new(child));
                    app_handle.manage::<SharedChild>(child.clone());

.....
.on_window_event(|window, event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
let app_handle = window.app_handle();

            // This is the main window, so we'll shut down the app
            api.prevent_close();

            // Kill the sidecar process if it exists
            if let Some(child_state) = app_handle.try_state::<SharedChild>() {
                let child = child_state.lock().expect("Failed to lock child process");
                // ERROR -> cannot move out of dereference of `MutexGuard<'_, CommandChild>`move occurs because value has type `CommandChild`, which does not implement the `Copy`
                if let Err(e) = child.kill() {
                    eprintln!("Failed to kill sidecar process: {}", e);
                }
            }

            // Close the window and exit the app
            window.close().unwrap();
            std::process::exit(0);
        }
    })
    .run(tauri::generate_context!())
    .expect("error while running tauri application");

}`

#

Sidecar kill on exit

rocky wigeon
#

I finally solved it, for future sidecar enthusiasts, I finally used this prompt in ChatGPT:
"in tauri version 2, how do you spawn a sidecar and kill that child process when the application is quit"

Here are the relevant parts that work properly:
`
fn kill_sidecar(app: &tauri::AppHandle) {
// Retrieve the managed child process from the application state
if let Some(child_state) = app.try_state::<SharedChild>() {
let mut child = child_state.lock().unwrap();

    if let Some(child) = child.take() {
        if let Err(e) = child.kill() {
            eprintln!("Failed to kill sidecar process: {}", e);
        } else {
            println!("Sidecar process killed successfully.");
        }
    }
}

}
`

#

type SharedChild = Arc<Mutex<Option<CommandChild>>>; .... // Store the child process in a thread-safe manner let child = Arc::new(Mutex::new(Some(child))); app_handle.manage::<SharedChild>(child.clone());

hasty gate
#

can you share your whole code please? I need this exact code for both start and exit

potent slate
#

@rocky wigeon @hasty gate i'm trying to do the same thing. how did you manage to do it?

rocky wigeon
#

Will share later this afternoon

rocky wigeon
#

@potent slate @hasty gate will try to paste the whole file in sections, discord does not allow long comments

#

`

#

`
use std::sync::{Arc, Mutex};
use tauri::Manager;
use tauri_plugin_shell::ShellExt;
use tauri_plugin_shell::process::CommandEvent;
use tauri_plugin_shell::process::CommandChild;

type SharedChild = Arc<Mutex<Option<CommandChild>>>;

fn kill_sidecar(app: &tauri::AppHandle) {
// Retrieve the managed child process from the application state
if let Some(child_state) = app.try_state::<SharedChild>() {
let mut child = child_state.lock().unwrap();

    if let Some(child) = child.take() {
        if let Err(e) = child.kill() {
            eprintln!("Failed to kill sidecar process: {}", e);
        } else {
            println!("Sidecar process killed successfully.");
        }
    }
}

}
`

#

`
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.setup(|app| {
if cfg!(debug_assertions) {
app.handle().plugin(
tauri_plugin_log::Builder::default()
.level(log::LevelFilter::Info)
.build(),
)?;

            let app_handle = app.handle();
            match app_handle.shell().sidecar("server") {
                Ok(command) => {
                    let (mut rx, child) = command.spawn()?;

                    // app_handle.manage::<CommandChild>(child);

                    // Store the child process in a thread-safe manner
                    let child = Arc::new(Mutex::new(Some(child)));
                    app_handle.manage::<SharedChild>(child.clone());

                    tauri::async_runtime::spawn(async move {
                        #[cfg(debug_assertions)]
                        while let Some(event) = rx.recv().await {
                            match event {
                                CommandEvent::Stdout(line) => {
                                    println!("[server] {:?}", String::from_utf8(line));
                                }
                                CommandEvent::Stderr(line) => {
                                    println!("[server-err] {:?}", String::from_utf8(line));
                                }
                                _ => {}
                            }
                        }
                    });
                }
                Err(err) => panic!("Server failed to start: {}", err),
            };
        }
        Ok(())
    })
    `
#

`
.on_window_event(|window, event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
let app_handle = window.app_handle();

            // This is the main window, so we'll shut down the app
            api.prevent_close();

            // kill
            kill_sidecar(&app_handle);

            // Close the window and exit the app
            window.close().unwrap();
            std::process::exit(0);
        }
    })
    .run(tauri::generate_context!())
    .expect("error while running tauri application");

}
`

#

this is main.rs:
`
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
app_lib::run();
}
`

#

I'm on tauri 2.0.0-rc-15, probably not the latest now

#

hope this helps... this was a bugger to figure out