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 {
|
impl LinuxGnomeWaylandBackend {
|
||||||
/// Synchronous probe via the blocking proxy. Cheap; runs once
|
/// Synchronous probe via the blocking proxy. Cheap; runs once
|
||||||
/// at `try_select` time before we commit to the live flow.
|
/// 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> {
|
fn probe() -> Result<Self, PttBackendError> {
|
||||||
let conn = BlockingConnection::session()
|
let join_result = std::thread::Builder::new()
|
||||||
.map_err(|e| PttBackendError::Init(format!("session bus: {e}")))?;
|
.name("chanora-ptt-portal-probe".to_string())
|
||||||
let version = {
|
.spawn(|| -> Result<u32, String> {
|
||||||
let proxy = BlockingGlobalShortcutsProxy::new(&conn)
|
let conn = BlockingConnection::session()
|
||||||
.map_err(|e| PttBackendError::Init(format!("proxy: {e}")))?;
|
.map_err(|e| format!("session bus: {e}"))?;
|
||||||
proxy
|
let proxy = BlockingGlobalShortcutsProxy::new(&conn)
|
||||||
.version()
|
.map_err(|e| format!("proxy: {e}"))?;
|
||||||
.map_err(|e| PttBackendError::Init(format!("portal version: {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!(
|
info!(
|
||||||
target: "chanora_audio",
|
target: "chanora_audio",
|
||||||
portal_version = version,
|
portal_version = version,
|
||||||
|
|||||||
Reference in New Issue
Block a user