Skip to content

Commit

Permalink
Cleanup on SIGTERM and clean exit
Browse files Browse the repository at this point in the history
Note that this not solve cleanup any resources when a SIGKILL is send to the fpx-app process
  • Loading branch information
hatchan committed Oct 7, 2024
1 parent 11ecc00 commit 32a2372
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 43 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions fpx-app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ nix = { version = "0.29", default-features = false, features = ["signal"] }
schemars = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
tauri = { version = "2.0.1", features = ["config-toml"] }
tauri = { version = "2.0.1", features = ["config-toml", "tracing"] }
tauri-plugin-dialog = "2.0.1"
tauri-plugin-store = { version = "2.0.1" }
tokio = { version = "1", default-features = false, features = [
"sync",
"process",
"macros",
"process",
"signal",
"sync",
] }
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
Expand Down
7 changes: 4 additions & 3 deletions fpx-app/src/api_manager/fpx_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ use fpx::service::Service;
use std::sync::{Arc, Mutex};
use tauri::async_runtime::spawn;
use tokio::sync::broadcast::error::RecvError;
use tokio::sync::oneshot;
use tracing::{error, info, trace, warn};

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct ApiManager {
// Sending a message on this channel will shutdown the axum server.
shutdown_tx: Mutex<Option<tokio::sync::oneshot::Sender<()>>>,
shutdown_tx: Arc<Mutex<Option<oneshot::Sender<()>>>>,
}

