feat: move to async POSIX signal handler

Instead of trying to handle signals for every step of the `cursive`
event loop, move the signal handling into its own asynchronous task and
send callbacks to `cursive` when a signal arrives.
This commit is contained in:
Thomas Frans
2023-10-07 18:34:49 +02:00
committed by Henrik Friedrichsen
parent 209d8e260b
commit a067ab2ae2
3 changed files with 49 additions and 15 deletions

13
Cargo.lock generated
View File

@@ -1985,6 +1985,7 @@ dependencies = [
"serde_cbor",
"serde_json",
"signal-hook",
"signal-hook-tokio",
"strum",
"strum_macros",
"tokio",
@@ -3267,6 +3268,18 @@ dependencies = [
"libc",
]
[[package]]
name = "signal-hook-tokio"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e"
dependencies = [
"futures-core",
"libc",
"signal-hook",
"tokio",
]
[[package]]
name = "slab"
version = "0.4.9"

View File

@@ -66,6 +66,7 @@ wl-clipboard-rs = {version = "0.7", optional = true}
[target.'cfg(unix)'.dependencies]
signal-hook = "0.3.0"
signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] }
[dependencies.rspotify]
default-features = false

View File

@@ -3,11 +3,15 @@ use std::rc::Rc;
use std::sync::{Arc, OnceLock};
use cursive::traits::Nameable;
use cursive::{Cursive, CursiveRunner};
use cursive::{CbSink, Cursive, CursiveRunner};
use log::{error, info, trace};
#[cfg(unix)]
use signal_hook::{consts::SIGHUP, consts::SIGTERM, iterator::Signals};
use futures::stream::StreamExt;
#[cfg(unix)]
use signal_hook::{consts::SIGHUP, consts::SIGTERM};
#[cfg(unix)]
use signal_hook_tokio::Signals;
use crate::command::Command;
use crate::commands::CommandManager;
@@ -50,6 +54,27 @@ pub fn setup_logging(filename: &Path) -> Result<(), fern::InitError> {
Ok(())
}
#[cfg(unix)]
async fn handle_signals(cursive_callback_sink: CbSink) {
let mut signals = Signals::new([SIGTERM, SIGHUP]).expect("could not register signal handler");
while let Some(signal) = signals.next().await {
info!("Caught {}, cleaning up and closing", signal);
match signal {
SIGTERM => {
cursive_callback_sink
.send(Box::new(|cursive| {
if let Some(data) = cursive.user_data::<UserData>().cloned() {
data.cmd.handle(cursive, Command::Quit);
}
}))
.expect("can't send callback to cursive");
}
_ => unreachable!(),
}
}
}
pub type UserData = Rc<UserDataInner>;
pub struct UserDataInner {
pub cmd: CommandManager,
@@ -193,6 +218,14 @@ impl Application {
cursive.add_fullscreen_layer(layout.with_name("main"));
#[cfg(unix)]
let cursive_callback_sink = cursive.cb_sink().clone();
#[cfg(unix)]
ASYNC_RUNTIME.get().unwrap().spawn(async {
handle_signals(cursive_callback_sink).await;
});
Ok(Self {
queue,
spotify,
@@ -207,22 +240,9 @@ impl Application {
/// Start the application and run the event loop.
pub fn run(&mut self) -> Result<(), String> {
#[cfg(unix)]
let mut signals =
Signals::new([SIGTERM, SIGHUP]).expect("could not register signal handler");
// cursive event loop
while self.cursive.is_running() {
self.cursive.step();
#[cfg(unix)]
for signal in signals.pending() {
if signal == SIGTERM || signal == SIGHUP {
info!("Caught {}, cleaning up and closing", signal);
if let Some(data) = self.cursive.user_data::<UserData>().cloned() {
data.cmd.handle(&mut self.cursive, Command::Quit);
}
}
}
for event in self.event_manager.msg_iter() {
match event {
Event::Player(state) => {