worker: add `SharedArrayBuffer` sharing · nodejs/node@d1f372f · GitHub
Skip to content

Commit d1f372f

Browse files
addaleaxtargos
authored andcommitted
worker: add SharedArrayBuffer sharing
Logic is added to the `MessagePort` mechanism that attaches hidden objects to those instances when they are transferred that track their lifetime and maintain a reference count, to make sure that memory is freed at the appropriate times. Thanks to Stephen Belanger for reviewing this change in its original PR. Refs: ayojs/ayo#106 PR-URL: #20876 Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Shingo Inoue <leko.noor@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: John-David Dalton <john.david.dalton@gmail.com> Reviewed-By: Gus Caplan <me@gus.host>
1 parent f447acd commit d1f372f

8 files changed

Lines changed: 274 additions & 9 deletions

File tree

doc/api/worker.md

Lines changed: 11 additions & 4 deletions

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@
352352
'src/node_i18n.cc',
353353
'src/pipe_wrap.cc',
354354
'src/process_wrap.cc',
355+
'src/sharedarraybuffer_metadata.cc',
355356
'src/signal_wrap.cc',
356357
'src/spawn_sync.cc',
357358
'src/string_bytes.cc',
@@ -411,6 +412,7 @@
411412
'src/udp_wrap.h',
412413
'src/req_wrap.h',
413414
'src/req_wrap-inl.h',
415+
'src/sharedarraybuffer_metadata.h',
414416
'src/string_bytes.h',
415417
'src/string_decoder.h',
416418
'src/string_decoder-inl.h',

src/env.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ struct PackageConfig {
106106
V(decorated_private_symbol, "node:decorated") \
107107
V(napi_env, "node:napi:env") \
108108
V(napi_wrapper, "node:napi:wrapper") \
109+
V(sab_lifetimepartner_symbol, "node:sharedArrayBufferLifetimePartner") \
109110

110111
// Symbols are per-isolate primitives but Environment proxies them
111112
// for the sake of convenience.
@@ -338,6 +339,7 @@ struct PackageConfig {
338339
V(promise_wrap_template, v8::ObjectTemplate) \
339340
V(push_values_to_array_function, v8::Function) \
340341
V(randombytes_constructor_template, v8::ObjectTemplate) \
342+
V(sab_lifetimepartner_constructor_template, v8::FunctionTemplate) \
341343
V(script_context_constructor_template, v8::FunctionTemplate) \
342344
V(script_data_constructor_function, v8::Function) \
343345
V(secure_context_constructor_template, v8::FunctionTemplate) \

src/node_errors.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace node {
3131
V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, TypeError) \
3232
V(ERR_MISSING_MODULE, Error) \
3333
V(ERR_STRING_TOO_LONG, Error) \
34+
V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \
3435

3536
#define V(code, type) \
3637
inline v8::Local<v8::Value> code(v8::Isolate* isolate, \
@@ -60,7 +61,9 @@ namespace node {
6061
V(ERR_INVALID_TRANSFER_OBJECT, "Found invalid object in transferList") \
6162
V(ERR_MEMORY_ALLOCATION_FAILED, "Failed to allocate memory") \
6263
V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, \
63-
"MessagePort was found in message but not listed in transferList")
64+
"MessagePort was found in message but not listed in transferList") \
65+
V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, \
66+
"Cannot serialize externalized SharedArrayBuffer") \
6467

6568
#define V(code, message) \
6669
inline v8::Local<v8::Value> code(v8::Isolate* isolate) { \

src/node_messaging.cc

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ using v8::Maybe;
2424
using v8::MaybeLocal;
2525
using v8::Nothing;
2626
using v8::Object;
27+
using v8::SharedArrayBuffer;
2728
using v8::String;
2829
using v8::Value;
2930
using v8::ValueDeserializer;
@@ -43,8 +44,13 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
4344
public:
4445
DeserializerDelegate(Message* m,
4546
Environment* env,
46-
const std::vector<MessagePort*>& message_ports)
47-
: env_(env), msg_(m), message_ports_(message_ports) {}
47+
const std::vector<MessagePort*>& message_ports,
48+
const std::vector<Local<SharedArrayBuffer>>&
49+
shared_array_buffers)
50+
: env_(env),
51+
msg_(m),
52+
message_ports_(message_ports),
53+
shared_array_buffers_(shared_array_buffers) {}
4854

4955
MaybeLocal<Object> ReadHostObject(Isolate* isolate) override {
5056
// Currently, only MessagePort hosts objects are supported, so identifying
@@ -56,12 +62,19 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
5662
return message_ports_[id]->object();
5763
};
5864

65+
MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
66+
Isolate* isolate, uint32_t clone_id) override {
67+
CHECK_LE(clone_id, shared_array_buffers_.size());
68+
return shared_array_buffers_[clone_id];
69+
}
70+
5971
ValueDeserializer* deserializer = nullptr;
6072

