Skip to content

fix(config): an unreadable ini is never reset to defaults (#377) - #379

Open
Maxaubert wants to merge 1 commit into
developfrom
fix/377-ini-unreadable-reset
Open

Maxaubert wants to merge 1 commit into
developfrom
fix/377-ini-unreadable-reset

Conversation

@Maxaubert

Copy link
Copy Markdown
Owner

Closes #377.

What happened

Dragging the tray's Night light slider while zoomed left the zoom stuck, and the next start opened the first-launch setup. The core had reloaded the factory defaults: zoom buttons unbound, onboarded=0.

Cause

Writers replace magnifier.ini atomically, but around each MoveFileExW the name briefly refuses opens. Measured on the dev PC: ~1% of reads during a burst of replaces failed with ERROR_ACCESS_DENIED. LoadConfig took a failed open for a missing file and wrote the defaults over the user's ini. ReadTextFile returned "" to read-modify-write callers.

Fix

  • ReadTextFileOk and WriteTextFileAtomic retry sharing and access errors until a deadline: 250 ms by default, 20 ms on the core's tick thread. Reads now share delete, so they never block a replace.
  • LoadConfig writes the defaults only for a missing file. TryLoadConfig reports an unreadable one.
  • The hot-reload keeps the running settings on an unreadable ini and re-checks on the next poll (config ini unreadable on reload). A deleted ini is still recreated.
  • New ReadLiveIni guards the read-modify-write writers: setConfig, profile rename/switch (settings app and tray), the engine revert and the first-run profile seed.
  • Docs: 02, 08, plus a CLAUDE.md gotcha.

Verification

  • Unit suite: 592/592. build.bat check and build.bat tray are clean (only pre-existing warnings).
  • Deterministic repro (C:\RE harness ini_lock_test.py): change the ini, then hold it open with no sharing for 1.5 s, zooming in and out during and after the lock.
    • Unfixed develop: zoom dead (raw X-button events, no inHeld). This is the field symptom.
    • This branch: one ini unreadable warning, the zoom works during the lock (press to tick 0.08 ms, no stall) and after, and the ini is intact.
  • Race stress (ini_race_test.py): 870 read-modify-write replaces in 25 s while zooming. The ini kept every key, all zoom cycles worked, and no writes failed.
  • Deployed (signed UIAccess) on the dev PC.

Not version-bumped: develop PRs ride the bump that comes with the develop to main merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_011zPmivSAQeivGUdBjTsaMu

Writers replace magnifier.ini atomically, but around each MoveFileEx the name briefly
refuses opens (measured: ~1% of reads during a burst of replaces failed with
ERROR_ACCESS_DENIED). LoadConfig treated that as a missing file and wrote the defaults
over the user's ini; read-modify-write callers got "" from ReadTextFile. Field: a tray
Night light drag mid-zoom unbound the zoom keys (stuck zoom) and set onboarded=0 (setup
at the next start).

- ReadTextFileOk / WriteTextFileAtomic retry sharing and access errors until a deadline
  (250 ms; the tick thread reads with 20 ms), and reads share delete.
- LoadConfig creates the defaults only for a missing file; TryLoadConfig reports failure.
- The hot-reload keeps the running settings on an unreadable ini and re-checks next poll.
- ReadLiveIni guards the read-modify-write writers (settings app, tray, profile seed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011zPmivSAQeivGUdBjTsaMu
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