impl ApiManager {
Expand All @@ -28,7 +29,7 @@ impl ApiManager {
let listener = std::net::TcpListener::bind(format!("127.0.0.1:{listen_port}")).unwrap();
listener.set_nonblocking(true).unwrap();

let (shutdown, on_shutdown) = tokio::sync::oneshot::channel::<()>();
let (shutdown, on_shutdown) = oneshot::channel::<()>();
*shutdown_tx = Some(shutdown);

spawn(async move {
Expand Down
41 changes: 18 additions & 23 deletions fpx-app/src/api_manager/legacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ use nix::sys::signal::{killpg, Signal};
use nix::unistd::Pid;
use std::os::unix::process::CommandExt;
use std::process;
use std::sync::Mutex;
use std::sync::{Arc, Mutex};
use tauri::async_runtime::spawn;
use tracing::{error, trace, warn};

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct ApiManager {
api_pid: Mutex<Option<Pid>>,
api_pid: Arc<Mutex<Option<Pid>>>,
}

impl ApiManager {
Expand All @@ -23,7 +23,7 @@ impl ApiManager {
// signal to that process group.
if let Some(api_pid) = api_pid.take() {
// shutdown any existing api server
Self::send_sigterm_signal(api_pid);
send_sigterm_signal(api_pid);
}

// Create some environment variables overrides based on the fpx.toml
Expand Down Expand Up @@ -61,28 +61,23 @@ impl ApiManager {
/// Sends the SIGTERM signal to the API process group. If no API pid was set
/// then this function will do nothing.
pub fn stop_api(&self) {
let Some(api_pid) = self.api_pid.lock().expect("lock is poisoned").take() else {
trace!("No API running");
return;
match self.api_pid.lock().expect("lock is poisoned").take() {
Some(api_pid) => send_sigterm_signal(api_pid),
_ => trace!("No API running"),
};

Self::send_sigterm_signal(api_pid)
}
}

/// Send the SIGTERM signal to the specified process group.
///
/// This uses a Process ID type instead of a specific process group ID as
/// that does not exist.
fn send_sigterm_signal(api_pid: Pid) {
trace!(?api_pid, "sending SIGTERM signal to API process group");
/// Send the SIGTERM signal to the specified process group.
fn send_sigterm_signal(api_pid: Pid) {
trace!(?api_pid, "sending SIGTERM signal to API process group");

let result = killpg(api_pid, Signal::SIGTERM);
if let Err(errno) = result {
warn!(
?errno,
?api_pid,
"failed to send SIGNTERM signal to API process group"
);
}
let result = killpg(api_pid, Signal::SIGTERM);
if let Err(errno) = result {
warn!(
?errno,
?api_pid,
"failed to send SIGTERM signal to API process group"
);
}
}
47 changes: 33 additions & 14 deletions fpx-app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use std::time::Duration;
use tauri::menu::{MenuBuilder, MenuId, MenuItemBuilder, SubmenuBuilder};
use tauri::{Emitter, WebviewWindowBuilder};
use tauri::{Manager, Wry};
use tauri_plugin_store::StoreCollection;
use tauri_plugin_store::{StoreCollection, StoreExt};

Check failure on line 13 in fpx-app/src/main.rs

View workflow job for this annotation

GitHub Actions / Create binary for x86_64-unknown-linux-gnu

the name `StoreCollection` is defined multiple times

Check warning on line 13 in fpx-app/src/main.rs

View workflow job for this annotation

GitHub Actions / Create binary for x86_64-unknown-linux-gnu

unused import: `StoreCollection`

Check failure on line 13 in fpx-app/src/main.rs

View workflow job for this annotation

GitHub Actions / Create binary for x86_64-unknown-linux-gnu

[clippy] reported by reviewdog 🐶 error[E0252]: the name `StoreCollection` is defined multiple times --> fpx-app/src/main.rs:13:26 | 12 | use tauri_plugin_store::StoreCollection; | ----------------------------------- previous import of the type `StoreCollection` here 13 | use tauri_plugin_store::{StoreCollection, StoreExt}; | ^^^^^^^^^^^^^^^-- | | | `StoreCollection` reimported here | help: remove unnecessary import | = note: `StoreCollection` must be defined only once in the type namespace of this module Raw Output: fpx-app/src/main.rs:13:26:e:error[E0252]: the name `StoreCollection` is defined multiple times --> fpx-app/src/main.rs:13:26 | 12 | use tauri_plugin_store::StoreCollection; | ----------------------------------- previous import of the type `StoreCollection` here 13 | use tauri_plugin_store::{StoreCollection, StoreExt}; | ^^^^^^^^^^^^^^^-- | | | `StoreCollection` reimported here | help: remove unnecessary import | = note: `StoreCollection` must be defined only once in the type namespace of this module __END__

Check warning on line 13 in fpx-app/src/main.rs

View workflow job for this annotation

GitHub Actions / Create binary for x86_64-unknown-linux-gnu

[clippy] reported by reviewdog 🐶 warning: unused import: `StoreCollection` --> fpx-app/src/main.rs:13:26 | 13 | use tauri_plugin_store::{StoreCollection, StoreExt}; | ^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default Raw Output: fpx-app/src/main.rs:13:26:w:warning: unused import: `StoreCollection` --> fpx-app/src/main.rs:13:26 | 13 | use tauri_plugin_store::{StoreCollection, StoreExt}; | ^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default __END__

Check failure on line 13 in fpx-app/src/main.rs

View workflow job for this annotation

GitHub Actions / Create binary for aarch64-apple-darwin

the name `StoreCollection` is defined multiple times

Check warning on line 13 in fpx-app/src/main.rs

View workflow job for this annotation

GitHub Actions / Create binary for aarch64-apple-darwin

unused import: `StoreCollection`

Check failure on line 13 in fpx-app/src/main.rs

View workflow job for this annotation

GitHub Actions / Create binary for x86_64-apple-darwin

the name `StoreCollection` is defined multiple times

Check warning on line 13 in fpx-app/src/main.rs

View workflow job for this annotation

GitHub Actions / Create binary for x86_64-apple-darwin

unused import: `StoreCollection`
use tokio::signal::unix::SignalKind;
use tracing::debug;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, Registry};
Expand All @@ -28,12 +31,32 @@ fn main() {
std::process::exit(1);
}

let api_manager = ApiManager::default();

// Create a signal handler which will cleanup any resources in use
// (currently only the api manager) when fpx-app receives the SIGTERM signal.
let api_manager_ = api_manager.clone();
tauri::async_runtime::spawn(async move {
// Block until we receive a SIGTERM signal
tokio::signal::unix::signal(SignalKind::terminate())
.expect("Unable to set signal handler")
.recv()
.await;

debug!("received SIGTERM signal, stopping API server");

api_manager_.stop_api();

// Do we need to exit the process?
std::process::exit(0);
});

tauri::Builder::default()
.plugin(tauri_plugin_window_state::Builder::new().build())
.plugin(tauri_plugin_store::Builder::new().build())
.plugin(tauri_plugin_dialog::init())
.manage(AppState::default())
.manage(ApiManager::default())
.manage(api_manager)
.setup(|app| {
// Init store and load it from disk
let store = app
Expand Down Expand Up @@ -112,17 +135,6 @@ fn main() {
}
});

let window_ = window.clone();
window.on_window_event(move |event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
let app_state = window_.state::<AppState>();
if app_state.get_workspace().is_some() {
api.prevent_close();
window_.emit("request-close-workspace", "").unwrap();
}
}
});

Ok(())
})
.invoke_handler(tauri::generate_handler![
Expand All @@ -131,8 +143,15 @@ fn main() {
commands::workspace::list_recent_workspaces,
commands::workspace::open_workspace_by_path,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
.build(tauri::generate_context!())
.expect("failed to build application")
.run(|app_handle, event| {
// Make sure we cleanup after the app is going to exit
if let tauri::RunEvent::ExitRequested { .. } = event {
let api_manager = app_handle.state::<ApiManager>();
api_manager.stop_api();
};
});
}

fn setup_tracing() -> Result<()> {
Expand Down

0 comments on commit 32a2372

Please sign in to comment.