fix(host): crash visibility + Windows deliberate-quit teardown; pin outranks quit

Two strands from the "Windows host went Offline after reconnect, zero errors in
the logs" investigation.

Crash visibility — the field reports kept arriving with nothing in the Logs
tab because every way the host can die silently was unlogged:
- A panic hook tees each panic (thread, location, backtrace) through `tracing`
  into the in-memory ring + host.log before the default hook runs — a panicking
  thread otherwise only hit stderr, absent from the web console and gone when
  stderr is detached.
- A last-resort Windows SEH filter (windows/crash.rs) logs an unhandled native
  exception with its code, faulting address, and faulting MODULE — the smoking
  gun that separates our bug from a GPU runtime/driver DLL (amfrt64, the UMD,
  d3d11) crashing under us.
- A 10 s watchdog around the vdisplay monitor teardown logs an ERROR when the
  driver REMOVE/CCD-restore looks wedged (it runs under the manager state lock,
  so a hang there silently blocks every future acquire).

Deliberate-quit teardown (Windows pf-vdisplay) — the Windows manager never
wired the linger skip the Linux registry has: on ⌘D (the QUIT close code) the
monitor lingered 10 s, so a quick reconnect hit the Lingering-preempt's
back-to-back REMOVE→ADD churn. A per-lease quit flag (VirtualDisplay::
set_quit_flag) now tears the monitor down immediately on a deliberate quit, so
the reconnect finds the manager Idle and does a clean fresh ADD.

Pin outranks quit (both platforms) — keep_alive=forever (the gaming-rig preset)
promises "the screen stays alive", so a deliberate quit must skip only the
linger WINDOW, never the pin. Windows release() checks keep_alive_forever()
before the quit; the Linux registry had the inverse bug (force_immediate tore
down even a pinned display) — fixed via effective_linger() with a unit test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 14:16:19 +02:00
parent ab6790ef6c
commit 0b7e4a00ee
8 changed files with 276 additions and 20 deletions
+7
View File
@@ -3162,6 +3162,13 @@ fn virtual_stream(ctx: SessionContext) -> Result<()> {
// reapplies the client's saved per-monitor config (DPI scaling) on reconnect. No-op on Linux backends
// and for anonymous/GameStream clients (no fingerprint → the driver auto-allocates).
vd.set_client_identity(endpoint::peer_fingerprint(&conn));
// Deliberate-quit wiring (Windows pf-vdisplay; no-op elsewhere): every lease the backend mints —
// the retry-hold below AND the capturer's — carries the session's quit flag, so a user "stop"
// (⌘D → the QUIT close code) tears the virtual monitor down the moment the pipeline drops instead
// of lingering 10 s. The reconnect then finds the manager Idle and does a clean fresh ADD (with
// the user's think-time as driver settle) rather than the Lingering-preempt's REMOVE→ADD churn.
// `keep_alive = forever` (gaming-rig) outranks the quit — the monitor pins as before.
vd.set_quit_flag(quit.clone());
// Per-session launch (non-Windows): hand the resolved command to the backend instance so
// gamescope's bare spawn nests it — per-instance, no process-global env, so concurrent sessions
// can't stomp each other's launch target. The other backends' default `set_launch_command` is a