adds a shutdown sender for the log reader task

This commit is contained in:
Automated Release Test 2024-03-06 19:27:52 -05:00
parent 0fd2b014c1
commit bfa52a7c27
4 changed files with 78 additions and 29 deletions

View File

@ -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<String>) {
pub fn run_zebrad() -> (Child, Receiver<String>, 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<String>) {
.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(

View File

@ -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<String, InvokeError> {
tracing::info!("dropping and killing zebrad child process");
app_handle.state::<AppState>().kill_zebrad_child();
println!("dropping and killing zebrad child process");
app_handle.state::<ZebradChild>().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<String
fs::write(zebrad_config_path, new_config)
.map_err(|err| format!("could not write to config file, error: {err}"))?;
tracing::info!("waiting for old zebrad child process to shutdown");
println!("waiting for old zebrad child process to shutdown");
tokio::time::sleep(Duration::from_secs(5)).await;
tracing::info!("starting new zebrad child process");
let (zebrad_child, zebrad_output_receiver) = run_zebrad();
println!("starting new zebrad child process");
loop {
// Check for an existing zebrad process
if app_handle.state::<ZebradChild>().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::<ZebradChild>().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::<AppState>()
.state::<ZebradChild>()
.insert_zebrad_child(zebrad_child);
app_handle
.state::<ZebradChild>()
.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<String, InvokeError> {
}
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::<AppState>().kill_zebrad_child();
app_handle.state::<ZebradChild>().kill();
app_handle.exit(0);
}
});

View File

@ -1,32 +1,57 @@
use std::{process::Child, sync::Mutex};
pub struct AppState {
zebrad_child: Mutex<Option<Child>>,
use tokio::sync::oneshot;
pub struct ZebradChild {
handle: Mutex<Option<Child>>,
read_task_shutdown_sender: Mutex<Option<oneshot::Sender<()>>>,
}
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()

View File

@ -12,8 +12,8 @@
"windows": [
{
"title": "zebra-app",
"width": 800,
"height": 600
"width": 1000,
"height": 800
}
],
"security": {