From b94ad0f5ab6dfe27954800e96bc34c833401e584 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 2 Jan 2019 16:33:58 +0100 Subject: [PATCH 1/2] worker: enable transferring WASM modules Enable in-memory transfer of WASM modules without recompilation. Previously, the serialization step worked, but deserialization failed because we did not explicitly enable decoding inline WASM modules, and so the message was not successfully received. --- src/node_messaging.cc | 38 +++++++++++++++---- src/node_messaging.h | 4 ++ .../test-worker-message-port-wasm-module.js | 19 ++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) create mode 100644 test/parallel/test-worker-message-port-wasm-module.js diff --git a/src/node_messaging.cc b/src/node_messaging.cc index ae60187b6f3ec2..6036648dd4c527 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -30,6 +30,7 @@ using v8::String; using v8::Value; using v8::ValueDeserializer; using v8::ValueSerializer; +using v8::WasmCompiledModule; namespace node { namespace worker { @@ -43,13 +44,15 @@ namespace { // `MessagePort`s and `SharedArrayBuffer`s, and make new JS objects out of them. class DeserializerDelegate : public ValueDeserializer::Delegate { public: - DeserializerDelegate(Message* m, - Environment* env, - const std::vector& message_ports, - const std::vector>& - shared_array_buffers) - : message_ports_(message_ports), - shared_array_buffers_(shared_array_buffers) {} + DeserializerDelegate( + Message* m, + Environment* env, + const std::vector& message_ports, + const std::vector>& shared_array_buffers, + const std::vector& wasm_modules) + : message_ports_(message_ports), + shared_array_buffers_(shared_array_buffers), + wasm_modules_(wasm_modules) {} MaybeLocal ReadHostObject(Isolate* isolate) override { // Currently, only MessagePort hosts objects are supported, so identifying @@ -67,11 +70,19 @@ class DeserializerDelegate : public ValueDeserializer::Delegate { return shared_array_buffers_[clone_id]; } + MaybeLocal GetWasmModuleFromId( + Isolate* isolate, uint32_t transfer_id) override { + CHECK_LE(transfer_id, wasm_modules_.size()); + return WasmCompiledModule::FromTransferrableModule( + isolate, wasm_modules_[transfer_id]); + } + ValueDeserializer* deserializer = nullptr; private: const std::vector& message_ports_; const std::vector>& shared_array_buffers_; + const std::vector& wasm_modules_; }; } // anonymous namespace @@ -109,7 +120,8 @@ MaybeLocal Message::Deserialize(Environment* env, } shared_array_buffers_.clear(); - DeserializerDelegate delegate(this, env, ports, shared_array_buffers); + DeserializerDelegate delegate( + this, env, ports, shared_array_buffers, wasm_modules_); ValueDeserializer deserializer( env->isolate(), reinterpret_cast(main_message_buf_.data), @@ -143,6 +155,11 @@ void Message::AddMessagePort(std::unique_ptr&& data) { message_ports_.emplace_back(std::move(data)); } +uint32_t Message::AddWASMModule(WasmCompiledModule::TransferrableModule&& mod) { + wasm_modules_.emplace_back(std::move(mod)); + return wasm_modules_.size() - 1; +} + namespace { void ThrowDataCloneException(Environment* env, Local message) { @@ -202,6 +219,11 @@ class SerializerDelegate : public ValueSerializer::Delegate { return Just(i); } + Maybe GetWasmModuleTransferId( + Isolate* isolate, Local module) override { + return Just(msg_->AddWASMModule(module->GetTransferrableModule())); + } + void Finish() { // Only close the MessagePort handles and actually transfer them // once we know that serialization succeeded. diff --git a/src/node_messaging.h b/src/node_messaging.h index e4674885d2b89e..3c79e24f24b896 100644 --- a/src/node_messaging.h +++ b/src/node_messaging.h @@ -47,6 +47,9 @@ class Message : public MemoryRetainer { // Internal method of Message that is called once serialization finishes // and that transfers ownership of `data` to this message. void AddMessagePort(std::unique_ptr&& data); + // Internal method of Message that is called when a new WebAssembly.Module + // object is encountered in the incoming value's structure. + uint32_t AddWASMModule(v8::WasmCompiledModule::TransferrableModule&& mod); // The MessagePorts that will be transferred, as recorded by Serialize(). // Used for warning user about posting the target MessagePort to itself, @@ -65,6 +68,7 @@ class Message : public MemoryRetainer { std::vector> array_buffer_contents_; std::vector shared_array_buffers_; std::vector> message_ports_; + std::vector wasm_modules_; friend class MessagePort; }; diff --git a/test/parallel/test-worker-message-port-wasm-module.js b/test/parallel/test-worker-message-port-wasm-module.js new file mode 100644 index 00000000000000..43c12a8519981b --- /dev/null +++ b/test/parallel/test-worker-message-port-wasm-module.js @@ -0,0 +1,19 @@ +// Flags: --experimental-worker +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); + +const { Worker } = require('worker_threads'); +const wasmModule = new WebAssembly.Module(fixtures.readSync('test.wasm')); + +const worker = new Worker(` +const { parentPort } = require('worker_threads'); +parentPort.once('message', ({ wasmModule }) => { + const instance = new WebAssembly.Instance(wasmModule); + parentPort.postMessage(instance.exports.addTwo(10, 20)); +}); +`, { eval: true }); + +worker.once('message', common.mustCall((num) => assert.strictEqual(num, 30))); +worker.postMessage({ wasmModule }); From 3960521eb7348cda2d8162cf79b88ab02ee80d1f Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Fri, 4 Jan 2019 19:35:19 +0100 Subject: [PATCH 2/2] fixup! worker: enable transferring WASM modules --- .../test-worker-message-port-wasm-threads.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-worker-message-port-wasm-threads.js b/test/parallel/test-worker-message-port-wasm-threads.js index 6d4f21d728d1b3..891da80e30af36 100644 --- a/test/parallel/test-worker-message-port-wasm-threads.js +++ b/test/parallel/test-worker-message-port-wasm-threads.js @@ -32,15 +32,23 @@ assert(buffer instanceof SharedArrayBuffer); // stopped when we exit. const worker = new Worker(` const { parentPort } = require('worker_threads'); + + // Compile the same WASM module from its source bytes. const wasmSource = new Uint8Array([${wasmSource.join(',')}]); const wasmModule = new WebAssembly.Module(wasmSource); const instance = new WebAssembly.Instance(wasmModule); parentPort.postMessage(instance.exports.memory); + + // Do the same thing, except we receive the WASM module via transfer. + parentPort.once('message', ({ wasmModule }) => { + const instance = new WebAssembly.Instance(wasmModule); + parentPort.postMessage(instance.exports.memory); + }); `, { eval: true }); - worker.once('message', common.mustCall(({ buffer }) => { + worker.on('message', common.mustCall(({ buffer }) => { assert(buffer instanceof SharedArrayBuffer); worker.buf = buffer; // Basically just keep the reference to buffer alive. - })); + }, 2)); worker.once('exit', common.mustCall()); worker.postMessage({ wasmModule }); }