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

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) => {