Repository navigation
perf(runtime): skip unread BroadcastChannel deliveries and let idle channels be collected - #2086
Draft
edusperoni wants to merge 1 commit into
Draft
edusperoni wants to merge 1 commit into
edusperoni wants to merge 1 commit into
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
…hannels be collected Every open BroadcastChannel deserialized every message posted on its name and dispatched two MessageEvents (one on the hidden port, one re-wrapped by a relay onto the channel), whether or not anything listened. A channel was also held until close(), so fire-and-forget senders accumulated as group members that each paid that cost on every later post. - The channel reports its own 'message'/'messageerror' listener count through the kListenerChanged hook. While it has one, native holds the channel strongly as the port's delivery target and dispatches a single MessageEvent on it; without one, a drained message is discarded unread. The decision is made per message at delivery, so a listener attached in the same turn as the post still receives it. - That strong reference is the only one, matching HTML's GC rule: an open channel with no listener is collectable, and a FinalizationRegistry closes its port so it leaves the group. - Group members are kept in join order, so destinations on one isolate are served in channel creation order as HTML specifies, rather than pointer order. - A port that has not been started is no longer woken for each incoming burst; only its close sentinel wakes it. Mirrors NativeScript/ios#543.
edusperoni
force-pushed
the
perf/broadcast-channel-idle-channels
branch
from
November 5, 2026 20:33
8f59fc9 to
71719d1
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Android counterpart of NativeScript/ios#543, kept in sync with it.
What
Every open
BroadcastChanneldeserialized every message posted on its name and dispatched twoMessageEvents, even with no listener. The first went on the hidden port, and a relay listener re-wrapped it onto the channel. A channel was also held strongly untilclose(). Fire-and-forget senders such asnew BroadcastChannel(n).postMessage(x)therefore stayed as group members forever, and each paid that cost on every later post.Changes
message/messageerrorlistener count through thekListenerChangedhook, the same one MessagePort uses. While it has a listener, native holds the channel as the port's delivery target and dispatches oneMessageEventdirectly on it. Without a listener, a drained message is discarded unread: no deserialization, no event, no microtask checkpoint.onmessageis attached in the same turn is still delivered, as in browsers.FinalizationRegistrycloses its port so it leaves the group.MessagePortthat hasn't been started is no longer woken for every incoming burst; only its close sentinel wakes it.The native side is the same change as iOS, ported onto Android's
Messaging.cpp.broadcast-channel.jsis now byte-identical across both runtimes.Numbers
Measured on iOS (Release, simulator) with the same native design. I didn't benchmark Android separately.
postMessageitself.Deliberate differences
close()/unref(). This runtime now follows the browsers and HTML instead.Proxyaround a channel reaches the channel's listener list, but the hook sees the proxy and cannot reach the port. Messages are then discarded.Out of scope
Tests
test-app/app/src/main/assets/app/tests/testMessaging.jsgains the same 10 BroadcastChannel specs as iOS:messageerror-only listener keeps the channel aliveFull suite on
main+ this commit, on a Pixel 9 Pro API 35 arm64 emulator: 1478 specs, 0 failures, 4 skipped. The skips are unrelated to messaging.