more informative shutdown

This commit is contained in:
Joe Ardent 2025-07-08 16:31:00 -07:00
parent 9e1e08851e
commit a543c177b2
3 changed files with 38 additions and 29 deletions

View file

@ -16,7 +16,7 @@ use serde::{Deserialize, Serialize};
use tokio::{
net::UdpSocket,
sync::{Mutex, mpsc},
task::JoinHandle,
task::JoinSet,
};
use transfer::Session;
@ -25,7 +25,14 @@ pub const MULTICAST_IP: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 167);
pub const LISTENING_SOCKET_ADDR: SocketAddrV4 =
SocketAddrV4::new(Ipv4Addr::from_bits(0), DEFAULT_PORT);
type ShutdownSender = mpsc::Sender<()>;
pub type ShutdownSender = mpsc::Sender<()>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Listeners {
Udp,
Http,
Multicast,
}
/// Contains the main network and backend state for an application session.
#[derive(Clone)]
@ -57,35 +64,34 @@ impl JoecalState {
})
}
pub async fn start(
&self,
config: &Config,
) -> crate::error::Result<(JoinHandle<()>, JoinHandle<()>, JoinHandle<()>)> {
pub async fn start(&self, config: &Config, handles: &mut JoinSet<Listeners>) {
let state = self.clone();
let konfig = config.clone();
let server_handle = {
handles.spawn({
let (tx, shutdown_rx) = mpsc::channel(1);
self.stop_tx.get_or_init(|| tx);
tokio::spawn(async move {
async move {
if let Err(e) = state.start_http_server(shutdown_rx, &konfig).await {
eprintln!("HTTP server error: {e}");
}
})
};
Listeners::Http
}
});
let state = self.clone();
let konfig = config.clone();
let udp_handle = {
tokio::spawn(async move {
handles.spawn({
async move {
if let Err(e) = state.listen_multicast(&konfig).await {
eprintln!("UDP listener error: {e}");
}
})
};
Listeners::Multicast
}
});
let state = self.clone();
let config = config.clone();
let announcement_handle = {
tokio::spawn(async move {
handles.spawn({
async move {
loop {
let rstate = state.running_state.lock().await;
if *rstate == RunningState::Stopping {
@ -96,10 +102,9 @@ impl JoecalState {
}
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
}
})
};
Ok((server_handle, udp_handle, announcement_handle))
Listeners::Udp
}
});
}
pub async fn stop(&self) {

View file

@ -1,11 +1,11 @@
#![feature(slice_as_array)]
use std::{collections::BTreeMap, io, net::SocketAddr};
use joecalsend::{Config, JoecalState, RunningState, error, models::Device};
use joecalsend::{Config, JoecalState, error, models::Device};
use local_ip_address::local_ip;
use network_interface::{Addr, NetworkInterface, NetworkInterfaceConfig, V4IfAddr};
use ratatui::{
DefaultTerminal, Frame,
DefaultTerminal,
buffer::Buffer,
crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind},
layout::Rect,
@ -14,6 +14,7 @@ use ratatui::{
text::{Line, Text},
widgets::{Block, Paragraph, Widget},
};
use tokio::task::JoinSet;
mod ui;
@ -46,14 +47,19 @@ async fn main() -> error::Result<()> {
.await
.expect("Could not create application session");
let config = Config::default();
let (h1, h2, h3) = state.start(&config).await.unwrap();
let mut handles = JoinSet::new();
state.start(&config, &mut handles).await;
let mut app = App::new(state.clone()).await;
let mut terminal = ratatui::init();
let result = app.run(&mut terminal).await;
ratatui::restore();
let _ = tokio::join!(h1, h2, h3);
while let Some(handle) = handles.join_next().await {
match handle {
Ok(h) => println!("Stopped {h:?}"),
Err(e) => println!("Got error {e:?}"),
}
}
Ok(result?)
}
@ -78,7 +84,7 @@ enum CurrentScreen {
impl App {
pub async fn new(state: JoecalState) -> Self {
App {
state: state.clone(),
state,
screen: CurrentScreen::Main,
peers: Default::default(),
}

View file

@ -1,10 +1,8 @@
use std::{collections::BTreeMap, net::SocketAddr};
use ratatui::{
Frame,
layout::{Constraint, Direction, Layout, Rect},
style::Stylize,
text::{Line, Span},
text::Line,
widgets::{Block, Borders, List, ListItem},
};