diff --git a/src-tauri/src/child_process.rs b/src-tauri/src/child_process.rs index 349d9bc..0fe6441 100644 --- a/src-tauri/src/child_process.rs +++ b/src-tauri/src/child_process.rs @@ -9,7 +9,7 @@ use std::{ use tauri::{utils, AppHandle, Manager}; -use tokio::sync::mpsc::Receiver; +use tokio::sync::{mpsc::Receiver, oneshot}; /// Zebrad Configuration Filename pub const CONFIG_FILE: &str = "zebrad.toml"; @@ -42,7 +42,7 @@ pub fn zebrad_bin_path() -> PathBuf { exe_dir_path.join(ZEBRAD_COMMAND_NAME) } -pub fn run_zebrad() -> (Child, Receiver) { +pub fn run_zebrad() -> (Child, Receiver, oneshot::Sender<()>) { let zebrad_config_path = zebrad_config_path(); let zebrad_path = zebrad_bin_path(); @@ -77,22 +77,27 @@ pub fn run_zebrad() -> (Child, Receiver) { .expect("should have anonymous pipe"); // Spawn a task for reading output and sending it to a channel + let (shutdown_sender, mut shutdown_receiver) = oneshot::channel(); let (output_sender, output_receiver) = tokio::sync::mpsc::channel(100); let _output_reader_task_handle = tauri::async_runtime::spawn_blocking(move || { for line in BufReader::new(zebrad_stdout).lines() { + let line = line.expect("zebrad logs should be valid UTF-8"); + // Ignore send errors for now - if let Err(error) = - output_sender.blocking_send(line.expect("zebrad logs should be valid UTF-8")) - { + if let Err(error) = output_sender.blocking_send(line) { tracing::warn!( ?error, "zebrad output channel is closed before output terminated" ); } + + if shutdown_receiver.try_recv().is_ok() { + break; + } } }); - (zebrad_child, output_receiver) + (zebrad_child, output_receiver, shutdown_sender) } pub fn spawn_logs_emitter( diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 0a9eb6d..fc59eae 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -11,15 +11,15 @@ use tauri::{ipc::InvokeError, AppHandle, Manager, RunEvent}; mod child_process; mod state; -use state::AppState; +use state::ZebradChild; #[tauri::command] async fn save_config(app_handle: AppHandle, new_config: String) -> Result { - tracing::info!("dropping and killing zebrad child process"); - app_handle.state::().kill_zebrad_child(); + println!("dropping and killing zebrad child process"); + app_handle.state::().kill(); let zebrad_config_path = zebrad_config_path(); - tracing::info!("reading old config"); + println!("reading old config"); let old_config_contents = fs::read_to_string(&zebrad_config_path) .map_err(|err| format!("could not read existing config file, error: {err}"))?; @@ -27,16 +27,35 @@ async fn save_config(app_handle: AppHandle, new_config: String) -> Result().is_running() { + println!("new zebrad child is running after old zebrad child was killed, was a new config saved before zebrad restarted?"); + app_handle.state::().kill(); + tokio::time::sleep(Duration::from_secs(5)).await; + continue; + } else { + break; + } + } + + let (zebrad_child, zebrad_output_receiver, read_task_shutdown_sender) = run_zebrad(); + + println!("started new zebrad child process, starting output reader task"); - tracing::info!("started new zebrad child process, starting output reader task"); app_handle - .state::() + .state::() .insert_zebrad_child(zebrad_child); + + app_handle + .state::() + .insert_log_reader_shutdown_sender(read_task_shutdown_sender); + spawn_logs_emitter(zebrad_output_receiver, app_handle, false); Ok(old_config_contents) @@ -49,10 +68,10 @@ fn read_config() -> Result { } fn main() { - let (zebrad_child, zebrad_output_receiver) = run_zebrad(); + let (zebrad_child, zebrad_output_receiver, read_task_shutdown_sender) = run_zebrad(); tauri::Builder::default() - .manage(AppState::new(zebrad_child)) + .manage(ZebradChild::new(zebrad_child, read_task_shutdown_sender)) .setup(|app| { spawn_logs_emitter(zebrad_output_receiver, app.handle().clone(), true); Ok(()) @@ -62,7 +81,7 @@ fn main() { .unwrap() .run(move |app_handle: &AppHandle, _event| { if let RunEvent::Exit = &_event { - app_handle.state::().kill_zebrad_child(); + app_handle.state::().kill(); app_handle.exit(0); } }); diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 1f3d97d..b5d590b 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -1,32 +1,57 @@ use std::{process::Child, sync::Mutex}; -pub struct AppState { - zebrad_child: Mutex>, +use tokio::sync::oneshot; + +pub struct ZebradChild { + handle: Mutex>, + read_task_shutdown_sender: Mutex>>, } -impl AppState { - pub fn new(zebrad_child: Child) -> Self { +impl ZebradChild { + pub fn new(zebrad_child: Child, read_task_shutdown_sender: oneshot::Sender<()>) -> Self { Self { - zebrad_child: Mutex::new(Some(zebrad_child)), + handle: Mutex::new(Some(zebrad_child)), + read_task_shutdown_sender: Mutex::new(Some(read_task_shutdown_sender)), } } pub fn insert_zebrad_child(&self, new_zebrad_child: Child) { let mut zebrad_child_handle = self - .zebrad_child + .handle .lock() .expect("could not get lock on zebrad child mutex"); *zebrad_child_handle = Some(new_zebrad_child); } - /// Drops and kills the `zebrad_child` child process, if any. + pub fn insert_log_reader_shutdown_sender(&self, new_shutdown_sender: oneshot::Sender<()>) { + let mut read_task_shutdown_sender = self + .read_task_shutdown_sender + .lock() + .expect("could not get lock on zebrad child mutex"); + + if let Some(old_shutdown_sender) = read_task_shutdown_sender.replace(new_shutdown_sender) { + // It's okay if there's a send error, the task may exit and close the channel before + // the shutdown signal is sent. + let _ = old_shutdown_sender.send(()); + }; + } + + pub fn is_running(&self) -> bool { + self.handle + .lock() + .expect("could not get lock on zebrad_child mutex") + .is_some() + } + + /// Drops and kills the `zebrad_child` child process, if any, and + /// sends a shutdown signal to the log reader task, if any /// /// Returns true if there was a zebrad child process that's been killed and dropped, or /// returns false if there was no zebrad child process in the state. - pub fn kill_zebrad_child(&self) -> bool { + pub fn kill(&self) -> bool { if let Some(mut zebrad_child) = self - .zebrad_child + .handle .lock() .expect("could not get lock on zebrad_child mutex") .take() diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 35b18ec..fad209b 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -12,8 +12,8 @@ "windows": [ { "title": "zebra-app", - "width": 800, - "height": 600 + "width": 1000, + "height": 800 } ], "security": {