diff --git a/src/app.rs b/src/app.rs index 32749c6..a0b47b8 100644 --- a/src/app.rs +++ b/src/app.rs @@ -154,12 +154,12 @@ impl App { action_tx.send(Action::ClearScreen)?; block_in_place(|| tui.enter())?; } else if self.should_quit { - block_in_place(|| tui.stop())?; + tui.stop().await?; break; } } - block_in_place(|| tui.exit()) + tui.exit().await } async fn handle_events(&mut self, tui: &mut Tui) -> Result<()> { diff --git a/src/tui/mod.rs b/src/tui/mod.rs index c8f5d39..23d1db4 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -15,18 +15,19 @@ use crossterm::{ use serde::{Deserialize, Serialize}; use status::TuiStatus; use tokio::{ + runtime::Handle, sync::{ mpsc::{self, UnboundedReceiver, UnboundedSender}, Mutex, RwLock, }, - task::JoinHandle, - time::interval, + task::{block_in_place, JoinHandle}, + time::{interval, sleep, timeout}, }; use tokio_util::sync::CancellationToken; use tracing::error; -pub(crate) mod status; pub(crate) mod backend; +pub(crate) mod status; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum Event { @@ -148,20 +149,30 @@ impl Tui { cancellation_token.cancel(); } - pub fn stop(&self) -> Result<()> { - self.cancel(); - let mut counter = 0; + async fn await_shutdown(&self) { while !self.task.is_finished() { - std::thread::sleep(Duration::from_millis(1)); - counter += 1; - if counter > 50 { + sleep(Duration::from_millis(1)).await; + } + } + + pub async fn stop(&self) -> Result<()> { + self.cancel(); + + let attempt_timeout = Duration::from_millis(50); + let abort_shutdown = async { + while !self.task.is_finished() { self.task.abort(); } - if counter > 100 { - error!("Failed to abort task in 100 milliseconds for unknown reason"); - break; - } + }; + + if let Err(_) = timeout(attempt_timeout, self.await_shutdown()).await { + timeout(attempt_timeout, abort_shutdown) + .await + .inspect_err(|_| { + error!("Failed to abort task in 100 milliseconds for unknown reason") + })?; } + Ok(()) } @@ -184,8 +195,8 @@ impl Tui { Ok(()) } - pub fn exit(&mut self) -> Result<()> { - self.stop()?; + pub async fn exit(&mut self) -> Result<()> { + self.stop().await?; // TODO: enable raw mode for pty if true || crossterm::terminal::is_raw_mode_enabled()? { let mut term = self.terminal.try_lock()?; @@ -211,7 +222,7 @@ impl Tui { pub async fn suspend(&mut self) -> Result> { // Exit the current Tui - tokio::task::block_in_place(|| self.exit())?; + self.exit().await?; // Update the status and initialize a cancellation token let token = Arc::new(CancellationToken::new()); @@ -244,6 +255,13 @@ impl Tui { impl Drop for Tui { fn drop(&mut self) { - let _ = self.exit().inspect_err(|err| error!("Failed to exit Tui: {err}")); + block_in_place(|| { + let handle = Handle::current(); + let _ = handle.block_on(async { + self.exit() + .await + .inspect_err(|err| error!("Failed to exit Tui: {err}")) + }); + }) } }