bump hbb_common: WebRTC peer connections own their I/O runtime

Closing the controlling window left the controlled side waiting out
ICE decay — ~25-30s in the peer's log, its disconnected/failed ladder
running to completion — where TCP delivers a FIN at once. The session
end closed the pc by spawning onto io_loop's own
`#[tokio::main(flavor = "current_thread")]` runtime, which is dropped
the moment io_loop returns, and nothing after that call yields: the
task was never polled even once, so no DTLS close_notify ever left.

Every attempt to fix that on the caller's side failed the same way,
because the mismatch was never about where the close ran: a pc's UDP
sockets register with the reactor, and its ICE/DTLS/SCTP pumps spawn
on the runtime, that is current while it is built — so a pc created
by a session outlives the only runtime that can drive its I/O, and a
close driven anywhere else completes without reaching the wire.

The bump homes them where they can outlive any caller: WebRTCStream
builds on a process-lifetime runtime and every detached close runs
there as its own never-cancelled task. io_loop keeps its plain
close_webrtc() calls and only documents why nothing here may spawn or
await the teardown on the dying session runtime.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016HV43uh1ztv6Wm5qi3Y1ne
This commit is contained in:
rustdesk
2026-08-22 21:33:54 +08:00
parent 2786d12a28
commit bfd3a9d7d9
2 changed files with 9 additions and 3 deletions

View File

@@ -197,7 +197,9 @@ impl<T: InvokeUiSession> Remote<T> {
}
self.handle_disconnected(round);
// Close the WebRTC pc on this decline path too (no-op for TCP/WS); otherwise its
// pc lingers in the global session cache until ICE decays on its own.
// pc lingers in the global session cache until ICE decays on its own. The pc
// and its teardown live on hbb_common's WebRTC I/O runtime, so this session
// runtime's death on return affects neither.
peer.close_webrtc();
return;
}
@@ -347,7 +349,11 @@ impl<T: InvokeUiSession> Remote<T> {
log::debug!("Exit io_loop of id={}", self.handler.get_id());
// Close the WebRTC peer connection (if this session used it) so its pc is not left
// lingering in the global session cache after the session ends; dropping `peer`
// alone does not release it. No-op for TCP/WebSocket transports.
// alone does not release it. No-op for TCP/WebSocket transports. The pc, its
// sockets and pump tasks, and this close all live on hbb_common's WebRTC I/O
// runtime — nothing may run them on this session runtime, which is dropped the
// moment io_loop returns and would kill the teardown (or, if awaited under a
// timeout, cancel it after `close()` latches `is_closed`, stranding the pc).
peer.close_webrtc();
// Stop client audio server.
if let Some(s) = self.stop_voice_call_sender.take() {