fix(audio,linux): isolate zbus blocking probe on a fresh OS thread
LinuxGnomeWaylandBackend::probe() called zbus::blocking::Connection::session() directly. The blocking facade internally constructs a current-thread tokio runtime and block_on()s its async D-Bus client. probe() runs from PttController::new (sync) which is called from start_audio (async on the bridge tokio runtime). Nested runtimes panic with 'Cannot start a runtime from within a runtime'. Symptom on Linux: the first voice-channel join surfaced a SnackBar 'Could not join channel: join: task N panicked ...' while the channel-move command had already succeeded server-side. User saw 'channel joined but voice not enabled'. Fix: run the cheap blocking probe on a dedicated std::thread (no ambient runtime), join it synchronously, propagate the version / error. Probe is microseconds; the join cost is negligible.
This commit is contained in:
@@ -231,16 +231,33 @@ enum WorkerCmd {
|
||||
impl LinuxGnomeWaylandBackend {
|
||||
/// Synchronous probe via the blocking proxy. Cheap; runs once
|
||||
/// at `try_select` time before we commit to the live flow.
|
||||
///
|
||||
/// `zbus::blocking` internally spins up its own current-thread
|
||||
/// tokio runtime to drive the async D-Bus client. If we called
|
||||
/// it directly here we would panic with "Cannot start a runtime
|
||||
/// from within a runtime" because `try_select()` is invoked
|
||||
/// from `PttController::new`, which is called from the audio
|
||||
/// engine start path, which runs on the bridge's tokio
|
||||
/// multi-thread runtime. We therefore push the blocking probe
|
||||
/// onto a fresh OS thread (no ambient runtime), join it
|
||||
/// synchronously, and propagate the result. The probe is in
|
||||
/// the millisecond range so the join cost is negligible.
|
||||
fn probe() -> Result<Self, PttBackendError> {
|
||||
let conn = BlockingConnection::session()
|
||||
.map_err(|e| PttBackendError::Init(format!("session bus: {e}")))?;
|
||||
let version = {
|
||||
let proxy = BlockingGlobalShortcutsProxy::new(&conn)
|
||||
.map_err(|e| PttBackendError::Init(format!("proxy: {e}")))?;
|
||||
proxy
|
||||
.version()
|
||||
.map_err(|e| PttBackendError::Init(format!("portal version: {e}")))?
|
||||
};
|
||||
let join_result = std::thread::Builder::new()
|
||||
.name("chanora-ptt-portal-probe".to_string())
|
||||
.spawn(|| -> Result<u32, String> {
|
||||
let conn = BlockingConnection::session()
|
||||
.map_err(|e| format!("session bus: {e}"))?;
|
||||
let proxy = BlockingGlobalShortcutsProxy::new(&conn)
|
||||
.map_err(|e| format!("proxy: {e}"))?;
|
||||
proxy
|
||||
.version()
|
||||
.map_err(|e| format!("portal version: {e}"))
|
||||
})
|
||||
.map_err(|e| PttBackendError::Init(format!("probe thread spawn: {e}")))?
|
||||
.join()
|
||||
.map_err(|_| PttBackendError::Init("probe thread panicked".to_string()))?;
|
||||
let version = join_result.map_err(PttBackendError::Init)?;
|
||||
info!(
|
||||
target: "chanora_audio",
|
||||
portal_version = version,
|
||||
|
||||
Reference in New Issue
Block a user