Skip to content

Commit

Permalink
feat: move to async POSIX signal handler
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ThomasFrans committed Oct 7, 2023
1 parent 209d8e2 commit 92c7622
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 15 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ toml = "0.8"
unicode-width = "0.1.9"
url = "2.2"
cursive_buffered_backend = "0.6.1"
signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] }

[target.'cfg(target_os = "linux")'.dependencies]
wl-clipboard-rs = {version = "0.7", optional = true}
Expand Down
49 changes: 34 additions & 15 deletions src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ 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};

use futures::stream::StreamExt;
#[cfg(unix)]
use signal_hook::{consts::SIGHUP, consts::SIGTERM, iterator::Signals};
use signal_hook::{consts::SIGHUP, consts::SIGTERM};
#[cfg(unix)]
use signal_hook_tokio::Signals;

use crate::command::Command;
use crate::commands::CommandManager;
Expand Down Expand Up @@ -50,6 +53,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,
Expand Down Expand Up @@ -193,6 +217,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,
Expand All @@ -207,22 +239,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) => {
Expand Down

0 comments on commit 92c7622

Please sign in to comment.