Skip to content

fix(windows): eq lifecycle — apo bypass on daemon exit, profile auto-apply, console flashes#65

Open
ealtun21 wants to merge 9 commits into
masterfrom
worktree-win-lifecycle-fixes
Open

fix(windows): eq lifecycle — apo bypass on daemon exit, profile auto-apply, console flashes#65
ealtun21 wants to merge 9 commits into
masterfrom
worktree-win-lifecycle-fixes

Conversation

@ealtun21

@ealtun21 ealtun21 commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Fixes three Windows tester bugs:

1. EQ kept running after quitting from the tray

The DSP lives in the APO inside audiodg, fed by a shared state file; killing the daemon left the last EQ snapshot active forever.

  • Graceful layer: the daemon now publishes an enabled=0 bypass snapshot on every graceful shutdown (IPC Shutdown, SIGTERM/Ctrl-C), and Windows service::stop() tries graceful IPC first (taskkill only as fallback).
  • Crash-safe layer: the daemon heartbeats the shared file every 30 ms; the APO worker auto-bypasses when the heartbeat stalls ≥2 s (taskkill /f, crash, hang) and rebuilds when it resumes (suspend/resume safe). STATE_VERSION 9→10 — manual deployers must update daemon + resonance_apo.dll together and restart the audio service (the installer already does this).
  • Tray "Quit Resonance" now closes GUI + daemon + tray per quit_stops_daemon (default on); "Quit tray" (setting off) exits only the tray.

2. Device-mapped profile not applied after daemon restart

The cross-platform output-mapping task was never fed on Windows (output_tx dropped; no audio backend). The device-watch thread now reports the default render device, so the mapped profile auto-applies at startup and on device change.

3. PowerShell window flash at startup

The GUI-subsystem daemon spawned powershell.exe (APO endpoint attach) without CREATE_NO_WINDOW; same for the quit-path taskkill. Both flagged.

Verification

  • Live on the Win10 VM (audiodg): graceful shutdown → snapshot_enabled=0 + daemon gone; taskkill /f during playback → apo.log daemon heartbeat stale - forcing bypass within ~2 s, enabled=false on subsequent blocks; daemon restart → profile vmtest auto-applied + enabled=true. Full cycle repeated 3×.
  • In-guest cargo test -p resonance-apo: 35/35 (incl. new heartbeat-watch + three-phase recovery harness tests).
  • make check green; cargo clippy --target x86_64-pc-windows-msvc clean across apo/ipc/daemon/tray.
  • Needs eyes-on-tester confirmation (not automatable over ssh): absence of the console flash; tray menu click paths.

ealtun21 added 9 commits July 9, 2026 15:24
Track the stale->live heartbeat transition and invalidate the worker's
latched generation so the existing rebuild path re-reads the daemon's
snapshot on the next tick. Previously the worker only ever forced
enabled=false on staleness; nothing rebuilt on recovery, so the APO
stayed bypassed until the daemon's next publish (which may never come,
e.g. after a suspend/resume or the startup beat-before-publish race).

Also swap two em-dashes for ASCII hyphens in log strings that render
as mojibake in the ANSI apo.log, clean up leftover temp files in two
state.rs tests, and add a worker-level harness test proving the full
stale-bypass-then-recover cycle through the real FFI exports.
publish_apo_bypass ran on the SIGTERM/Ctrl-C signal path with a plain
lock().unwrap(); a poisoned mutex there would panic the signal task
and eat shutdown forever. Recover the guard via PoisonError::into_inner
instead.

set_apo_writer now beats immediately after its initial publish, so
liveness lands together with the first snapshot (belt-and-braces
against the startup race the APO side now guards against). Also note
in pump_telemetry's doc that it doubles as the APO's liveness signal
and must keep running with no clients connected.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant