summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/state.rs22
-rw-r--r--src/swarm/peer.rs3
2 files changed, 22 insertions, 3 deletions
diff --git a/src/state.rs b/src/state.rs
index a43c2f9..dd360de 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -274,11 +274,29 @@ impl PeerState {
}
/// True (and records the ping) if an established, quiet peer needs a keepalive.
- pub(crate) fn take_keepalive_due(&mut self, timing: &Timing, now: Instant) -> bool {
+ ///
+ /// One PING and its PONG tell both sides that the other is alive, so only one side of
+ /// a pair needs to ping: the one with the smaller key (`we_ping`). Each of its PINGs
+ /// makes the other side's peer look recently heard from, so the other side never
+ /// pings while it does. If the pings stop, the other side pings once it has heard
+ /// nothing for half an interval more. Without that, both sides would get due at about
+ /// the same time (each one's quiet time starts with the same exchange) and their
+ /// PINGs would often cross.
+ pub(crate) fn take_keepalive_due(
+ &mut self,
+ timing: &Timing,
+ we_ping: bool,
+ now: Instant,
+ ) -> bool {
+ let quiet_enough = if we_ping {
+ timing.keepalive
+ } else {
+ timing.keepalive + timing.keepalive / 2
+ };
let due = self.liveness == Liveness::Active
&& self.hello_state == HelloState::Established
&& now.saturating_duration_since(self.added_at) >= timing.keepalive
- && self.quiet_for(now) >= timing.keepalive
+ && self.quiet_for(now) >= quiet_enough
&& self
.last_ping_at
.is_none_or(|t| now.saturating_duration_since(t) >= timing.keepalive);
diff --git a/src/swarm/peer.rs b/src/swarm/peer.rs
index bad8e24..5928ac2 100644
--- a/src/swarm/peer.rs
+++ b/src/swarm/peer.rs
@@ -410,7 +410,8 @@ pub(crate) async fn run_peer_worker(state: AppState) {
let (became_dormant, ping_due, unanswered) = {
let mut peer_state = peer_state_mutex.lock().await;
let became_dormant = ready && peer_state.check_dormancy(&state.timing, now);
- let ping_due = peer_state.take_keepalive_due(&state.timing, now);
+ let we_ping = state.local_id() < peer.as_str();
+ let ping_due = peer_state.take_keepalive_due(&state.timing, we_ping, now);
let unanswered = peer_state.take_overdue_sync_reply(now);
(became_dormant, ping_due, unanswered)
};