ChatGPT Codex 5.5 xhigh refactored even more
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
//! Peer liveness checks and stale-peer cleanup.
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use tokio::{
|
||||
sync::{RwLock, mpsc::UnboundedSender},
|
||||
task::JoinHandle,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
PeerEvent,
|
||||
config::{PEER_PING_IDLE_SECS, PEER_PING_INTERVAL_SECS, peer_stale_timeout},
|
||||
events,
|
||||
network::ping_peer,
|
||||
peer_db::{PeerGameDB, PeerId},
|
||||
};
|
||||
|
||||
/// Runs the ping service to check peer liveness.
|
||||
pub async fn run_ping_service(
|
||||
tx_notify_ui: UnboundedSender<PeerEvent>,
|
||||
peer_game_db: Arc<RwLock<PeerGameDB>>,
|
||||
downloading_games: Arc<RwLock<HashSet<String>>>,
|
||||
active_downloads: Arc<RwLock<HashMap<String, JoinHandle<()>>>>,
|
||||
) {
|
||||
log::info!(
|
||||
"Starting ping service ({PEER_PING_INTERVAL_SECS}s interval, \
|
||||
{}s idle threshold, {}s timeout)",
|
||||
PEER_PING_IDLE_SECS,
|
||||
peer_stale_timeout().as_secs()
|
||||
);
|
||||
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(PEER_PING_INTERVAL_SECS));
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
ping_idle_peers(
|
||||
&peer_game_db,
|
||||
&downloading_games,
|
||||
&active_downloads,
|
||||
&tx_notify_ui,
|
||||
)
|
||||
.await;
|
||||
|
||||
prune_stale_peers(
|
||||
&peer_game_db,
|
||||
&downloading_games,
|
||||
&active_downloads,
|
||||
&tx_notify_ui,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn ping_idle_peers(
|
||||
peer_game_db: &Arc<RwLock<PeerGameDB>>,
|
||||
downloading_games: &Arc<RwLock<HashSet<String>>>,
|
||||
active_downloads: &Arc<RwLock<HashMap<String, JoinHandle<()>>>>,
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
) {
|
||||
let peer_snapshots = { peer_game_db.read().await.peer_liveness_snapshot() };
|
||||
|
||||
for (peer_id, peer_addr, last_seen) in peer_snapshots {
|
||||
if last_seen.elapsed() < Duration::from_secs(PEER_PING_IDLE_SECS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let tx_notify_ui = tx_notify_ui.clone();
|
||||
let peer_game_db = peer_game_db.clone();
|
||||
let downloading_games = downloading_games.clone();
|
||||
let active_downloads = active_downloads.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
match ping_peer(peer_addr).await {
|
||||
Ok(true) => {
|
||||
peer_game_db.write().await.update_last_seen(&peer_id);
|
||||
}
|
||||
Ok(false) => {
|
||||
log::warn!("Peer {peer_addr} failed ping check");
|
||||
remove_peer_and_refresh(
|
||||
&peer_game_db,
|
||||
&downloading_games,
|
||||
&active_downloads,
|
||||
&tx_notify_ui,
|
||||
peer_id,
|
||||
"Removed stale peer",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Failed to ping peer {peer_addr}: {err}");
|
||||
remove_peer_and_refresh(
|
||||
&peer_game_db,
|
||||
&downloading_games,
|
||||
&active_downloads,
|
||||
&tx_notify_ui,
|
||||
peer_id,
|
||||
"Removed peer due to ping error",
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn prune_stale_peers(
|
||||
peer_game_db: &Arc<RwLock<PeerGameDB>>,
|
||||
downloading_games: &Arc<RwLock<HashSet<String>>>,
|
||||
active_downloads: &Arc<RwLock<HashMap<String, JoinHandle<()>>>>,
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
) {
|
||||
let stale_peers = {
|
||||
peer_game_db
|
||||
.read()
|
||||
.await
|
||||
.get_stale_peer_ids(peer_stale_timeout())
|
||||
};
|
||||
|
||||
let mut removed_any = false;
|
||||
for peer_id in stale_peers {
|
||||
removed_any |= remove_peer(peer_game_db, tx_notify_ui, peer_id, "Removed stale peer").await;
|
||||
}
|
||||
|
||||
if removed_any {
|
||||
events::emit_peer_game_list(peer_game_db, tx_notify_ui).await;
|
||||
handle_active_downloads_without_peers(
|
||||
peer_game_db,
|
||||
downloading_games,
|
||||
active_downloads,
|
||||
tx_notify_ui,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_peer_and_refresh(
|
||||
peer_game_db: &Arc<RwLock<PeerGameDB>>,
|
||||
downloading_games: &Arc<RwLock<HashSet<String>>>,
|
||||
active_downloads: &Arc<RwLock<HashMap<String, JoinHandle<()>>>>,
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
peer_id: PeerId,
|
||||
log_label: &str,
|
||||
) {
|
||||
if remove_peer(peer_game_db, tx_notify_ui, peer_id, log_label).await {
|
||||
events::emit_peer_game_list(peer_game_db, tx_notify_ui).await;
|
||||
handle_active_downloads_without_peers(
|
||||
peer_game_db,
|
||||
downloading_games,
|
||||
active_downloads,
|
||||
tx_notify_ui,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn remove_peer(
|
||||
peer_game_db: &Arc<RwLock<PeerGameDB>>,
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
peer_id: PeerId,
|
||||
log_label: &str,
|
||||
) -> bool {
|
||||
let removed_peer = { peer_game_db.write().await.remove_peer(&peer_id) };
|
||||
let Some(peer) = removed_peer else {
|
||||
return false;
|
||||
};
|
||||
|
||||
log::info!("{log_label}: {}", peer.addr);
|
||||
events::emit_peer_lost(peer_game_db, tx_notify_ui, peer.addr).await;
|
||||
true
|
||||
}
|
||||
|
||||
async fn handle_active_downloads_without_peers(
|
||||
peer_game_db: &Arc<RwLock<PeerGameDB>>,
|
||||
downloading_games: &Arc<RwLock<HashSet<String>>>,
|
||||
active_downloads: &Arc<RwLock<HashMap<String, JoinHandle<()>>>>,
|
||||
tx_notify_ui: &UnboundedSender<PeerEvent>,
|
||||
) {
|
||||
let active_ids = {
|
||||
downloading_games
|
||||
.read()
|
||||
.await
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
if active_ids.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
for id in active_ids {
|
||||
if peers_still_have_game(peer_game_db, &id).await {
|
||||
continue;
|
||||
}
|
||||
|
||||
let removed_from_tracking = {
|
||||
let mut guard = downloading_games.write().await;
|
||||
guard.remove(&id)
|
||||
};
|
||||
|
||||
if !removed_from_tracking {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(handle) = { active_downloads.write().await.remove(&id) } {
|
||||
handle.abort();
|
||||
}
|
||||
|
||||
events::send(
|
||||
tx_notify_ui,
|
||||
PeerEvent::DownloadGameFilesAllPeersGone { id },
|
||||
"DownloadGameFilesAllPeersGone",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn peers_still_have_game(peer_game_db: &Arc<RwLock<PeerGameDB>>, game_id: &str) -> bool {
|
||||
let guard = peer_game_db.read().await;
|
||||
!guard.peers_with_game(game_id).is_empty()
|
||||
}
|
||||
Reference in New Issue
Block a user