Skip to content

shell: serve forwarded launches as soon as they can be served - #631

Open
deblasis wants to merge 1 commit into
windowsfrom
single-instance-serve-earlier
Open

shell: serve forwarded launches as soon as they can be served#631
deblasis wants to merge 1 commit into
windowsfrom
single-instance-serve-earlier

Conversation

@deblasis

Copy link
Copy Markdown
Owner

Addresses the main finding in # 629.

A secondary reaches its forward at App.xaml.cs:520 and gives the primary 2s to answer. The primary did not start listening until :793, the very end of the same method, so most of that budget was spent waiting through session restore, MainWindow construction and Activate, the quake window's HWND, and the hooks -- the same region the launch splash exists to cover.

This starts the server as soon as a forwarded launch can actually be serviced: immediately after the bootstrap host exists.

Why that exact line

It cannot go earlier. OpenWindowFromLaunch drops a request outright if _configService, _loggerFactory, _lifetimeSupervisor or _bootstrapHost is still null, and the last of those is the line above the new call site. Listening before that would turn a visible failure (the secondary cannot connect, so it opens its own window) into a silent one (it connects, exits, and its launch is discarded with no window and no message).

It does not need to go earlier. Everything expensive is below. The callback is _uiDispatcher?.TryEnqueue(...) and _uiDispatcher is set at :633, so an inbound forward is queued to a UI thread still inside OnLaunched and runs once it returns -- no deferral queue needed.

That silent drop now logs at Error. It is unreachable as things stand, but it is the one failure that costs a user their launch with nothing to show for it.

Measured

just splash-race -DelayMs 0, six iterations each, same machine, Debug build. SecondaryExitMs is how long the secondary took to forward and exit.

SecondaryExitMs mean range
before 3944, 4550, 5260, 4429, 4487, 3944 4516 1316
after 3037, 3033, 2909, 2684, 3119, 3130 2985 446

About 1.5s of the 2s budget reclaimed. The variance collapse is the more telling half: the secondary's exit no longer depends on the primary's progress at all.

What this does not claim

It does not fix a failure that reproduces on HEAD. On current windows on this machine, forwarding already succeeds 10/10 at 300ms spacing and 6/6 at 0ms. I saw outright failures while developing # 626, but on a tree predating # 627 and # 628 and with a colder cache, and I could not reproduce them here. So this lands as margin and latency, not as a fix for a live defect: it takes the remaining headroom from roughly half a second to essentially the whole budget, which is what a session restore with several windows, a cold cache or a slower disk would otherwise eat.

It does not close # 629. The acknowledgement problem noted there is untouched, and this change slightly widens it: a secondary treats a successful pipe write as success, so if the primary now dies between the new call site and the end of OnLaunched, that launch is lost where previously the secondary would have failed to connect and opened its own window. Fixing that means the secondary waiting for confirmation the window opened, which is a larger change.

Verification

  • Ghostty.Tests 1884 passed / 0 failed, Ghostty.Tests.Windows 28 passed / 0 failed
  • just splash-race 10/10 at 300ms and 6/6 at 0ms, no secondary splash, every iteration scored

@deblasis
deblasis force-pushed the single-instance-serve-earlier branch from 5baa2b2 to 4aba95a Compare August 14, 2026 07:10
A secondary reaches its forward near the top of OnLaunched and gives the
primary 2s to answer. The primary did not start listening until the very
end of the same method, so most of that budget was spent waiting through
session restore, MainWindow construction and Activate, the quake window's
HWND, and the hooks -- the same region the launch splash exists to cover.

Measured with `just splash-race -DelayMs 0`, six iterations, Debug: the
secondary took a mean 4516ms to forward and exit, against 2985ms with the
server started earlier. The primary's tail was consuming about 1.5s of the
2s, leaving roughly half a second of margin for a session restore with
several windows, a cold cache, or a slower disk to eat.

Start the server as soon as a forwarded launch can actually be serviced,
which is immediately after the bootstrap host exists.

It cannot go earlier. OpenWindowFromLaunch drops a request outright if
_configService, _loggerFactory, _lifetimeSupervisor or _bootstrapHost is
still null, and the last of those is the line above the new call site.
Listening before that would turn a visible failure -- the secondary cannot
connect, so it opens its own window -- into a silent one, where it
connects, exits, and its launch is discarded. It does not need to go
earlier either: everything expensive is below.

That drop now logs. It is unreachable as things stand, but it is the one
failure that costs a user their launch with no window and no message.

Does not close # 629 on its own: the acknowledgement problem noted there
is untouched. A secondary still treats a successful pipe write as success,
so a primary that dies before opening the window loses that launch.
@deblasis
deblasis force-pushed the single-instance-serve-earlier branch from 4aba95a to c3b410b Compare August 14, 2026 09:34
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