From 683c81be031a822099c28507951306af52e005ac Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Thu, 25 Jun 2026 20:06:41 +0000 Subject: [PATCH] =?UTF-8?q?fix(windows-host):=20=C2=A72.5=20=E2=80=94=20op?= =?UTF-8?q?en=20the=20backend=20before=20the=20IDD-push=20preempt=20(vdm()?= =?UTF-8?q?=20init=20order)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On-glass caught a runtime panic the box compile couldn't: `VirtualDisplayManager used before a backend initialised it`. Step 3 put the preempt (`vdm().begin_idd_setup`) BEFORE `vdisplay::open` in virtual_stream, but vdisplay::open is what constructs the backend that calls manager::init() — so vdm() was reached before init and panicked on the first IDD-push session. (The old IDD_SETUP_LOCK/IDD_SESSION_STOP globals needed no init, so the prior ordering was fine.) Fix: open the backend first (it does no monitor work — just constructs the marker + opens the control device, initialising the manager), THEN run the preempt, THEN build the pipeline (which creates the monitor). The preempt still precedes this session's monitor creation, so the semantics are unchanged. Validates why §2.5 needs the on-glass gate, not just the compile. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/punktfunk-host/src/punktfunk1.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/punktfunk-host/src/punktfunk1.rs b/crates/punktfunk-host/src/punktfunk1.rs index 29ffe81..6d5334a 100644 --- a/crates/punktfunk-host/src/punktfunk1.rs +++ b/crates/punktfunk-host/src/punktfunk1.rs @@ -2216,15 +2216,19 @@ fn virtual_stream(ctx: SessionContext) -> Result<()> { bit_depth, "punktfunk/1 virtual display" ); + // Open the backend FIRST — on Windows this constructs the vdisplay backend, which initialises the + // host-lifetime VirtualDisplayManager (§2.5). It does NO monitor work, so it must precede the IDD-push + // preempt below (which reaches the manager) — otherwise `vdm()` is called before init and panics. + let mut vd = crate::vdisplay::open(compositor)?; // IDD-push reconnect preempt (the dance now lives in the manager, Goal-1 §2.5): serialize setup so a // reconnect FLOOD can't run concurrent monitor create/teardown, STOP the prior session + WAIT for it // to release its monitor (instead of tearing a monitor out from under a still-live session), and // register THIS session's stop. The returned guard holds the setup lock across the pipeline build; - // dropping it lets the next reconnect begin (and preempt us). + // dropping it lets the next reconnect begin (and preempt us). Held BEFORE the monitor is created + // (build_pipeline → vd.create), so the preempt still precedes this session's monitor creation. #[cfg(target_os = "windows")] let _idd_setup_guard = (plan.capture == crate::session_plan::CaptureBackend::IddPush) .then(|| crate::vdisplay::manager::vdm().begin_idd_setup(stop.clone())); - let mut vd = crate::vdisplay::open(compositor)?; let (mut capturer, mut enc, mut frame, mut interval) = build_pipeline_with_retry(&mut vd, mode, bitrate_kbps, bit_depth, plan)?; // Setup done — release the IDD-push setup lock so the next reconnect can begin (and preempt us).