Skip to content

MessagePort alignment with Nodejs - #19941

Closed
39ali wants to merge 5 commits into
oven-sh:mainfrom
39ali:message-port
Closed

MessagePort alignment with Nodejs #19941
39ali wants to merge 5 commits into
oven-sh:mainfrom
39ali:message-port

Conversation

@39ali

@39ali 39ali commented May 27, 2025

Copy link
Copy Markdown
Contributor

What does this PR do?

fixed 19863 and 19862

  • Documentation or TypeScript types (it's okay to leave the rest blank in this case)
  • Code changes

How did you verify your code works?

Comment thread src/bun.js/bindings/webcore/MessagePort.cpp Outdated
Comment thread src/bun.js/bindings/webcore/MessagePort.h Outdated

@Jarred-Sumner Jarred-Sumner left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If the RefPtr causes it to be destructed, it would be really easy to lead to a use-after-free.

Instead, we should increment/decrement the reference count while messages are in-flight. Enqueuing a message should increment the reference count of the MessagePort and processing that message should decrement it. To do that, we can use a protectedThis = Ref{ *this } in the lambda capture. That is the typical pattern WebKit uses for this sort of thing.

@39ali

39ali commented May 29, 2025

Copy link
Copy Markdown
Contributor Author

@Jarred-Sumner that won't work because we need to keep MessagePort alive unitl we call MessagePort::close() for example :

import { MessageChannel } from "node:worker_threads";

function main (){
    const { port1, port2 } = new MessageChannel();

port1.on("message", (msg) =>
  console.log("message received on port 1 via on:", msg),
);
 port2.postMessage("hey");

}

main()

this should never close or get GCed even if we got no messages left but it does in bun unless we keep Ref for MessagePort

another solution could be to keep std::optional<Ref<MessagePort>> and optional::reset() on close()

@39ali

39ali commented Jun 1, 2025

Copy link
Copy Markdown
Contributor Author

@Jarred-Sumner any update on this ?

if (auto port = JSMessagePort::toWrapped(vm, transferable.get())) {
if (port->isDetached())
return Exception { DataCloneError, "MessagePort in transfer list is already detached"_s };
port->dispatchCloseEvent();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The dispatchCloseEvent() call during transfer appears to be inconsistent with the MessagePort specification. According to the standard, MessagePorts should only dispatch 'close' events when explicitly closed via the close() method, not when transferred between contexts. Transferring a port detaches it from its original context but doesn't conceptually "close" it - it continues to exist in the target context. This behavior difference could cause compatibility issues with Node.js and other standard implementations.

Suggested change
port->dispatchCloseEvent();
port->detachWithoutClosing();

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

Comment on lines -430 to -431
if (listener->isAttribute())
start();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Please see my comment on actual issue. #19863 (comment)

I will emphasis that we should make WebCore implementation to align withe the JS specs and have that NodeJS compatibility layer abstracted away.

And until then just having documentation for such points would be suffice.

@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Thanks for working on this. Both issues this PR targeted have since been fixed on main by other changes:

Verified on current main (f426a8e): the #19863 script prints the output of both handlers without an explicit start(), and the #19862 worker script exchanges all four messages, emits close on port2 after terminate(), and exits on its own.

The files this PR touches have also moved since (src/bun.js/bindings/webcore is now src/jsc/bindings/webcore), so closing this rather than asking for a rebase. The one part of this change that is not on main is the close event on the port object that was transferred away (Node emits it); that is being tracked as its own bug.

@robobun robobun closed this Aug 13, 2026
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.

MessagePort does not transmit unless started explicitly

5 participants