From e27abc065e6a4597a2873e95cb1d75215caacaac Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Mon, 22 Jun 2026 22:07:14 +0200 Subject: [PATCH] =?UTF-8?q?feat(windows):=20pf-vdisplay=20CLEAR=5FALL=20?= =?UTF-8?q?=E2=80=94=20reap=20orphaned=20virtual=20monitors=20on=20startup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "5-6 stale monitors that never tear down" failure (also seen with SudoVDA): an orphan from a crashed/killed previous host lingers because the driver watchdog is kept reset by a still-pinging new session, so it never fires for the orphan. - Driver (pf-vdisplay control.rs): new IOCTL_CLEAR_ALL (0x804) -> tear down every monitor. A pf-vdisplay extension; SudoVDA returns invalid for it (ignored), so the host can issue it unconditionally. - Host (vdisplay/sudovda.rs): send IOCTL_CLEAR_ALL once on startup (best-effort) to reap orphans before creating ours; and surface a failing keepalive PING (the old `let _ =` swallowed it, masking a lost control handle). Co-Authored-By: Claude Opus 4.8 --- crates/punktfunk-host/src/vdisplay/sudovda.rs | 27 +++++++++++++++++-- .../pf-vdisplay/src/control.rs | 8 ++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/punktfunk-host/src/vdisplay/sudovda.rs b/crates/punktfunk-host/src/vdisplay/sudovda.rs index fb1472e..f85b95e 100644 --- a/crates/punktfunk-host/src/vdisplay/sudovda.rs +++ b/crates/punktfunk-host/src/vdisplay/sudovda.rs @@ -53,6 +53,9 @@ const IOCTL_ADD: u32 = ctl(0x800); const IOCTL_REMOVE: u32 = ctl(0x801); const IOCTL_SET_RENDER_ADAPTER: u32 = ctl(0x802); // == 0x0022_2008 const IOCTL_GET_WATCHDOG: u32 = ctl(0x803); +/// pf-vdisplay extension (NOT in SudoVDA): tear down every virtual monitor. Sent once on host startup +/// to reap monitors orphaned by a crashed/killed previous host. SudoVDA returns invalid (ignored). +const IOCTL_CLEAR_ALL: u32 = ctl(0x804); const IOCTL_DRIVER_PING: u32 = ctl(0x888); const IOCTL_GET_VERSION: u32 = ctl(0x8FF); @@ -722,10 +725,20 @@ unsafe fn create_monitor(device: isize, mode: Mode, watchdog_s: u32) -> Result warned = false, + // A persistently failing PING means the cached control handle went invalid — the + // driver watchdog will then tear the monitor down mid-session. Surface it once + // (the old `let _ =` swallowed it, which masked exactly this during the bad-state churn). + Err(e) => { + if !warned { + tracing::warn!("SudoVDA keepalive PING failed (control handle lost?): {e:#}"); + warned = true; + } + } } thread::sleep(interval); } @@ -848,6 +861,16 @@ fn mgr_ensure_device(g: &mut Mgr) -> Result { 3 }; tracing::info!("SudoVDA watchdog timeout {}s", g.watchdog_s); + // Reap monitors orphaned by a crashed/killed previous host instance before we create ours. + // pf-vdisplay honors IOCTL_CLEAR_ALL; SudoVDA returns invalid (ignored). Without it an orphan + // lingers until the driver watchdog fires — but a still-pinging new session keeps resetting that + // watchdog, so orphans could accumulate (the "5-6 stale monitors that never tear down" failure). + { + let mut none: [u8; 0] = []; + if unsafe { ioctl(device, IOCTL_CLEAR_ALL, &[], &mut none) }.is_ok() { + tracing::info!("cleared orphaned virtual monitors on host startup"); + } + } let raw = device.0 as isize; g.device = Some(raw); Ok(raw) diff --git a/packaging/windows/vdisplay-driver/pf-vdisplay/src/control.rs b/packaging/windows/vdisplay-driver/pf-vdisplay/src/control.rs index 8a3c63f..91e38da 100644 --- a/packaging/windows/vdisplay-driver/pf-vdisplay/src/control.rs +++ b/packaging/windows/vdisplay-driver/pf-vdisplay/src/control.rs @@ -30,6 +30,10 @@ const IOCTL_ADD: u32 = ctl(0x800); const IOCTL_REMOVE: u32 = ctl(0x801); const IOCTL_SET_RENDER_ADAPTER: u32 = ctl(0x802); const IOCTL_GET_WATCHDOG: u32 = ctl(0x803); +/// pf-vdisplay extension (NOT in SudoVDA): tear down every monitor. The host issues this on startup to +/// reap monitors orphaned by a crashed/killed previous host instance. SudoVDA returns invalid for it +/// (harmlessly ignored), so the host can send it unconditionally. +const IOCTL_CLEAR_ALL: u32 = ctl(0x804); const IOCTL_PING: u32 = ctl(0x888); const IOCTL_GET_VERSION: u32 = ctl(0x8FF); @@ -112,6 +116,10 @@ pub extern "C-unwind" fn device_io_control( IOCTL_SET_RENDER_ADAPTER => do_set_render_adapter(request, input_len), IOCTL_GET_WATCHDOG => do_get_watchdog(request, output_len, &mut bytes), IOCTL_PING => NTSTATUS::STATUS_SUCCESS, + IOCTL_CLEAR_ALL => { + disconnect_all_monitors(); + NTSTATUS::STATUS_SUCCESS + } IOCTL_GET_VERSION => do_get_version(request, output_len, &mut bytes), _ => NTSTATUS::STATUS_INVALID_DEVICE_REQUEST, }