feat(windows): pf-vdisplay CLEAR_ALL — reap orphaned virtual monitors on startup
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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<M
|
||||
let stop_t = stop.clone();
|
||||
let pinger = thread::spawn(move || {
|
||||
let h = HANDLE(device_raw as *mut c_void);
|
||||
let mut warned = false;
|
||||
while !stop_t.load(Ordering::Relaxed) {
|
||||
let mut none: [u8; 0] = [];
|
||||
unsafe {
|
||||
let _ = ioctl(h, IOCTL_DRIVER_PING, &[], &mut none);
|
||||
match unsafe { ioctl(h, IOCTL_DRIVER_PING, &[], &mut none) } {
|
||||
Ok(_) => 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<isize> {
|
||||
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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user