6173
private:
6274
Environment* env_;
6375
Message* msg_;
6476
const std::vector<MessagePort*>& message_ports_;
77+
const std::vector<Local<SharedArrayBuffer>>& shared_array_buffers_;
6578
};
6679

6780
} // anonymous namespace
@@ -87,7 +100,18 @@ MaybeLocal<Value> Message::Deserialize(Environment* env,
87100
}
88101
message_ports_.clear();
89102

90-
DeserializerDelegate delegate(this, env, ports);
103+
std::vector<Local<SharedArrayBuffer>> shared_array_buffers;
104+
// Attach all transfered SharedArrayBuffers to their new Isolate.
105+
for (uint32_t i = 0; i < shared_array_buffers_.size(); ++i) {
106+
Local<SharedArrayBuffer> sab;
107+
if (!shared_array_buffers_[i]->GetSharedArrayBuffer(env, context)
108+
.ToLocal(&sab))
109+
return MaybeLocal<Value>();
110+
shared_array_buffers.push_back(sab);
111+
}
112+
shared_array_buffers_.clear();
113+
114+
DeserializerDelegate delegate(this, env, ports, shared_array_buffers);
91115
ValueDeserializer deserializer(
92116
env->isolate(),
93117
reinterpret_cast<const uint8_t*>(main_message_buf_.data),
@@ -112,6 +136,11 @@ MaybeLocal<Value> Message::Deserialize(Environment* env,
112136
deserializer.ReadValue(context).FromMaybe(Local<Value>()));
113137
}
114138

139+
void Message::AddSharedArrayBuffer(
140+
SharedArrayBufferMetadataReference reference) {
141+
shared_array_buffers_.push_back(reference);
142+
}
143+
115144
void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) {
116145
message_ports_.emplace_back(std::move(data));
117146
}
@@ -139,6 +168,27 @@ class SerializerDelegate : public ValueSerializer::Delegate {
139168
return Nothing<bool>();
140169
}
141170

171+
Maybe<uint32_t> GetSharedArrayBufferId(
172+
Isolate* isolate,
173+
Local<SharedArrayBuffer> shared_array_buffer) override {
174+
uint32_t i;
175+
for (i = 0; i < seen_shared_array_buffers_.size(); ++i) {
176+
if (seen_shared_array_buffers_[i] == shared_array_buffer)
177+
return Just(i);
178+
}
179+
180+
auto reference = SharedArrayBufferMetadata::ForSharedArrayBuffer(
181+
env_,
182+
context_,
183+
shared_array_buffer);
184+
if (!reference) {
185+
return Nothing<uint32_t>();
186+
}
187+
seen_shared_array_buffers_.push_back(shared_array_buffer);
188+
msg_->AddSharedArrayBuffer(reference);
189+
return Just(i);
190+
}
191+
142192
void Finish() {
143193
// Only close the MessagePort handles and actually transfer them
144194
// once we know that serialization succeeded.
@@ -166,6 +216,7 @@ class SerializerDelegate : public ValueSerializer::Delegate {
166216
Environment* env_;
167217
Local<Context> context_;
168218
Message* msg_;
219+
std::vector<Local<SharedArrayBuffer>> seen_shared_array_buffers_;
169220
std::vector<MessagePort*> ports_;
170221

171222
friend class worker::Message;

src/node_messaging.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
#include "env.h"
77
#include "node_mutex.h"
8+
#include "sharedarraybuffer_metadata.h"
89
#include <list>
9-
#include <memory>
1010

1111
namespace node {
1212
namespace worker {
@@ -37,13 +37,17 @@ class Message {
3737
v8::Local<v8::Value> input,
3838
v8::Local<v8::Value> transfer_list);
3939

40+
// Internal method of Message that is called when a new SharedArrayBuffer
41+
// object is encountered in the incoming value's structure.
42+
void AddSharedArrayBuffer(SharedArrayBufferMetadataReference ref);
4043
// Internal method of Message that is called once serialization finishes
4144
// and that transfers ownership of `data` to this message.
4245
void AddMessagePort(std::unique_ptr<MessagePortData>&& data);
4346

4447
private:
4548
MallocedBuffer<char> main_message_buf_;
4649
std::vector<MallocedBuffer<char>> array_buffer_contents_;
50+
std::vector<SharedArrayBufferMetadataReference> shared_array_buffers_;
4751
std::vector<std::unique_ptr<MessagePortData>> message_ports_;
4852

4953
friend class MessagePort;

src/sharedarraybuffer_metadata.cc

Lines changed: 129 additions & 0 deletions

0 commit comments

Comments
 (0)