fix(state_portal): prevent shutdown deadlock on full bounded channel (closes #8)

shutdown() calls enc.flush() → drain_encoder() → tx.send() on a
crossbeam bounded(32) channel.  If the channel is full and the
receiver (webrtc_rx) is alive but not being drained, send() blocks
forever — a self-deadlock since both ends belong to the same struct.

Two-layer fix:
- avhw.rs: replace tx.send() with tx.try_send(); handle Full (drop
  frame) and Disconnected (set flag) separately.
- state_portal.rs: drop webrtc_rx before flushing in shutdown() so
  try_send returns Disconnected immediately.

Regression tests added for the channel semantics.
This commit is contained in:
dailz
2026-06-06 20:02:09 +08:00
parent 7c1c9b2e19
commit 36f07c92e9
2 changed files with 61 additions and 7 deletions
+16 -7
View File
@@ -935,13 +935,22 @@ impl SwEncState {
let data: &[u8] = unsafe {
std::slice::from_raw_parts(raw.data, raw.size as usize)
};
if let Err(e) = tx.send(data.to_vec()) {
tracing::warn!(
"WebRTC channel send failed (receiver dropped): {} bytes lost",
e.0.len()
);
self.webrtc_disconnected = true;
break;
match tx.try_send(data.to_vec()) {
Ok(()) => {}
Err(crossbeam_channel::TrySendError::Full(frame)) => {
tracing::warn!(
"WebRTC channel full, dropping frame: {} bytes lost",
frame.len()
);
}
Err(crossbeam_channel::TrySendError::Disconnected(frame)) => {
tracing::warn!(
"WebRTC channel disconnected: {} bytes lost",
frame.len()
);
self.webrtc_disconnected = true;
break;
}
}
}
}