refactor: asyncify stop and exit for Tui

This commit is contained in:
Erica Marigold 2025-02-03 06:24:32 +00:00
parent 764cddee0e
commit 21de453415
Signed by: DevComp
GPG key ID: 429EF1C337871656
2 changed files with 37 additions and 19 deletions

View file

@ -154,12 +154,12 @@ impl App {
action_tx.send(Action::ClearScreen)?; action_tx.send(Action::ClearScreen)?;
block_in_place(|| tui.enter())?; block_in_place(|| tui.enter())?;
} else if self.should_quit { } else if self.should_quit {
block_in_place(|| tui.stop())?; tui.stop().await?;
break; break;
} }
} }
block_in_place(|| tui.exit()) tui.exit().await
} }
async fn handle_events(&mut self, tui: &mut Tui) -> Result<()> { async fn handle_events(&mut self, tui: &mut Tui) -> Result<()> {

View file

@ -15,18 +15,19 @@ use crossterm::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use status::TuiStatus; use status::TuiStatus;
use tokio::{ use tokio::{
runtime::Handle,
sync::{ sync::{
mpsc::{self, UnboundedReceiver, UnboundedSender}, mpsc::{self, UnboundedReceiver, UnboundedSender},
Mutex, RwLock, Mutex, RwLock,
}, },
task::JoinHandle, task::{block_in_place, JoinHandle},
time::interval, time::{interval, sleep, timeout},
}; };
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use tracing::error; use tracing::error;
pub(crate) mod status;
pub(crate) mod backend; pub(crate) mod backend;
pub(crate) mod status;
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Event { pub enum Event {
@ -148,20 +149,30 @@ impl Tui {
cancellation_token.cancel(); cancellation_token.cancel();
} }
pub fn stop(&self) -> Result<()> { async fn await_shutdown(&self) {
self.cancel();
let mut counter = 0;
while !self.task.is_finished() { while !self.task.is_finished() {
std::thread::sleep(Duration::from_millis(1)); sleep(Duration::from_millis(1)).await;
counter += 1; }
if counter > 50 { }
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(); 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(()) Ok(())
} }
@ -184,8 +195,8 @@ impl Tui {
Ok(()) Ok(())
} }
pub fn exit(&mut self) -> Result<()> { pub async fn exit(&mut self) -> Result<()> {
self.stop()?; self.stop().await?;
// TODO: enable raw mode for pty // TODO: enable raw mode for pty
if true || crossterm::terminal::is_raw_mode_enabled()? { if true || crossterm::terminal::is_raw_mode_enabled()? {
let mut term = self.terminal.try_lock()?; let mut term = self.terminal.try_lock()?;
@ -211,7 +222,7 @@ impl Tui {
pub async fn suspend(&mut self) -> Result<Arc<CancellationToken>> { pub async fn suspend(&mut self) -> Result<Arc<CancellationToken>> {
// Exit the current Tui // Exit the current Tui
tokio::task::block_in_place(|| self.exit())?; self.exit().await?;
// Update the status and initialize a cancellation token // Update the status and initialize a cancellation token
let token = Arc::new(CancellationToken::new()); let token = Arc::new(CancellationToken::new());
@ -244,6 +255,13 @@ impl Tui {
impl Drop for Tui { impl Drop for Tui {
fn drop(&mut self) { 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}"))
});
})
} }
} }