adds a shutdown sender for the log reader task
This commit is contained in:
parent
0fd2b014c1
commit
bfa52a7c27
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
"windows": [
|
||||
{
|
||||
"title": "zebra-app",
|
||||
"width": 800,
|
||||
"height": 600
|
||||
"width": 1000,
|
||||
"height": 800
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
|
|
Loading…
Reference in New Issue