diff --git a/crates/chanora_audio/src/ptt_backends/linux.rs b/crates/chanora_audio/src/ptt_backends/linux.rs index 68146e5..a0ac894 100644 --- a/crates/chanora_audio/src/ptt_backends/linux.rs +++ b/crates/chanora_audio/src/ptt_backends/linux.rs @@ -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 { - 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 { + 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,