Skip to content

Back off between QMP startup validation attempts - #228

Closed
NimbleAINinja wants to merge 1 commit into
omacom:mainfrom
NimbleAINinja:fix-qmp-startup-backoff
Closed

NimbleAINinja wants to merge 1 commit into
omacom:mainfrom
NimbleAINinja:fix-qmp-startup-backoff

Conversation

@NimbleAINinja

@NimbleAINinja NimbleAINinja commented Sep 18, 2026

Copy link
Copy Markdown

What this changes

QMPVMHostSleepController.init validated QEMU's monitor with three connection attempts issued back to back with no wait between them. The existing comment already acknowledged that "the launcher sees the socket before QEMU finishes initializing its monitor" — but three attempts inside the same instant do not give it any time to finish. This spaces them: five attempts at 0, 50, 100, 200 and 400 ms, bounded at 750 ms, spent only on the failure path.

A monitor that never answers still fails startup with the same error, so a broken monitor is not mistaken for a slow one.

Honest scope

This was written while investigating a startup failure:

The virtual machine started, but Try Omarchy could not enable safe Mac sleep. Please close and reopen the app. (I/O failure: cannot connect to QMP socket: Connection refused)

It did not fix that failure, which recurred with this change installed. The symptom appears only on the first launch after installing a new build and has not reproduced since (four subsequent launches, via both the terminal and LaunchServices, all succeeded), so the underlying cause is still unidentified — a persistent ECONNREFUSED suggests nothing was listening on the socket at all, rather than a monitor that was merely slow to accept.

I am offering this only as a correctness improvement to an obviously unpaced retry loop, not as a fix for that bug. Happy to close it if you would rather wait until the real cause is known.

Worth noting for whoever picks that up: when validation fails, VMApplicationController.failHostSleepControlSetup sends QEMU SIGTERM, so a false negative here destroys a running VM and leaves an empty guest console log.

The wait blocks the main thread. That is confined to the retry path, but it is why the total is kept under a second; happy to move it off the main thread instead.

Validation

  • make test passes.
  • Updated the retry-limit test to assert the attempt count and the delay schedule.
  • Added a test covering a monitor that refuses twice before accepting.

🤖 Generated with Claude Code

Launching could fail with "The virtual machine started, but Try Omarchy
could not enable safe Mac sleep ... cannot connect to QMP socket:
Connection refused", leaving an empty guest console log. Reopening the
app succeeded, and the failure was most likely right after installing a
new build.

run-qemu-gpu.sh reports the VM ready once the QMP socket file exists,
which is before QEMU's monitor accepts connections and completes its
greeting. The controller validated that monitor with three connection
attempts issued back to back with no wait between them, so all three
could land inside the same short window while the VM was perfectly
healthy. The helper treats that as a startup failure and sends QEMU
SIGTERM, which is why the guest console log was empty: the VM was killed
moments after it began booting.

Space the attempts instead: five in total at 0, 50, 100, 200 and 400 ms.
The wait is only spent on the failure path, it is bounded at 750 ms, and
a monitor that never answers still fails startup with the same error, so
a genuinely broken monitor is not mistaken for a slow one. The wait is
injected so the tests assert the schedule without spending it.

The sleep blocks the main thread. That is confined to the retry path,
where the alternative is destroying a running VM, but it is the reason
the total is kept under a second rather than extended further.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@NimbleAINinja

Copy link
Copy Markdown
Author

Closing in favour of #229, which fixes the actual cause. The failure was the launcher printing Ready before QEMU's main loop was accepting on its backlog-1 QMP socket; once a probe times out there, the queue stays full and every retry is refused regardless of pacing, so this backoff cannot help. Details and reproduction in #229.

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