fix(host/windows): per-session SudoVDA monitor GUID (stop overlapping-session monitor teardown)
User observed: 'display disconnected' + freeze with NO context change, and 'first switch happy, subsequent slower, then chaos under stress'. Log shows the cause: MONITOR_GUID was a FIXED constant, so overlapping sessions (a client RECONNECTING after a freeze before the old session tore down, or concurrent sessions) all map to the SAME SudoVDA monitor (same GUID -> IOCTL_ADD reuses target 257). When the old session ends, its IOCTL_REMOVE tears the monitor down OUT FROM UNDER the live session -> 'display disconnected' + the late E_INVALIDARG/MODE_CHANGE failures (output vanished mid-session) -> cascade. Fix: next_monitor_guid() returns a unique GUID per (process, session) [base GUID with low 48-bit node = pid<<16 | session#]; create() threads it into AddParams AND the keepalive (which REMOVEs by it). Each session now owns its own monitor; one ending can't kill another. (The 200ms DuplicateOutput1 retry confirmed working — 'succeeded on retry' logged; the residual failures were this collision, not the race.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,19 @@ const IOCTL_GET_VERSION: u32 = ctl(0x8FF);
|
|||||||
// A fixed monitor identity. One session at a time today; Windows persists this monitor's layout
|
// A fixed monitor identity. One session at a time today; Windows persists this monitor's layout
|
||||||
// across sessions by GUID, and REMOVE keys off it. (TODO: derive per-client when concurrent
|
// across sessions by GUID, and REMOVE keys off it. (TODO: derive per-client when concurrent
|
||||||
// sessions land.)
|
// sessions land.)
|
||||||
const MONITOR_GUID: GUID = GUID::from_u128(0x70756E6B_7466_756E_6B30_000000000001);
|
/// A UNIQUE-per-session SudoVDA monitor GUID. The monitor is keyed by GUID for IOCTL_ADD/REMOVE, so a
|
||||||
|
/// FIXED GUID makes overlapping sessions (a client reconnecting after a freeze before the old session
|
||||||
|
/// has torn down, or genuine concurrent sessions) all map to the SAME monitor — then one session's
|
||||||
|
/// IOCTL_REMOVE on teardown tears the monitor down OUT FROM UNDER a still-live session ("display
|
||||||
|
/// disconnected" sound + freeze, even with no context change — observed live). Make it unique per
|
||||||
|
/// (process, session): base GUID with the low 48-bit node = (pid << 16 | session#).
|
||||||
|
fn next_monitor_guid() -> GUID {
|
||||||
|
use std::sync::atomic::AtomicU32;
|
||||||
|
static N: AtomicU32 = AtomicU32::new(0);
|
||||||
|
let n = N.fetch_add(1, Ordering::Relaxed) as u128;
|
||||||
|
let pid = std::process::id() as u128;
|
||||||
|
GUID::from_u128(0x70756E6B_7466_756E_6B30_000000000000u128 | (pid << 16) | (n & 0xFFFF))
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@@ -664,11 +676,14 @@ impl VirtualDisplay for SudoVdaDisplay {
|
|||||||
let mut device_name = [0u8; 14];
|
let mut device_name = [0u8; 14];
|
||||||
let nm = b"punktfunk";
|
let nm = b"punktfunk";
|
||||||
device_name[..nm.len()].copy_from_slice(nm);
|
device_name[..nm.len()].copy_from_slice(nm);
|
||||||
|
// Unique GUID PER SESSION so overlapping sessions / client reconnects each own their own
|
||||||
|
// SudoVDA monitor — a stale session's REMOVE must never tear down a live session's monitor.
|
||||||
|
let session_guid = next_monitor_guid();
|
||||||
let add = AddParams {
|
let add = AddParams {
|
||||||
width: mode.width,
|
width: mode.width,
|
||||||
height: mode.height,
|
height: mode.height,
|
||||||
refresh: mode.refresh_hz,
|
refresh: mode.refresh_hz,
|
||||||
guid: MONITOR_GUID,
|
guid: session_guid,
|
||||||
device_name,
|
device_name,
|
||||||
serial: [0u8; 14],
|
serial: [0u8; 14],
|
||||||
};
|
};
|
||||||
@@ -802,7 +817,7 @@ impl VirtualDisplay for SudoVdaDisplay {
|
|||||||
}),
|
}),
|
||||||
keepalive: Box::new(SudoVdaKeepalive {
|
keepalive: Box::new(SudoVdaKeepalive {
|
||||||
device: device_raw,
|
device: device_raw,
|
||||||
guid: MONITOR_GUID,
|
guid: session_guid,
|
||||||
stop,
|
stop,
|
||||||
pinger: Some(pinger),
|
pinger: Some(pinger),
|
||||||
gdi_name,
|
gdi_name,
|
||||||
|
|||||||
Reference in New Issue
Block a user