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: https://github.com/ayojs/ayo/pull/106 PR-URL: https://github.com/nodejs/node/pull/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>
This commit is contained in:
parent
749a13b76c
commit
b0404047c1
@ -85,14 +85,16 @@ to stringify.
|
|||||||
|
|
||||||
`transferList` may be a list of `ArrayBuffer` and `MessagePort` objects.
|
`transferList` may be a list of `ArrayBuffer` and `MessagePort` objects.
|
||||||
After transferring, they will not be usable on the sending side of the channel
|
After transferring, they will not be usable on the sending side of the channel
|
||||||
anymore (even if they are not contained in `value`).
|
anymore (even if they are not contained in `value`). Unlike with
|
||||||
|
[child processes][], transferring handles such as network sockets is currently
|
||||||
|
not supported.
|
||||||
|
|
||||||
|
If `value` contains [`SharedArrayBuffer`][] instances, those will be accessible
|
||||||
|
from either thread. They cannot be listed in `transferList`.
|
||||||
|
|
||||||
`value` may still contain `ArrayBuffer` instances that are not in
|
`value` may still contain `ArrayBuffer` instances that are not in
|
||||||
`transferList`; in that case, the underlying memory is copied rather than moved.
|
`transferList`; in that case, the underlying memory is copied rather than moved.
|
||||||
|
|
||||||
For more information on the serialization and deserialization mechanisms
|
|
||||||
behind this API, see the [serialization API of the `v8` module][v8.serdes].
|
|
||||||
|
|
||||||
Because the object cloning uses the structured clone algorithm,
|
Because the object cloning uses the structured clone algorithm,
|
||||||
non-enumerable properties, property accessors, and object prototypes are
|
non-enumerable properties, property accessors, and object prototypes are
|
||||||
not preserved. In particular, [`Buffer`][] objects will be read as
|
not preserved. In particular, [`Buffer`][] objects will be read as
|
||||||
@ -101,6 +103,9 @@ plain [`Uint8Array`][]s on the receiving side.
|
|||||||
The message object will be cloned immediately, and can be modified after
|
The message object will be cloned immediately, and can be modified after
|
||||||
posting without having side effects.
|
posting without having side effects.
|
||||||
|
|
||||||
|
For more information on the serialization and deserialization mechanisms
|
||||||
|
behind this API, see the [serialization API of the `v8` module][v8.serdes].
|
||||||
|
|
||||||
### port.ref()
|
### port.ref()
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: REPLACEME
|
added: REPLACEME
|
||||||
@ -137,10 +142,12 @@ be `ref()`ed and `unref()`ed automatically depending on whether
|
|||||||
listeners for the event exist.
|
listeners for the event exist.
|
||||||
|
|
||||||
[`Buffer`]: buffer.html
|
[`Buffer`]: buffer.html
|
||||||
|
[child processes]: child_process.html
|
||||||
[`EventEmitter`]: events.html
|
[`EventEmitter`]: events.html
|
||||||
[`MessagePort`]: #worker_class_messageport
|
[`MessagePort`]: #worker_class_messageport
|
||||||
[`port.postMessage()`]: #worker_port_postmessage_value_transferlist
|
[`port.postMessage()`]: #worker_port_postmessage_value_transferlist
|
||||||
[v8.serdes]: v8.html#v8_serialization_api
|
[v8.serdes]: v8.html#v8_serialization_api
|
||||||
|
[`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer
|
||||||
[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
|
[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
|
||||||
[browser `MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
|
[browser `MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
|
||||||
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
|
||||||
|
2
node.gyp
2
node.gyp
@ -353,6 +353,7 @@
|
|||||||
'src/node_i18n.cc',
|
'src/node_i18n.cc',
|
||||||
'src/pipe_wrap.cc',
|
'src/pipe_wrap.cc',
|
||||||
'src/process_wrap.cc',
|
'src/process_wrap.cc',
|
||||||
|
'src/sharedarraybuffer_metadata.cc',
|
||||||
'src/signal_wrap.cc',
|
'src/signal_wrap.cc',
|
||||||
'src/spawn_sync.cc',
|
'src/spawn_sync.cc',
|
||||||
'src/string_bytes.cc',
|
'src/string_bytes.cc',
|
||||||
@ -412,6 +413,7 @@
|
|||||||
'src/udp_wrap.h',
|
'src/udp_wrap.h',
|
||||||
'src/req_wrap.h',
|
'src/req_wrap.h',
|
||||||
'src/req_wrap-inl.h',
|
'src/req_wrap-inl.h',
|
||||||
|
'src/sharedarraybuffer_metadata.h',
|
||||||
'src/string_bytes.h',
|
'src/string_bytes.h',
|
||||||
'src/string_decoder.h',
|
'src/string_decoder.h',
|
||||||
'src/string_decoder-inl.h',
|
'src/string_decoder-inl.h',
|
||||||
|
@ -106,6 +106,7 @@ struct PackageConfig {
|
|||||||
V(decorated_private_symbol, "node:decorated") \
|
V(decorated_private_symbol, "node:decorated") \
|
||||||
V(napi_env, "node:napi:env") \
|
V(napi_env, "node:napi:env") \
|
||||||
V(napi_wrapper, "node:napi:wrapper") \
|
V(napi_wrapper, "node:napi:wrapper") \
|
||||||
|
V(sab_lifetimepartner_symbol, "node:sharedArrayBufferLifetimePartner") \
|
||||||
|
|
||||||
// Symbols are per-isolate primitives but Environment proxies them
|
// Symbols are per-isolate primitives but Environment proxies them
|
||||||
// for the sake of convenience.
|
// for the sake of convenience.
|
||||||
@ -338,6 +339,7 @@ struct PackageConfig {
|
|||||||
V(promise_wrap_template, v8::ObjectTemplate) \
|
V(promise_wrap_template, v8::ObjectTemplate) \
|
||||||
V(push_values_to_array_function, v8::Function) \
|
V(push_values_to_array_function, v8::Function) \
|
||||||
V(randombytes_constructor_template, v8::ObjectTemplate) \
|
V(randombytes_constructor_template, v8::ObjectTemplate) \
|
||||||
|
V(sab_lifetimepartner_constructor_template, v8::FunctionTemplate) \
|
||||||
V(script_context_constructor_template, v8::FunctionTemplate) \
|
V(script_context_constructor_template, v8::FunctionTemplate) \
|
||||||
V(script_data_constructor_function, v8::Function) \
|
V(script_data_constructor_function, v8::Function) \
|
||||||
V(secure_context_constructor_template, v8::FunctionTemplate) \
|
V(secure_context_constructor_template, v8::FunctionTemplate) \
|
||||||
|
@ -37,6 +37,7 @@ namespace node {
|
|||||||
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \
|
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \
|
||||||
V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error) \
|
V(ERR_SCRIPT_EXECUTION_TIMEOUT, Error) \
|
||||||
V(ERR_STRING_TOO_LONG, Error) \
|
V(ERR_STRING_TOO_LONG, Error) \
|
||||||
|
V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, TypeError) \
|
||||||
|
|
||||||
#define V(code, type) \
|
#define V(code, type) \
|
||||||
inline v8::Local<v8::Value> code(v8::Isolate* isolate, \
|
inline v8::Local<v8::Value> code(v8::Isolate* isolate, \
|
||||||
@ -68,7 +69,9 @@ namespace node {
|
|||||||
V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, \
|
V(ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST, \
|
||||||
"MessagePort was found in message but not listed in transferList") \
|
"MessagePort was found in message but not listed in transferList") \
|
||||||
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, \
|
V(ERR_SCRIPT_EXECUTION_INTERRUPTED, \
|
||||||
"Script execution was interrupted by `SIGINT`")
|
"Script execution was interrupted by `SIGINT`") \
|
||||||
|
V(ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER, \
|
||||||
|
"Cannot serialize externalized SharedArrayBuffer") \
|
||||||
|
|
||||||
#define V(code, message) \
|
#define V(code, message) \
|
||||||
inline v8::Local<v8::Value> code(v8::Isolate* isolate) { \
|
inline v8::Local<v8::Value> code(v8::Isolate* isolate) { \
|
||||||
|
@ -24,6 +24,7 @@ using v8::Maybe;
|
|||||||
using v8::MaybeLocal;
|
using v8::MaybeLocal;
|
||||||
using v8::Nothing;
|
using v8::Nothing;
|
||||||
using v8::Object;
|
using v8::Object;
|
||||||
|
using v8::SharedArrayBuffer;
|
||||||
using v8::String;
|
using v8::String;
|
||||||
using v8::Value;
|
using v8::Value;
|
||||||
using v8::ValueDeserializer;
|
using v8::ValueDeserializer;
|
||||||
@ -43,8 +44,13 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
|
|||||||
public:
|
public:
|
||||||
DeserializerDelegate(Message* m,
|
DeserializerDelegate(Message* m,
|
||||||
Environment* env,
|
Environment* env,
|
||||||
const std::vector<MessagePort*>& message_ports)
|
const std::vector<MessagePort*>& message_ports,
|
||||||
: env_(env), msg_(m), message_ports_(message_ports) {}
|
const std::vector<Local<SharedArrayBuffer>>&
|
||||||
|
shared_array_buffers)
|
||||||
|
: env_(env),
|
||||||
|
msg_(m),
|
||||||
|
message_ports_(message_ports),
|
||||||
|
shared_array_buffers_(shared_array_buffers) {}
|
||||||
|
|
||||||
MaybeLocal<Object> ReadHostObject(Isolate* isolate) override {
|
MaybeLocal<Object> ReadHostObject(Isolate* isolate) override {
|
||||||
// Currently, only MessagePort hosts objects are supported, so identifying
|
// Currently, only MessagePort hosts objects are supported, so identifying
|
||||||
@ -56,12 +62,19 @@ class DeserializerDelegate : public ValueDeserializer::Delegate {
|
|||||||
return message_ports_[id]->object();
|
return message_ports_[id]->object();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
|
||||||
|
Isolate* isolate, uint32_t clone_id) override {
|
||||||
|
CHECK_LE(clone_id, shared_array_buffers_.size());
|
||||||
|
return shared_array_buffers_[clone_id];
|
||||||
|
}
|
||||||
|
|
||||||
ValueDeserializer* deserializer = nullptr;
|
ValueDeserializer* deserializer = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Environment* env_;
|
Environment* env_;
|
||||||
Message* msg_;
|
Message* msg_;
|
||||||
const std::vector<MessagePort*>& message_ports_;
|
const std::vector<MessagePort*>& message_ports_;
|
||||||
|
const std::vector<Local<SharedArrayBuffer>>& shared_array_buffers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
@ -87,7 +100,18 @@ MaybeLocal<Value> Message::Deserialize(Environment* env,
|
|||||||
}
|
}
|
||||||
message_ports_.clear();
|
message_ports_.clear();
|
||||||
|
|
||||||
DeserializerDelegate delegate(this, env, ports);
|
std::vector<Local<SharedArrayBuffer>> shared_array_buffers;
|
||||||
|
// Attach all transfered SharedArrayBuffers to their new Isolate.
|
||||||
|
for (uint32_t i = 0; i < shared_array_buffers_.size(); ++i) {
|
||||||
|
Local<SharedArrayBuffer> sab;
|
||||||
|
if (!shared_array_buffers_[i]->GetSharedArrayBuffer(env, context)
|
||||||
|
.ToLocal(&sab))
|
||||||
|
return MaybeLocal<Value>();
|
||||||
|
shared_array_buffers.push_back(sab);
|
||||||
|
}
|
||||||
|
shared_array_buffers_.clear();
|
||||||
|
|
||||||
|
DeserializerDelegate delegate(this, env, ports, shared_array_buffers);
|
||||||
ValueDeserializer deserializer(
|
ValueDeserializer deserializer(
|
||||||
env->isolate(),
|
env->isolate(),
|
||||||
reinterpret_cast<const uint8_t*>(main_message_buf_.data),
|
reinterpret_cast<const uint8_t*>(main_message_buf_.data),
|
||||||
@ -112,6 +136,11 @@ MaybeLocal<Value> Message::Deserialize(Environment* env,
|
|||||||
deserializer.ReadValue(context).FromMaybe(Local<Value>()));
|
deserializer.ReadValue(context).FromMaybe(Local<Value>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Message::AddSharedArrayBuffer(
|
||||||
|
SharedArrayBufferMetadataReference reference) {
|
||||||
|
shared_array_buffers_.push_back(reference);
|
||||||
|
}
|
||||||
|
|
||||||
void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) {
|
void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) {
|
||||||
message_ports_.emplace_back(std::move(data));
|
message_ports_.emplace_back(std::move(data));
|
||||||
}
|
}
|
||||||
@ -139,6 +168,27 @@ class SerializerDelegate : public ValueSerializer::Delegate {
|
|||||||
return Nothing<bool>();
|
return Nothing<bool>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Maybe<uint32_t> GetSharedArrayBufferId(
|
||||||
|
Isolate* isolate,
|
||||||
|
Local<SharedArrayBuffer> shared_array_buffer) override {
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < seen_shared_array_buffers_.size(); ++i) {
|
||||||
|
if (seen_shared_array_buffers_[i] == shared_array_buffer)
|
||||||
|
return Just(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reference = SharedArrayBufferMetadata::ForSharedArrayBuffer(
|
||||||
|
env_,
|
||||||
|
context_,
|
||||||
|
shared_array_buffer);
|
||||||
|
if (!reference) {
|
||||||
|
return Nothing<uint32_t>();
|
||||||
|
}
|
||||||
|
seen_shared_array_buffers_.push_back(shared_array_buffer);
|
||||||
|
msg_->AddSharedArrayBuffer(reference);
|
||||||
|
return Just(i);
|
||||||
|
}
|
||||||
|
|
||||||
void Finish() {
|
void Finish() {
|
||||||
// Only close the MessagePort handles and actually transfer them
|
// Only close the MessagePort handles and actually transfer them
|
||||||
// once we know that serialization succeeded.
|
// once we know that serialization succeeded.
|
||||||
@ -166,6 +216,7 @@ class SerializerDelegate : public ValueSerializer::Delegate {
|
|||||||
Environment* env_;
|
Environment* env_;
|
||||||
Local<Context> context_;
|
Local<Context> context_;
|
||||||
Message* msg_;
|
Message* msg_;
|
||||||
|
std::vector<Local<SharedArrayBuffer>> seen_shared_array_buffers_;
|
||||||
std::vector<MessagePort*> ports_;
|
std::vector<MessagePort*> ports_;
|
||||||
|
|
||||||
friend class worker::Message;
|
friend class worker::Message;
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
#include "env.h"
|
#include "env.h"
|
||||||
#include "node_mutex.h"
|
#include "node_mutex.h"
|
||||||
|
#include "sharedarraybuffer_metadata.h"
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
namespace worker {
|
namespace worker {
|
||||||
@ -37,6 +37,9 @@ class Message {
|
|||||||
v8::Local<v8::Value> input,
|
v8::Local<v8::Value> input,
|
||||||
v8::Local<v8::Value> transfer_list);
|
v8::Local<v8::Value> transfer_list);
|
||||||
|
|
||||||
|
// Internal method of Message that is called when a new SharedArrayBuffer
|
||||||
|
// object is encountered in the incoming value's structure.
|
||||||
|
void AddSharedArrayBuffer(SharedArrayBufferMetadataReference ref);
|
||||||
// Internal method of Message that is called once serialization finishes
|
// Internal method of Message that is called once serialization finishes
|
||||||
// and that transfers ownership of `data` to this message.
|
// and that transfers ownership of `data` to this message.
|
||||||
void AddMessagePort(std::unique_ptr<MessagePortData>&& data);
|
void AddMessagePort(std::unique_ptr<MessagePortData>&& data);
|
||||||
@ -44,6 +47,7 @@ class Message {
|
|||||||
private:
|
private:
|
||||||
MallocedBuffer<char> main_message_buf_;
|
MallocedBuffer<char> main_message_buf_;
|
||||||
std::vector<MallocedBuffer<char>> array_buffer_contents_;
|
std::vector<MallocedBuffer<char>> array_buffer_contents_;
|
||||||
|
std::vector<SharedArrayBufferMetadataReference> shared_array_buffers_;
|
||||||
std::vector<std::unique_ptr<MessagePortData>> message_ports_;
|
std::vector<std::unique_ptr<MessagePortData>> message_ports_;
|
||||||
|
|
||||||
friend class MessagePort;
|
friend class MessagePort;
|
||||||
|
129
src/sharedarraybuffer_metadata.cc
Normal file
129
src/sharedarraybuffer_metadata.cc
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#include "sharedarraybuffer_metadata.h"
|
||||||
|
#include "base_object.h"
|
||||||
|
#include "base_object-inl.h"
|
||||||
|
#include "node_errors.h"
|
||||||
|
|
||||||
|
using v8::Context;
|
||||||
|
using v8::Function;
|
||||||
|
using v8::FunctionTemplate;
|
||||||
|
using v8::Local;
|
||||||
|
using v8::Maybe;
|
||||||
|
using v8::MaybeLocal;
|
||||||
|
using v8::Nothing;
|
||||||
|
using v8::Object;
|
||||||
|
using v8::SharedArrayBuffer;
|
||||||
|
using v8::Value;
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
namespace worker {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Yield a JS constructor for SABLifetimePartner objects in the form of a
|
||||||
|
// standard API object, that has a single field for containing the raw
|
||||||
|
// SABLiftimePartner* pointer.
|
||||||
|
Local<Function> GetSABLifetimePartnerConstructor(
|
||||||
|
Environment* env, Local<Context> context) {
|
||||||
|
Local<FunctionTemplate> templ;
|
||||||
|
templ = env->sab_lifetimepartner_constructor_template();
|
||||||
|
if (!templ.IsEmpty())
|
||||||
|
return templ->GetFunction(context).ToLocalChecked();
|
||||||
|
|
||||||
|
templ = BaseObject::MakeLazilyInitializedJSTemplate(env);
|
||||||
|
templ->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(),
|
||||||
|
"SABLifetimePartner"));
|
||||||
|
env->set_sab_lifetimepartner_constructor_template(templ);
|
||||||
|
|
||||||
|
return GetSABLifetimePartnerConstructor(env, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SABLifetimePartner : public BaseObject {
|
||||||
|
public:
|
||||||
|
SABLifetimePartner(Environment* env,
|
||||||
|
Local<Object> obj,
|
||||||
|
SharedArrayBufferMetadataReference r)
|
||||||
|
: BaseObject(env, obj),
|
||||||
|
reference(r) {
|
||||||
|
MakeWeak();
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedArrayBufferMetadataReference reference;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
SharedArrayBufferMetadataReference
|
||||||
|
SharedArrayBufferMetadata::ForSharedArrayBuffer(
|
||||||
|
Environment* env,
|
||||||
|
Local<Context> context,
|
||||||
|
Local<SharedArrayBuffer> source) {
|
||||||
|
Local<Value> lifetime_partner;
|
||||||
|
|
||||||
|
if (!source->GetPrivate(context,
|
||||||
|
env->sab_lifetimepartner_symbol())
|
||||||
|
.ToLocal(&lifetime_partner)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lifetime_partner->IsObject() &&
|
||||||
|
env->sab_lifetimepartner_constructor_template()
|
||||||
|
->HasInstance(lifetime_partner)) {
|
||||||
|
CHECK(source->IsExternal());
|
||||||
|
SABLifetimePartner* partner =
|
||||||
|
Unwrap<SABLifetimePartner>(lifetime_partner.As<Object>());
|
||||||
|
CHECK_NE(partner, nullptr);
|
||||||
|
return partner->reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source->IsExternal()) {
|
||||||
|
// If this is an external SharedArrayBuffer but we do not see a lifetime
|
||||||
|
// partner object, it was not us who externalized it. In that case, there
|
||||||
|
// is no way to serialize it, because it's unclear how the memory
|
||||||
|
// is actually owned.
|
||||||
|
THROW_ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER(env);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedArrayBuffer::Contents contents = source->Externalize();
|
||||||
|
SharedArrayBufferMetadataReference r(new SharedArrayBufferMetadata(
|
||||||
|
contents.Data(), contents.ByteLength()));
|
||||||
|
if (r->AssignToSharedArrayBuffer(env, context, source).IsNothing())
|
||||||
|
return nullptr;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<bool> SharedArrayBufferMetadata::AssignToSharedArrayBuffer(
|
||||||
|
Environment* env, Local<Context> context,
|
||||||
|
Local<SharedArrayBuffer> target) {
|
||||||
|
CHECK(target->IsExternal());
|
||||||
|
Local<Function> ctor = GetSABLifetimePartnerConstructor(env, context);
|
||||||
|
Local<Object> obj;
|
||||||
|
if (!ctor->NewInstance(context).ToLocal(&obj))
|
||||||
|
return Nothing<bool>();
|
||||||
|
|
||||||
|
new SABLifetimePartner(env, obj, shared_from_this());
|
||||||
|
return target->SetPrivate(context,
|
||||||
|
env->sab_lifetimepartner_symbol(),
|
||||||
|
obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedArrayBufferMetadata::SharedArrayBufferMetadata(void* data, size_t size)
|
||||||
|
: data(data), size(size) { }
|
||||||
|
|
||||||
|
SharedArrayBufferMetadata::~SharedArrayBufferMetadata() {
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeLocal<SharedArrayBuffer> SharedArrayBufferMetadata::GetSharedArrayBuffer(
|
||||||
|
Environment* env, Local<Context> context) {
|
||||||
|
Local<SharedArrayBuffer> obj =
|
||||||
|
SharedArrayBuffer::New(env->isolate(), data, size);
|
||||||
|
|
||||||
|
if (AssignToSharedArrayBuffer(env, context, obj).IsNothing())
|
||||||
|
return MaybeLocal<SharedArrayBuffer>();
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace worker
|
||||||
|
} // namespace node
|
67
src/sharedarraybuffer_metadata.h
Normal file
67
src/sharedarraybuffer_metadata.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#ifndef SRC_SHAREDARRAYBUFFER_METADATA_H_
|
||||||
|
#define SRC_SHAREDARRAYBUFFER_METADATA_H_
|
||||||
|
|
||||||
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
|
||||||
|
#include "node.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace node {
|
||||||
|
namespace worker {
|
||||||
|
|
||||||
|
class SharedArrayBufferMetadata;
|
||||||
|
|
||||||
|
// This is an object associated with a SharedArrayBuffer, which keeps track
|
||||||
|
// of a cross-thread reference count. Once a SharedArrayBuffer is transferred
|
||||||
|
// for the first time (or is attempted to be transferred), one of these objects
|
||||||
|
// is created, and the SharedArrayBuffer is moved from internalized mode into
|
||||||
|
// externalized mode (i.e. the JS engine no longer frees the memory on its own).
|
||||||
|
//
|
||||||
|
// This will always be referred to using a std::shared_ptr, since it keeps
|
||||||
|
// a reference count and is guaranteed to be thread-safe.
|
||||||
|
typedef std::shared_ptr<SharedArrayBufferMetadata>
|
||||||
|
SharedArrayBufferMetadataReference;
|
||||||
|
|
||||||
|
class SharedArrayBufferMetadata
|
||||||
|
: public std::enable_shared_from_this<SharedArrayBufferMetadata> {
|
||||||
|
public:
|
||||||
|
static SharedArrayBufferMetadataReference ForSharedArrayBuffer(
|
||||||
|
Environment* env,
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
v8::Local<v8::SharedArrayBuffer> source);
|
||||||
|
~SharedArrayBufferMetadata();
|
||||||
|
|
||||||
|
// Create a SharedArrayBuffer object for a specific Environment and Context.
|
||||||
|
// The created SharedArrayBuffer will be in externalized mode and has
|
||||||
|
// a hidden object attached to it, during whose lifetime the reference
|
||||||
|
// count is increased by 1.
|
||||||
|
v8::MaybeLocal<v8::SharedArrayBuffer> GetSharedArrayBuffer(
|
||||||
|
Environment* env, v8::Local<v8::Context> context);
|
||||||
|
|
||||||
|
SharedArrayBufferMetadata(SharedArrayBufferMetadata&& other) = delete;
|
||||||
|
SharedArrayBufferMetadata& operator=(
|
||||||
|
SharedArrayBufferMetadata&& other) = delete;
|
||||||
|
SharedArrayBufferMetadata& operator=(
|
||||||
|
const SharedArrayBufferMetadata&) = delete;
|
||||||
|
SharedArrayBufferMetadata(const SharedArrayBufferMetadata&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit SharedArrayBufferMetadata(void* data, size_t size);
|
||||||
|
|
||||||
|
// Attach a lifetime tracker object with a reference count to `target`.
|
||||||
|
v8::Maybe<bool> AssignToSharedArrayBuffer(
|
||||||
|
Environment* env,
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
v8::Local<v8::SharedArrayBuffer> target);
|
||||||
|
|
||||||
|
void* data = nullptr;
|
||||||
|
size_t size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace worker
|
||||||
|
} // namespace node
|
||||||
|
|
||||||
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SRC_SHAREDARRAYBUFFER_METADATA_H_
|
Loading…
x
Reference in New Issue
Block a user