Skip to content

fix: deliver pending utilityProcess messages before emitting exit - #54924

Merged
jkleinsc merged 3 commits into
mainfrom
claude/utility-process-drain-on-exit
Oct 14, 2026
Merged

jkleinsc merged 3 commits into
mainfrom
claude/utility-process-drain-on-exit

Conversation

@claude

@claude claude Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Requested by John Kleinschmidt · Slack thread

Description of Change

UtilityProcessWrapper::HandleTermination closed the parentPort connector before emitting 'exit', so messages the child had already posted but the main process had not dispatched yet were dropped. The connector dispatches one message per task, and the child's exit notification can already be queued ahead of those tasks, so a child that does process.parentPort.postMessage(...) a few times and then process.exit(0) loses everything after the first message. @ckerr found this while reviewing #54901, where a spec fixture needed an ack handshake to work around it. Now that #54901 has landed, this PR also removes that handshake from spec/fixtures/api/utility-process/api-net-spec.js and waitForUtilityResult.

This change dispatches whatever is already queued on the port before closing it, so those messages are emitted before 'exit'. It only reads messages that are already there and never blocks. Browser shutdown (Shutdown()) still closes the port without dispatching, because the child is still running at that point.

The new spec has the child post 20 messages and exit while the main thread is briefly blocked. On 46.0.0-nightly.20261014 it failed in all 6 runs that completed (only message 0 arrived before 'exit'; 2 more runs died to an unrelated vitest worker start failure in my container). I did not compile the C++ change locally, so CI is the first build of it.

Checklist

🤖 Generated with Claude Code

https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Release Notes

Notes: Fixed utilityProcess dropping messages posted with process.parentPort.postMessage() right before the process exits.


Generated by Claude Code

HandleTermination closed the parentPort connector before emitting 'exit',
so messages the child had posted but the browser had not dispatched yet
were dropped. The connector dispatches one message per task, and the exit
notification can already be queued ahead of those tasks, so a child that
posts several messages and then calls process.exit() lost all but the
first.

Dispatch whatever is already queued on the port before closing it. Browser
shutdown still closes the port without dispatching, since the child is
still running then.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS
@claude claude Bot added semver/patch backwards-compatible bug fixes target/42-x-y PR should also be added to the "42-x-y" branch. target/43-x-y PR should also be added to the "43-x-y" branch. target/44-x-y PR should also be added to the "44-x-y" branch. target/45-x-y PR should also be added to the "45-x-y" branch. labels Oct 14, 2026
@claude
claude Bot marked this pull request as ready for review October 14, 2026 20:57

@dsanders11 dsanders11 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude, since #54901 landed can you fold the change mentioned in https://github.com/electron/electron/pull/54901/changes#r4274263333 into this PR?

Comment thread spec/api-utility-process.spec.ts Outdated
Move the Atomics.wait() call in the utilityProcess message-before-exit
test into a documented sleepSync() spec helper.

With pending parentPort messages now dispatched before 'exit', the
api-net-spec.js fixture can post its result and exit right away again,
so remove the ack handshake that #54901 added as a workaround, along
with the error logging that covered the wait for the ack.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS
@claude

claude Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor Author

