Description
- Version: v11.8.0
- Platform: Arch Linux 4.20.4-arch1-1-ARCH deps: update openssl to 1.0.1j #1 SMP PREEMPT Wed Jan 23 00:12:22 UTC 2019 x86_64 GNU/Linux
- Subsystem: worker_threads
I'm currently creating a worker_threads
compatability library which seeks to replicate worker_threads
behavior as accurately as possible using the child_process
module. This has caused me to investigate a number of edge cases with the worker_threads
module.
My own implementation question was something like this:
worker.postMessage(port1, [port1, port1]);
What should happen? Should postMessage
throw or simply ignore the second entry in the transfer list? It turns out node.js does neither.
Example case:
const threads = require('worker_threads');
if (threads.isMainThread) {
const worker = new threads.Worker(__filename);
const {port1, port2} = new threads.MessageChannel();
port1.on('message', console.log);
// Causes a segfault:
worker.postMessage(port2, [port2, port2]);
// Causes a fatal error:
const buf = new Uint8Array(1);
worker.postMessage(buf, [buf.buffer, buf.buffer]);
} else {
threads.parentPort.on('message', (port) => {
if (port instanceof threads.MessagePort)
port.postMessage('hello');
});
}
It seems that a duplicate port in the transfer list causes a segfault (I'm sorry I don't currently have a debug build available to provide a stack trace).
Transferring a duplicate array buffer causes a fatal error (at least a bit more descriptive):
FATAL ERROR: v8_ArrayBuffer_Externalize ArrayBuffer already externalized
1: 0x55fefb3fe881 node::Abort() [node]
2: 0x55fefb400238 node::OnFatalError(char const*, char const*) [node]
3: 0x55fefb5917cb v8::Utils::ReportApiFailure(char const*, char const*) [node]
4: 0x55fefb5af63b v8::ArrayBuffer::Externalize() [node]
5: 0x55fefb448747 node::worker::Message::Serialize(node::Environment*, v8::Local<v8::Context>, v8::Local<v8::Value>, v8::Local<v8::Value>, v8::Local<v8::Object>) [node]
6: 0x55fefb44923f node::worker::MessagePort::PostMessage(node::Environment*, v8::Local<v8::Value>, v8::Local<v8::Value>) [node]
7: 0x55fefb449608 node::worker::MessagePort::PostMessage(v8::FunctionCallbackInfo<v8::Value> const&) [node]
8: 0x55fefb606dcf [node]
9: 0x55fefb608307 [node]
10: 0x5ac0f1cfc5d
Aborted (core dumped)
Update:
After some more investigating, it looks like the browser throws a DataCloneError
in both of these edge cases.
Duplicate port:
Uncaught DataCloneError: Failed to execute 'postMessage' on 'Worker': Message port at index 1 is a duplicate of an earlier port.
Duplicate array buffer:
Uncaught DataCloneError: Failed to execute 'postMessage' on 'Worker': ArrayBuffer at index 1 is a duplicate of an earlier ArrayBuffer.