2 Commits
Author SHA1 Message Date
swaits 6d0f8cd50d chore: release 2.0.1 2026-04-25 20:52:41 -06:00
swaits b8e957fe33 fix(tui): drop stale "updating DHT" overlay, bound shutdown wait
Pre-upgrade, `Gossip::shutdown()` synchronously published a "remove
self" entry to mainline DHT — that's where the TUI's "updating
DHT..." overlay came from, and it was genuinely slow. The
iroh-gossip-rendezvous 0.1 migration dropped that publish (entries
age out instead), but the shutdown flow didn't change: the overlay
still drew before shutdown ran, and the terminal was restored after.
So `q` would land the user on the misleading screen for as long as
`endpoint.close()` took to flush.

Restore the terminal the instant `q` is pressed, remove the overlay,
and bound `swarm.shutdown()` with a 2s timeout so a slow endpoint
close can't stall the process.
2026-04-25 20:50:50 -06:00
5 changed files with 29 additions and 32 deletions

No files matched your search

+16
View File
@@ -1,5 +1,21 @@
# Changelog
## [2.0.1] - 2026-04-25
### Fixed
- **TUI quit no longer hangs on a misleading "updating DHT" screen.**
The 2.0.0 migration to `iroh-gossip-rendezvous` removed the
synchronous mainline-DHT publish that the old `Gossip::shutdown()`
performed (rendezvous entries age out instead), but the TUI flow
was unchanged: pressing `q` drew an "updating DHT…" overlay,
awaited `swarm.shutdown()`, and only then restored the terminal —
so the user sat on the stale message for as long as
`endpoint.close()` took to flush in-flight connections. Restore
the terminal the moment `q` is pressed, drop the overlay, and
cap the graceful-shutdown wait at 2 s so a slow endpoint close
can't stall the process.
## [2.0.0] - 2026-04-23
### Breaking
Generated
+1 -1
View File
@@ -1031,7 +1031,7 @@ dependencies = [
[[package]]
name = "donglora-bridge"
version = "2.0.0"
version = "2.0.1"
dependencies = [
"anyhow",
"blake3",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "donglora-bridge"
version = "2.0.0"
version = "2.0.1"
edition = "2024"
authors = ["Stephen Waits <steve@waits.net>"]
license = "MIT"
+10 -5
View File
@@ -1,7 +1,7 @@
use donglora_bridge::{config, gossip, radio, rate_limit, router, setup, tui};
use std::sync::Arc;
use std::time::Instant;
use std::time::{Duration, Instant};
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
@@ -174,13 +174,18 @@ async fn main() -> Result<()> {
} else {
let terminal = tui::run(config_watch_rx, swarm.clone(), stats, log_rx, cancel.clone(), start_time);
cancel.cancel();
swarm.shutdown().await;
info!("donglora-bridge stopped");
// Restore the terminal first so the user's shell reappears the moment
// they hit `q`. iroh-gossip-rendezvous needs no synchronous DHT work
// on shutdown — entries age out — so the only remaining wait is
// `endpoint.close()` flushing in-flight connections. Bound it.
if let Ok(mut term) = terminal {
tui::restore_terminal(&mut term);
}
cancel.cancel();
if tokio::time::timeout(Duration::from_secs(2), swarm.shutdown()).await.is_err() {
warn!("graceful shutdown timed out — exiting anyway");
}
info!("donglora-bridge stopped");
return Ok(());
}
+1 -25
View File
@@ -17,7 +17,7 @@ use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Modifier, Style};
use ratatui::symbols;
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Clear, Paragraph, Sparkline};
use ratatui::widgets::{Block, Borders, Paragraph, Sparkline};
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
@@ -173,7 +173,6 @@ pub fn run(
&mut series,
);
terminal.draw(|frame| draw_shutdown_overlay(frame, frame.area()))?;
Ok(terminal)
}
@@ -631,29 +630,6 @@ fn pad_right_align(data: &[u64], width: usize) -> Vec<u64> {
padded
}
// ── Shutdown overlay ─────────────────────────────────────────────
fn draw_shutdown_overlay(frame: &mut ratatui::Frame, area: Rect) {
let dim = Paragraph::new("").style(Style::default().bg(Color::Black));
frame.render_widget(dim, area);
let w = 36u16;
let h = 3u16;
let x = area.x + area.width.saturating_sub(w) / 2;
let y = area.y + area.height.saturating_sub(h) / 2;
let popup = Rect::new(x, y, w.min(area.width), h.min(area.height));
frame.render_widget(Clear, popup);
let block = Block::default().borders(Borders::ALL).border_style(Style::default().fg(C_BORDER));
let text = Paragraph::new(Line::from(vec![
Span::styled(" Shutting down", Style::default().fg(C_RATE_DROP)),
Span::styled(" — updating DHT...", Style::default().fg(C_LABEL)),
]))
.alignment(Alignment::Center)
.block(block);
frame.render_widget(text, popup);
}
// ── Shared helpers ───────────────────────────────────────────────
fn panel_block(title: &str) -> Block<'static> {