Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

postMessage shouldn't make a copy of the serialized buffer #15129

Open
andreubotella opened this issue Jul 8, 2022 · 8 comments
Open

postMessage shouldn't make a copy of the serialized buffer #15129

andreubotella opened this issue Jul 8, 2022 · 8 comments
Labels
bug Something isn't working correctly deno_core Changes in "deno_core" crate are needed

Comments

@andreubotella
Copy link
Contributor

andreubotella commented Jul 8, 2022

The postMessage method (in workers, MessagePort and BroadcastChannel) first serializes the passed objects into an ArrayBuffer (using Deno.core.serialize), and then an op is called which sends it across the underlying communications channel. However, for all of these, the op which sends the buffer with the serialized data takes it as a ZeroCopyBuf, which (as its name indicates) doesn't create a copy of the data before calling the op, but these operations do copy the data in order to send it as an owned Vec<u8>. This ultimately results in such buffers having copies in the V8 heaps for both isolates.

However, since #14102, serde_v8 has the DetachedBuffer type, which takes ownership of an ArrayBuffer by detaching it from the original isolate, and which can be "serialized" to create a new ArrayBuffer in a different isolate with the same backing memory – that is, it allows transferring the buffer in the structured clone sense. We should use this for workers and MessagePort.

For BroadcastChannel it's more complicated because, for any other instances of BroadcastChannel with the same name in the current isolate, a task will be queued which deserializes the buffer, and then the buffer is sent to other isolates. So the buffer can't be detached until all such tasks have run. One solution for this might be to deserialize for each instance before queuing the corresponding taks.

andreubotella added a commit to andreubotella/deno that referenced this issue Jul 9, 2022
This commit uses `DetachedBuffer` instead of `ZeroCopyBuf` in the ops
that back `Worker.prototype.postMessage` and
`MessagePort.prototype.postMessage`. This is done because the
serialized buffer is then copied to the destination isolate, even
though it is internal to runtime code and not used for anything else,
so detaching it and transferring it instead saves an unnecessary copy.

Towards denoland#15129.
@andreubotella
Copy link
Contributor Author

Come to think about it, this is even more complicated for BroadcastChannel, since workers and MessagePort are 1-to-1 communications, so the buffer can be transferred to the new isolate. Whereas BroadcastChannel is 1-to-n, and even if we transfer it, (n-1) copies would be needed.

@kitsonk kitsonk added bug Something isn't working correctly deno_core Changes in "deno_core" crate are needed labels Jul 10, 2022
@MierenManz
Copy link

Wouldn't a conditional copy work? Where if the receiver is a worker it will not make a copy and otherwise make n - 1 copies?

@sevapp
Copy link

sevapp commented Jan 24, 2023

Simple example:

// main file
const workers = [
  new Worker(new URL("./worker.ts", import.meta.url).href, {
    type: "module",
  }),
  new Worker(new URL("./worker.ts", import.meta.url).href, {
    type: "module",
  }),
];

workers.forEach((worker) => {
  worker.onmessage = (evt) => {
    console.log("PARENT: received message from child", evt.data);
  };
});

setInterval(() => {
  workers.forEach((worker) => {
    for (let i = 10000; i--;) {
      worker.postMessage({ data: "hello from parent!" });
    }
  });
}, 0);
// worker
self.onmessage = (evt) => {
  console.log("CHILD: received message from parent", evt.data);
  self.postMessage({ data: "hello from child!" });
};

After 20-30 seconds of work (mbp 2021 on m1 max):
Screenshot 2023-01-24 at 8 57 24 PM

@sevapp
Copy link

sevapp commented Jan 24, 2023

Unfortunately, I do not know how to fix this problem, but my work depends very much on it =(

@andreubotella
Copy link
Contributor Author

The specific bug that I raised this issue for, related to detaching the buffer with the serialized message on the original thread, was fixed in #15133 for the case of workers. That is still an issue for BroadcastChannel, but it's more complicated to resolve (see #15129 (comment)).

This remaining BroadcastChannel bug should not affect postMessage on workers, though. And while there does seem to be a memory leak, the serialized buffers do seem to be being detached, at least on the sending thread. So that memory leak is not due to this bug.

@sevapp
Copy link

sevapp commented Jan 24, 2023

The specific bug that I raised this issue for, related to detaching the buffer with the serialized message on the original thread, was fixed in #15133 for the case of workers. That is still an issue for BroadcastChannel, but it's more complicated to resolve (see #15129 (comment)).

This remaining BroadcastChannel bug should not affect postMessage on workers, though. And while there does seem to be a memory leak, the serialized buffers do seem to be being detached, at least on the sending thread. So that memory leak is not due to this bug.

What is the course of action in this case?)

@andreubotella
Copy link
Contributor Author

What is the course of action in this case?)

File a new bug, unrelated to detaching buffers.

@sevapp
Copy link

sevapp commented Jan 24, 2023

What is the course of action in this case?)

File a new bug, unrelated to detaching buffers.

Ok, complete issue 17517

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working correctly deno_core Changes in "deno_core" crate are needed
Projects
None yet
Development

No branches or pull requests

4 participants