Addressed in 5b92d8c (after merging main to pick up #54901):

On 46.0.0-nightly.20261014 (which doesn't include the C++ fix), the utility-process api-net tests pass without the handshake, and the new test still fails as expected with sleepSync.


Generated by Claude Code

@claude
claude Bot requested a review from dsanders11 October 14, 2026 21:52
@jkleinsc
jkleinsc merged commit 4a802b0 into main Oct 14, 2026
77 of 78 checks passed
@jkleinsc
jkleinsc deleted the claude/utility-process-drain-on-exit branch October 14, 2026 22:59
@release-clerk

release-clerk Bot commented Oct 14, 2026

Copy link
Copy Markdown

Release Notes Persisted

Fixed utilityProcess dropping messages posted with process.parentPort.postMessage() right before the process exits.

@trop

trop Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

I was unable to backport this PR to "42-x-y" cleanly;
you will need to perform this backport manually.

@trop trop Bot removed the target/42-x-y PR should also be added to the "42-x-y" branch. label Oct 14, 2026
@trop

trop Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

I was unable to backport this PR to "43-x-y" cleanly;
you will need to perform this backport manually.

@trop

trop Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

I was unable to backport this PR to "45-x-y" cleanly;
you will need to perform this backport manually.

@trop trop Bot added needs-manual-bp/42-x-y and removed target/43-x-y PR should also be added to the "43-x-y" branch. labels Oct 14, 2026
@trop

trop Bot commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

I was unable to backport this PR to "44-x-y" cleanly;
you will need to perform this backport manually.

@trop trop Bot added needs-manual-bp/43-x-y needs-manual-bp/45-x-y and removed target/45-x-y PR should also be added to the "45-x-y" branch. target/44-x-y PR should also be added to the "44-x-y" branch. labels Oct 14, 2026
@trop

trop Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

claude[bot] has manually backported this PR to "43-x-y", please check out #54962

ckerr pushed a commit that referenced this pull request Oct 15, 2026
…4960)

fix: deliver pending utilityProcess messages before emitting exit (#54924)

* fix: deliver pending utilityProcess messages before emitting exit

HandleTermination closed the parentPort connector before emitting 'exit',
so messages the child had posted but the browser had not dispatched yet
were dropped. The connector dispatches one message per task, and the exit
notification can already be queued ahead of those tasks, so a child that
posts several messages and then calls process.exit() lost all but the
first.

Dispatch whatever is already queued on the port before closing it. Browser
shutdown still closes the port without dispatching, since the child is
still running then.


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

* test: extract sleepSync helper and drop utility-process ack handshake

Move the Atomics.wait() call in the utilityProcess message-before-exit
test into a documented sleepSync() spec helper.

With pending parentPort messages now dispatched before 'exit', the
api-net-spec.js fixture can post its result and exit right away again,
so remove the ack handshake that #54901 added as a workaround, along
with the error logging that covered the wait for the ack.


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

(cherry picked from commit 4a802b0)


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
@trop trop Bot added merged/44-x-y PR was merged to the "44-x-y" branch. and removed in-flight/44-x-y labels Oct 15, 2026
claude Bot pushed a commit that referenced this pull request Oct 15, 2026
HandleTermination closed the parentPort connector before emitting 'exit',
so messages the child had posted but the browser had not dispatched yet
were dropped. The connector dispatches one message per task, and the exit
notification can already be queued ahead of those tasks, so a child that
posts several messages and then calls process.exit() lost all but the
first.

Dispatch whatever is already queued on the port before closing it. Browser
shutdown still closes the port without dispatching, since the child is
still running then.

Also extract a sleepSync helper for the new spec, and drop the
utility-process ack handshake that #54901 added as a workaround, since
pending parentPort messages are now dispatched before 'exit'.

Manual backport of #54924. Conflicts resolved: the header only adds the
DispatchPendingMessages() declaration (43-x-y has no CloseStdioReadFds()),
and the spec imports sleepSync into 43-x-y's existing spec-helpers import.

(cherry picked from commit 4a802b0)
(cherry picked from commit 53d6d27)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS
@trop

trop Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

claude[bot] has manually backported this PR to "42-x-y", please check out #55000

codebytere pushed a commit that referenced this pull request Oct 15, 2026
…4962)

HandleTermination closed the parentPort connector before emitting 'exit',
so messages the child had posted but the browser had not dispatched yet
were dropped. The connector dispatches one message per task, and the exit
notification can already be queued ahead of those tasks, so a child that
posts several messages and then calls process.exit() lost all but the
first.

Dispatch whatever is already queued on the port before closing it. Browser
shutdown still closes the port without dispatching, since the child is
still running then.

Also extract a sleepSync helper for the new spec, and drop the
utility-process ack handshake that #54901 added as a workaround, since
pending parentPort messages are now dispatched before 'exit'.

Manual backport of #54924. Conflicts resolved: the header only adds the
DispatchPendingMessages() declaration (43-x-y has no CloseStdioReadFds()),
and the spec imports sleepSync into 43-x-y's existing spec-helpers import.

(cherry picked from commit 4a802b0)
(cherry picked from commit 53d6d27)


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Co-authored-by: Claude <noreply@anthropic.com>
@trop trop Bot added merged/43-x-y PR was merged to the "43-x-y" branch. and removed in-flight/43-x-y labels Oct 15, 2026
MarshallOfSound pushed a commit that referenced this pull request Oct 15, 2026
…5000)

HandleTermination closed the parentPort connector before emitting 'exit',
so messages the child had posted but the browser had not dispatched yet
were dropped. The connector dispatches one message per task, and the exit
notification can already be queued ahead of those tasks, so a child that
posts several messages and then calls process.exit() lost all but the
first.

Dispatch whatever is already queued on the port before closing it. Browser
shutdown still closes the port without dispatching, since the child is
still running then.

Also extract a sleepSync helper for the new spec, and drop the
utility-process ack handshake that #54901 added as a workaround, since
pending parentPort messages are now dispatched before 'exit'.

Manual backport of #54924. Conflicts resolved: the header only adds the
DispatchPendingMessages() declaration (42-x-y has no CloseStdioReadFds()).
The spec's sleepSync import merged cleanly into 42-x-y's existing
spec-helpers import.

