refactor(protocol): clarify pending_moves expiry sweep

The original mem::replace + retain pattern worked but was opaque.
Switch to a two-pass approach: collect expired MessageHandles into
a small Vec, then remove + resolve. Behaviour-preserving; the
clippy-style readability win is worth the tiny extra allocation
(typical case: 0 or 1 expired entries per loop iteration).
This commit is contained in:
EdisonJwa
2026-05-16 10:10:03 +08:00
parent 2511b24982
commit 01003b3448
+15 -15
View File
@@ -500,24 +500,24 @@ async fn connection_task(
// 2b. Sweep stale pending_moves whose deadline has passed.
// The server should always reply within ~1 s; 3 s is a
// generous ceiling. Expired entries get an Ok() so the
// caller's snapshot-confirmation polling still has a chance
// to detect success (fall back to the previous optimistic
// behaviour rather than blocking the user with a fake
// ServerRejected).
// generous ceiling. Expired entries fall back to Ok() so
// the caller's snapshot-confirmation polling still has a
// chance to detect success — better than a fake
// ServerRejected for legacy servers that never reply to
// move requests.
if !pending_moves.is_empty() {
let now = std::time::Instant::now();
pending_moves.retain(|_, (reply, deadline)| {
if now >= *deadline {
// Cannot move `reply` out of `&mut` cleanly here
// without an intermediate take(); use a sentinel
// sender so retain's signature works.
let _ = std::mem::replace(reply, oneshot::channel().0).send(Ok(()));
false
} else {
true
let expired: Vec<MessageHandle> = pending_moves
.iter()
.filter_map(|(handle, (_, deadline))| {
if now >= *deadline { Some(*handle) } else { None }
})
.collect();
for handle in expired {
if let Some((reply, _)) = pending_moves.remove(&handle) {
let _ = reply.send(Ok(()));
}
});
}
}
// 3. Service at most one control request (non-blocking).