fix: deliver the displacement signal, and register under one lock

Three from review, the first of which made the change do nothing at all.

The signal was handled under `rx_from_cm`, but `AuthedConnID::new` is handed
`tx_from_authed`, so it arrived at `rx_from_authed` and fell into that match's
catch-all. The displaced connection ignored it and ran on to its timeout exactly
as before. Handled on the channel it actually arrives on now.

The scan and the insert took the lock separately, so two registrations that both
scanned before either inserted each found nothing and both stayed - the duplicate
this exists to prevent. They share one lock now; only the sending stays outside
it.

And a failed send is logged rather than dropped. It means the connection's loop
has already ended, which is the outcome that was wanted, but silence there left
nothing to read when teardown does not arrive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns
This commit is contained in:
rustdesk
2026-09-08 17:42:39 +08:00
parent 20b3540151
commit 36a6cb0c5a

View File

@@ -655,18 +655,6 @@ impl Connection {
conn.on_close("connection manager window closed", true).await;
break;
}
// Displaced by a newer connection from the same session: end now rather
// than waiting out the inactivity timeout. Until then this one stays a
// video subscriber the capture loop waits on, a viewer the rate control
// averages in, and a second entry in the connection manager - all of it
// charged to the session that replaced it. Nothing is sent to the peer,
// and the screen is not locked: the session continues, on the new one.
ipc::Data::Displaced => {
conn.chat_unanswered = false; // seen
conn.file_transferred = false; //seen
conn.on_close("displaced by a newer connection", false).await;
break;
}
ipc::Data::CmErr(e) => {
if e != "expected" {
// cm closed before connection
@@ -1030,6 +1018,20 @@ impl Connection {
},
Some(data) = rx_from_authed.recv() => {
match data {
// Sent by the newer connection's own registration, over the channel
// `AuthedConnID::new` was handed - this one, not the connection
// manager's. End now rather than waiting out the inactivity timeout:
// until then this connection stays a video subscriber the capture loop
// waits on, a viewer the rate control averages in, and a second entry in
// the connection manager, all of it charged to the session that replaced
// it. Nothing is sent to the peer, and the screen is not locked: the
// session continues, on the new connection.
ipc::Data::Displaced => {
conn.chat_unanswered = false; // seen
conn.file_transferred = false; //seen
conn.on_close("displaced by a newer connection", false).await;
break;
}
#[cfg(all(target_os = "windows", feature = "flutter"))]
ipc::Data::PrinterData(data) => {
if Self::permission(keys::OPTION_ENABLE_REMOTE_PRINTER, &conn.control_permissions) {
@@ -6700,23 +6702,31 @@ mod raii {
// A connection of the same kind sharing this one's session key is the same
// session's earlier attempt, still running because its own link died without a close
// reaching it. Collect it here and end it below, off the lock.
let displaced: Vec<_> = AUTHED_CONNS
.lock()
.unwrap()
.iter()
.filter(|c| Self::is_displaced(c, conn_id, conn_type, &session_key))
.map(|c| (c.conn_id, c.sender.clone()))
.collect();
AUTHED_CONNS.lock().unwrap().push(AuthedConn {
conn_id,
conn_type,
session_key,
sender,
printer,
});
// Scanned and inserted under one lock: two registrations that both scanned before
// either inserted would each find nothing and both stay, which is the state this is
// here to prevent. Only the sending happens off the lock.
let displaced: Vec<_> = {
let mut conns = AUTHED_CONNS.lock().unwrap();
let displaced: Vec<_> = conns
.iter()
.filter(|c| Self::is_displaced(c, conn_id, conn_type, &session_key))
.map(|c| (c.conn_id, c.sender.clone()))
.collect();
conns.push(AuthedConn {
conn_id,
conn_type,
session_key,
sender,
printer,
});
displaced
};
for (displaced_id, sender) in displaced {
log::info!("#{displaced_id} displaced by #{conn_id}");
sender.send(Data::Displaced).ok();
if let Err(err) = sender.send(Data::Displaced) {
// Its loop has already ended, which is the outcome this wanted anyway.
log::debug!("#{displaced_id} was already gone: {err}");
}
}
Self::check_wake_lock();
use std::sync::Once;