(cherry picked from commit 4a802b0)
(cherry picked from commit 53d6d27)


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Co-authored-by: Claude <noreply@anthropic.com>
@trop trop Bot added merged/42-x-y PR was merged to the "42-x-y" branch. and removed in-flight/42-x-y labels Oct 15, 2026
claude Bot pushed a commit that referenced this pull request Oct 19, 2026
process.exit() does not wait for pending postMessage() writes, so a
utility child that exits right after posting can lose its message.
Restore the acknowledgement handshake that #54924 removed from the
api-net fixture and helper, and add it to the env-app and
js2c-code-cache fixtures, so those children only exit once the parent
has acknowledged their message.

The #54924 test no longer posts right before exiting. The child posts
its parentPort messages, then posts on a transferred MessagePortMain and
exits only when the parent replies there. parentPort and the port share
the same ordered Mojo channel, so all of the parentPort messages have
reached the parent when the port message does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS
jkleinsc pushed a commit that referenced this pull request Oct 20, 2026
process.exit() does not wait for pending postMessage() writes, so a
utility child that exits right after posting can lose its message.
Restore the acknowledgement handshake that #54924 removed from the
api-net fixture and helper, and add it to the env-app and
js2c-code-cache fixtures, so those children only exit once the parent
has acknowledged their message.

The #54924 test no longer posts right before exiting. The child posts
its parentPort messages, then posts on a transferred MessagePortMain and
exits only when the parent replies there. parentPort and the port share
the same ordered Mojo channel, so all of the parentPort messages have
reached the parent when the port message does.


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Co-authored-by: Claude <noreply@anthropic.com>
MarshallOfSound pushed a commit that referenced this pull request Oct 20, 2026
process.exit() does not wait for pending postMessage() writes, so a
utility child that exits right after posting can lose its message.
Restore the acknowledgement handshake that #54924 removed from the
api-net fixture and helper, and add it to the env-app and
js2c-code-cache fixtures, so those children only exit once the parent
has acknowledged their message.

The #54924 test no longer posts right before exiting. The child posts
its parentPort messages, then posts on a transferred MessagePortMain and
exits only when the parent replies there. parentPort and the port share
the same ordered Mojo channel, so all of the parentPort messages have
reached the parent when the port message does.


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
MarshallOfSound pushed a commit that referenced this pull request Oct 20, 2026
process.exit() does not wait for pending postMessage() writes, so a
utility child that exits right after posting can lose its message.
Restore the acknowledgement handshake that #54924 removed from the
api-net fixture and helper, and add it to the env-app and
js2c-code-cache fixtures, so those children only exit once the parent
has acknowledged their message.

The #54924 test no longer posts right before exiting. The child posts
its parentPort messages, then posts on a transferred MessagePortMain and
exits only when the parent replies there. parentPort and the port share
the same ordered Mojo channel, so all of the parentPort messages have
reached the parent when the port message does.


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
MarshallOfSound pushed a commit that referenced this pull request Oct 20, 2026
process.exit() does not wait for pending postMessage() writes, so a
utility child that exits right after posting can lose its message.
Restore the acknowledgement handshake that #54924 removed from the
api-net fixture and helper, and add it to the env-app and
js2c-code-cache fixtures, so those children only exit once the parent
has acknowledged their message.

The #54924 test no longer posts right before exiting. The child posts
its parentPort messages, then posts on a transferred MessagePortMain and
exits only when the parent replies there. parentPort and the port share
the same ordered Mojo channel, so all of the parentPort messages have
reached the parent when the port message does.


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
MarshallOfSound pushed a commit that referenced this pull request Oct 20, 2026
process.exit() does not wait for pending postMessage() writes, so a
utility child that exits right after posting can lose its message.
Restore the acknowledgement handshake that #54924 removed from the
api-net fixture and helper, and add it to the env-app and
js2c-code-cache fixtures, so those children only exit once the parent
has acknowledged their message.

The #54924 test no longer posts right before exiting. The child posts
its parentPort messages, then posts on a transferred MessagePortMain and
exits only when the parent replies there. parentPort and the port share
the same ordered Mojo channel, so all of the parentPort messages have
reached the parent when the port message does.


Claude-Session: https://claude.ai/code/session_01LPMvJBQiyP6b7QjatACmnS

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

46-x-y merged/42-x-y PR was merged to the "42-x-y" branch. merged/43-x-y PR was merged to the "43-x-y" branch. merged/44-x-y PR was merged to the "44-x-y" branch. merged/45-x-y PR was merged to the "45-x-y" branch. semver/patch backwards-compatible bug fixes

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants