mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
src: remove custom tracking for SharedArrayBuffers
Remove custom tracking for `SharedArrayBuffer`s and their allocators and instead let V8 do the tracking of both. This is required starting in V8 7.9, because lifetime management for `ArrayBuffer::Allocator`s differs from what was performed previously (i.e. it is no longer easily possible for one Isolate to release an `ArrayBuffer` and another to accept it into its own allocator), and the alternative would have been adapting the `SharedArrayBuffer` tracking logic to also apply to regular `ArrayBuffer` instances. Refs: https://github.com/nodejs/node/pull/30044 PR-URL: https://github.com/nodejs/node/pull/30020 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
committed by
Michaël Zasso
parent
2707efd27b
commit
2bdeb88c27
2
node.gyp
2
node.gyp
@@ -564,7 +564,6 @@
|
||||
'src/node_zlib.cc',
|
||||
'src/pipe_wrap.cc',
|
||||
'src/process_wrap.cc',
|
||||
'src/sharedarraybuffer_metadata.cc',
|
||||
'src/signal_wrap.cc',
|
||||
'src/spawn_sync.cc',
|
||||
'src/stream_base.cc',
|
||||
@@ -642,7 +641,6 @@
|
||||
'src/pipe_wrap.h',
|
||||
'src/req_wrap.h',
|
||||
'src/req_wrap-inl.h',
|
||||
'src/sharedarraybuffer_metadata.h',
|
||||
'src/spawn_sync.h',
|
||||
'src/stream_base.h',
|
||||
'src/stream_base-inl.h',
|
||||
|
||||
@@ -279,6 +279,14 @@ Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
||||
return NewIsolate(¶ms, event_loop, platform);
|
||||
}
|
||||
|
||||
Isolate* NewIsolate(std::shared_ptr<ArrayBufferAllocator> allocator,
|
||||
uv_loop_t* event_loop,
|
||||
MultiIsolatePlatform* platform) {
|
||||
Isolate::CreateParams params;
|
||||
if (allocator) params.array_buffer_allocator_shared = allocator;
|
||||
return NewIsolate(¶ms, event_loop, platform);
|
||||
}
|
||||
|
||||
IsolateData* CreateIsolateData(Isolate* isolate,
|
||||
uv_loop_t* loop,
|
||||
MultiIsolatePlatform* platform,
|
||||
|
||||
15
src/env.cc
15
src/env.cc
@@ -1038,21 +1038,6 @@ char* Environment::Reallocate(char* data, size_t old_size, size_t size) {
|
||||
return new_data;
|
||||
}
|
||||
|
||||
void Environment::AddArrayBufferAllocatorToKeepAliveUntilIsolateDispose(
|
||||
std::shared_ptr<v8::ArrayBuffer::Allocator> allocator) {
|
||||
if (keep_alive_allocators_ == nullptr) {
|
||||
MultiIsolatePlatform* platform = isolate_data()->platform();
|
||||
CHECK_NOT_NULL(platform);
|
||||
|
||||
keep_alive_allocators_ = new ArrayBufferAllocatorList();
|
||||
platform->AddIsolateFinishedCallback(isolate(), [](void* data) {
|
||||
delete static_cast<ArrayBufferAllocatorList*>(data);
|
||||
}, static_cast<void*>(keep_alive_allocators_));
|
||||
}
|
||||
|
||||
keep_alive_allocators_->insert(allocator);
|
||||
}
|
||||
|
||||
bool Environment::RunWeakRefCleanup() {
|
||||
isolate()->ClearKeptObjects();
|
||||
|
||||
|
||||
@@ -156,7 +156,6 @@ constexpr size_t kFsStatsBufferLength =
|
||||
V(contextify_global_private_symbol, "node:contextify:global") \
|
||||
V(decorated_private_symbol, "node:decorated") \
|
||||
V(napi_wrapper, "node:napi:wrapper") \
|
||||
V(sab_lifetimepartner_symbol, "node:sharedArrayBufferLifetimePartner") \
|
||||
|
||||
// Symbols are per-isolate primitives but Environment proxies them
|
||||
// for the sake of convenience.
|
||||
@@ -1250,10 +1249,6 @@ class Environment : public MemoryRetainer {
|
||||
|
||||
#endif // HAVE_INSPECTOR
|
||||
|
||||
// Only available if a MultiIsolatePlatform is in use.
|
||||
void AddArrayBufferAllocatorToKeepAliveUntilIsolateDispose(
|
||||
std::shared_ptr<v8::ArrayBuffer::Allocator>);
|
||||
|
||||
private:
|
||||
template <typename Fn>
|
||||
inline void CreateImmediate(Fn&& cb,
|
||||
@@ -1433,10 +1428,6 @@ class Environment : public MemoryRetainer {
|
||||
// Used by embedders to shutdown running Node instance.
|
||||
AsyncRequest thread_stopper_;
|
||||
|
||||
typedef std::unordered_set<std::shared_ptr<v8::ArrayBuffer::Allocator>>
|
||||
ArrayBufferAllocatorList;
|
||||
ArrayBufferAllocatorList* keep_alive_allocators_ = nullptr;
|
||||
|
||||
template <typename T>
|
||||
void ForEachBaseObject(T&& iterator);
|
||||
|
||||
|
||||
@@ -109,6 +109,16 @@ void MemoryTracker::TrackField(const char* edge_name,
|
||||
TrackField(edge_name, value.get(), node_name);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void MemoryTracker::TrackField(const char* edge_name,
|
||||
const std::shared_ptr<T>& value,
|
||||
const char* node_name) {
|
||||
if (value.get() == nullptr) {
|
||||
return;
|
||||
}
|
||||
TrackField(edge_name, value.get(), node_name);
|
||||
}
|
||||
|
||||
template <typename T, typename Iterator>
|
||||
void MemoryTracker::TrackField(const char* edge_name,
|
||||
const T& value,
|
||||
@@ -206,6 +216,12 @@ void MemoryTracker::TrackField(const char* edge_name,
|
||||
TrackFieldWithSize(edge_name, value.size, "MallocedBuffer");
|
||||
}
|
||||
|
||||
void MemoryTracker::TrackField(const char* edge_name,
|
||||
const v8::BackingStore* value,
|
||||
const char* node_name) {
|
||||
TrackFieldWithSize(edge_name, value->ByteLength(), "BackingStore");
|
||||
}
|
||||
|
||||
void MemoryTracker::TrackField(const char* name,
|
||||
const uv_buf_t& value,
|
||||
const char* node_name) {
|
||||
|
||||
@@ -138,6 +138,10 @@ class MemoryTracker {
|
||||
inline void TrackField(const char* edge_name,
|
||||
const std::unique_ptr<T>& value,
|
||||
const char* node_name = nullptr);
|
||||
template <typename T>
|
||||
inline void TrackField(const char* edge_name,
|
||||
const std::shared_ptr<T>& value,
|
||||
const char* node_name = nullptr);
|
||||
|
||||
// For containers, the elements will be graphed as grandchildren nodes
|
||||
// if the container is not empty.
|
||||
@@ -197,6 +201,9 @@ class MemoryTracker {
|
||||
inline void TrackField(const char* edge_name,
|
||||
const MallocedBuffer<T>& value,
|
||||
const char* node_name = nullptr);
|
||||
inline void TrackField(const char* edge_name,
|
||||
const v8::BackingStore* value,
|
||||
const char* node_name = nullptr);
|
||||
// We do not implement CleanupHookCallback as MemoryRetainer
|
||||
// but instead specialize the method here to avoid the cost of
|
||||
// virtual pointers.
|
||||
|
||||
@@ -328,6 +328,10 @@ NODE_EXTERN v8::Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
||||
NODE_EXTERN v8::Isolate* NewIsolate(ArrayBufferAllocator* allocator,
|
||||
struct uv_loop_s* event_loop,
|
||||
MultiIsolatePlatform* platform);
|
||||
NODE_EXTERN v8::Isolate* NewIsolate(
|
||||
std::shared_ptr<ArrayBufferAllocator> allocator,
|
||||
struct uv_loop_s* event_loop,
|
||||
MultiIsolatePlatform* platform);
|
||||
|
||||
// Creates a new context with Node.js-specific tweaks.
|
||||
NODE_EXTERN v8::Local<v8::Context> NewContext(
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
using node::contextify::ContextifyContext;
|
||||
using v8::Array;
|
||||
using v8::ArrayBuffer;
|
||||
using v8::ArrayBufferCreationMode;
|
||||
using v8::BackingStore;
|
||||
using v8::Context;
|
||||
using v8::EscapableHandleScope;
|
||||
using v8::Exception;
|
||||
@@ -123,10 +123,9 @@ MaybeLocal<Value> Message::Deserialize(Environment* env,
|
||||
std::vector<Local<SharedArrayBuffer>> shared_array_buffers;
|
||||
// Attach all transferred 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>();
|
||||
Local<SharedArrayBuffer> sab =
|
||||
SharedArrayBuffer::New(env->isolate(),
|
||||
std::move(shared_array_buffers_[i]));
|
||||
shared_array_buffers.push_back(sab);
|
||||
}
|
||||
shared_array_buffers_.clear();
|
||||
@@ -141,30 +140,12 @@ MaybeLocal<Value> Message::Deserialize(Environment* env,
|
||||
delegate.deserializer = &deserializer;
|
||||
|
||||
// Attach all transferred ArrayBuffers to their new Isolate.
|
||||
for (uint32_t i = 0; i < array_buffer_contents_.size(); ++i) {
|
||||
if (!env->isolate_data()->uses_node_allocator()) {
|
||||
// We don't use Node's allocator on the receiving side, so we have
|
||||
// to create the ArrayBuffer from a copy of the memory.
|
||||
AllocatedBuffer buf =
|
||||
env->AllocateManaged(array_buffer_contents_[i].size);
|
||||
memcpy(buf.data(),
|
||||
array_buffer_contents_[i].data,
|
||||
array_buffer_contents_[i].size);
|
||||
deserializer.TransferArrayBuffer(i, buf.ToArrayBuffer());
|
||||
continue;
|
||||
}
|
||||
|
||||
env->isolate_data()->node_allocator()->RegisterPointer(
|
||||
array_buffer_contents_[i].data, array_buffer_contents_[i].size);
|
||||
|
||||
for (uint32_t i = 0; i < array_buffers_.size(); ++i) {
|
||||
Local<ArrayBuffer> ab =
|
||||
ArrayBuffer::New(env->isolate(),
|
||||
array_buffer_contents_[i].release(),
|
||||
array_buffer_contents_[i].size,
|
||||
ArrayBufferCreationMode::kInternalized);
|
||||
ArrayBuffer::New(env->isolate(), std::move(array_buffers_[i]));
|
||||
deserializer.TransferArrayBuffer(i, ab);
|
||||
}
|
||||
array_buffer_contents_.clear();
|
||||
array_buffers_.clear();
|
||||
|
||||
if (deserializer.ReadHeader(context).IsNothing())
|
||||
return MaybeLocal<Value>();
|
||||
@@ -173,8 +154,8 @@ MaybeLocal<Value> Message::Deserialize(Environment* env,
|
||||
}
|
||||
|
||||
void Message::AddSharedArrayBuffer(
|
||||
const SharedArrayBufferMetadataReference& reference) {
|
||||
shared_array_buffers_.push_back(reference);
|
||||
std::shared_ptr<BackingStore> backing_store) {
|
||||
shared_array_buffers_.emplace_back(std::move(backing_store));
|
||||
}
|
||||
|
||||
void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) {
|
||||
@@ -249,16 +230,9 @@ class SerializerDelegate : public ValueSerializer::Delegate {
|
||||
}
|
||||
}
|
||||
|
||||
auto reference = SharedArrayBufferMetadata::ForSharedArrayBuffer(
|
||||
env_,
|
||||
context_,
|
||||
shared_array_buffer);
|
||||
if (!reference) {
|
||||
return Nothing<uint32_t>();
|
||||
}
|
||||
seen_shared_array_buffers_.emplace_back(
|
||||
Global<SharedArrayBuffer> { isolate, shared_array_buffer });
|
||||
msg_->AddSharedArrayBuffer(reference);
|
||||
msg_->AddSharedArrayBuffer(shared_array_buffer->GetBackingStore());
|
||||
return Just(i);
|
||||
}
|
||||
|
||||
@@ -386,18 +360,12 @@ Maybe<bool> Message::Serialize(Environment* env,
|
||||
}
|
||||
|
||||
for (Local<ArrayBuffer> ab : array_buffers) {
|
||||
// If serialization succeeded, we want to take ownership of
|
||||
// (a.k.a. externalize) the underlying memory region and render
|
||||
// it inaccessible in this Isolate.
|
||||
ArrayBuffer::Contents contents = ab->Externalize();
|
||||
// If serialization succeeded, we render it inaccessible in this Isolate.
|
||||
std::shared_ptr<BackingStore> backing_store = ab->GetBackingStore();
|
||||
ab->Externalize(backing_store);
|
||||
ab->Detach();
|
||||
|
||||
CHECK(env->isolate_data()->uses_node_allocator());
|
||||
env->isolate_data()->node_allocator()->UnregisterPointer(
|
||||
contents.Data(), contents.ByteLength());
|
||||
|
||||
array_buffer_contents_.emplace_back(MallocedBuffer<char>{
|
||||
static_cast<char*>(contents.Data()), contents.ByteLength()});
|
||||
array_buffers_.emplace_back(std::move(backing_store));
|
||||
}
|
||||
|
||||
delegate.Finish();
|
||||
@@ -411,9 +379,8 @@ Maybe<bool> Message::Serialize(Environment* env,
|
||||
}
|
||||
|
||||
void Message::MemoryInfo(MemoryTracker* tracker) const {
|
||||
tracker->TrackField("array_buffer_contents", array_buffer_contents_);
|
||||
tracker->TrackFieldWithSize("shared_array_buffers",
|
||||
shared_array_buffers_.size() * sizeof(shared_array_buffers_[0]));
|
||||
tracker->TrackField("array_buffers_", array_buffers_);
|
||||
tracker->TrackField("shared_array_buffers", shared_array_buffers_);
|
||||
tracker->TrackField("message_ports", message_ports_);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
#include "env.h"
|
||||
#include "node_mutex.h"
|
||||
#include "sharedarraybuffer_metadata.h"
|
||||
#include <list>
|
||||
|
||||
namespace node {
|
||||
@@ -52,7 +51,7 @@ class Message : public MemoryRetainer {
|
||||
|
||||
// Internal method of Message that is called when a new SharedArrayBuffer
|
||||
// object is encountered in the incoming value's structure.
|
||||
void AddSharedArrayBuffer(const SharedArrayBufferMetadataReference& ref);
|
||||
void AddSharedArrayBuffer(std::shared_ptr<v8::BackingStore> backing_store);
|
||||
// Internal method of Message that is called once serialization finishes
|
||||
// and that transfers ownership of `data` to this message.
|
||||
void AddMessagePort(std::unique_ptr<MessagePortData>&& data);
|
||||
@@ -74,8 +73,8 @@ class Message : public MemoryRetainer {
|
||||
|
||||
private:
|
||||
MallocedBuffer<char> main_message_buf_;
|
||||
std::vector<MallocedBuffer<char>> array_buffer_contents_;
|
||||
std::vector<SharedArrayBufferMetadataReference> shared_array_buffers_;
|
||||
std::vector<std::shared_ptr<v8::BackingStore>> array_buffers_;
|
||||
std::vector<std::shared_ptr<v8::BackingStore>> shared_array_buffers_;
|
||||
std::vector<std::unique_ptr<MessagePortData>> message_ports_;
|
||||
std::vector<v8::WasmModuleObject::TransferrableModule> wasm_modules_;
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ Worker::Worker(Environment* env,
|
||||
per_isolate_opts_(per_isolate_opts),
|
||||
exec_argv_(exec_argv),
|
||||
platform_(env->isolate_data()->platform()),
|
||||
array_buffer_allocator_(ArrayBufferAllocator::Create()),
|
||||
start_profiler_idle_notifier_(env->profiler_idle_notifier_started()),
|
||||
thread_id_(Environment::AllocateThreadId()),
|
||||
env_vars_(env->env_vars()) {
|
||||
@@ -95,10 +94,6 @@ bool Worker::is_stopped() const {
|
||||
return stopped_;
|
||||
}
|
||||
|
||||
std::shared_ptr<ArrayBufferAllocator> Worker::array_buffer_allocator() {
|
||||
return array_buffer_allocator_;
|
||||
}
|
||||
|
||||
void Worker::UpdateResourceConstraints(ResourceConstraints* constraints) {
|
||||
constraints->set_stack_limit(reinterpret_cast<uint32_t*>(stack_base_));
|
||||
|
||||
@@ -138,9 +133,11 @@ class WorkerThreadData {
|
||||
: w_(w) {
|
||||
CHECK_EQ(uv_loop_init(&loop_), 0);
|
||||
|
||||
std::shared_ptr<ArrayBufferAllocator> allocator =
|
||||
ArrayBufferAllocator::Create();
|
||||
Isolate::CreateParams params;
|
||||
SetIsolateCreateParamsForNode(¶ms);
|
||||
params.array_buffer_allocator = w->array_buffer_allocator_.get();
|
||||
params.array_buffer_allocator_shared = allocator;
|
||||
|
||||
w->UpdateResourceConstraints(¶ms.constraints);
|
||||
|
||||
@@ -164,7 +161,7 @@ class WorkerThreadData {
|
||||
isolate_data_.reset(CreateIsolateData(isolate,
|
||||
&loop_,
|
||||
w_->platform_,
|
||||
w->array_buffer_allocator_.get()));
|
||||
allocator.get()));
|
||||
CHECK(isolate_data_);
|
||||
if (w_->per_isolate_opts_)
|
||||
isolate_data_->set_options(std::move(w_->per_isolate_opts_));
|
||||
|
||||
@@ -48,7 +48,6 @@ class Worker : public AsyncWrap {
|
||||
SET_SELF_SIZE(Worker)
|
||||
|
||||
bool is_stopped() const;
|
||||
std::shared_ptr<ArrayBufferAllocator> array_buffer_allocator();
|
||||
|
||||
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
static void CloneParentEnvVars(
|
||||
@@ -72,7 +71,6 @@ class Worker : public AsyncWrap {
|
||||
std::vector<std::string> argv_;
|
||||
|
||||
MultiIsolatePlatform* platform_;
|
||||
std::shared_ptr<ArrayBufferAllocator> array_buffer_allocator_;
|
||||
v8::Isolate* isolate_ = nullptr;
|
||||
bool start_profiler_idle_notifier_;
|
||||
uv_thread_t tid_;
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
#include "sharedarraybuffer_metadata.h"
|
||||
|
||||
#include "base_object-inl.h"
|
||||
#include "memory_tracker-inl.h"
|
||||
#include "node_errors.h"
|
||||
#include "node_worker.h"
|
||||
#include "util-inl.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
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
|
||||
// SABLifetimePartner* 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(std::move(r)) {
|
||||
MakeWeak();
|
||||
env->AddCleanupHook(CleanupHook, static_cast<void*>(this));
|
||||
}
|
||||
|
||||
~SABLifetimePartner() {
|
||||
env()->RemoveCleanupHook(CleanupHook, static_cast<void*>(this));
|
||||
}
|
||||
|
||||
static void CleanupHook(void* data) {
|
||||
// There is another cleanup hook attached to this object because it is a
|
||||
// BaseObject. Cleanup hooks are triggered in reverse order of addition,
|
||||
// so if this object is destroyed through GC, the destructor removes all
|
||||
// hooks associated with this object, meaning that this cleanup hook
|
||||
// only runs at the end of the Environment’s lifetime.
|
||||
// In that case, V8 still knows about the SharedArrayBuffer and tries to
|
||||
// free it when the last Isolate with access to it is disposed; for that,
|
||||
// the ArrayBuffer::Allocator needs to be kept alive longer than this
|
||||
// object and longer than the Environment instance.
|
||||
//
|
||||
// This is a workaround for https://github.com/nodejs/node-v8/issues/115
|
||||
// (introduced in V8 7.9) and we should be able to remove it once V8
|
||||
// ArrayBuffer::Allocator refactoring/removal is complete.
|
||||
SABLifetimePartner* self = static_cast<SABLifetimePartner*>(data);
|
||||
self->env()->AddArrayBufferAllocatorToKeepAliveUntilIsolateDispose(
|
||||
self->reference->allocator());
|
||||
}
|
||||
|
||||
SET_NO_MEMORY_INFO()
|
||||
SET_MEMORY_INFO_NAME(SABLifetimePartner)
|
||||
SET_SELF_SIZE(SABLifetimePartner)
|
||||
|
||||
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_NOT_NULL(partner);
|
||||
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;
|
||||
}
|
||||
|
||||
// If the SharedArrayBuffer is coming from a Worker, we need to make sure
|
||||
// that the corresponding ArrayBuffer::Allocator lives at least as long as
|
||||
// the SharedArrayBuffer itself.
|
||||
worker::Worker* w = env->worker_context();
|
||||
std::shared_ptr<v8::ArrayBuffer::Allocator> allocator =
|
||||
w != nullptr ? w->array_buffer_allocator() : nullptr;
|
||||
|
||||
SharedArrayBuffer::Contents contents = source->Externalize();
|
||||
SharedArrayBufferMetadataReference r(
|
||||
new SharedArrayBufferMetadata(contents, allocator));
|
||||
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(
|
||||
const SharedArrayBuffer::Contents& contents,
|
||||
std::shared_ptr<v8::ArrayBuffer::Allocator> allocator)
|
||||
: contents_(contents), allocator_(allocator) { }
|
||||
|
||||
SharedArrayBufferMetadata::~SharedArrayBufferMetadata() {
|
||||
contents_.Deleter()(contents_.Data(),
|
||||
contents_.ByteLength(),
|
||||
contents_.DeleterData());
|
||||
}
|
||||
|
||||
MaybeLocal<SharedArrayBuffer> SharedArrayBufferMetadata::GetSharedArrayBuffer(
|
||||
Environment* env, Local<Context> context) {
|
||||
Local<SharedArrayBuffer> obj =
|
||||
SharedArrayBuffer::New(env->isolate(),
|
||||
contents_.Data(),
|
||||
contents_.ByteLength());
|
||||
|
||||
if (AssignToSharedArrayBuffer(env, context, obj).IsNothing())
|
||||
return MaybeLocal<SharedArrayBuffer>();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
} // namespace worker
|
||||
} // namespace node
|
||||
@@ -1,72 +0,0 @@
|
||||
#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);
|
||||
std::shared_ptr<v8::ArrayBuffer::Allocator> allocator() const {
|
||||
return allocator_;
|
||||
}
|
||||
|
||||
SharedArrayBufferMetadata(SharedArrayBufferMetadata&& other) = delete;
|
||||
SharedArrayBufferMetadata& operator=(
|
||||
SharedArrayBufferMetadata&& other) = delete;
|
||||
SharedArrayBufferMetadata& operator=(
|
||||
const SharedArrayBufferMetadata&) = delete;
|
||||
SharedArrayBufferMetadata(const SharedArrayBufferMetadata&) = delete;
|
||||
|
||||
private:
|
||||
SharedArrayBufferMetadata(
|
||||
const v8::SharedArrayBuffer::Contents&,
|
||||
std::shared_ptr<v8::ArrayBuffer::Allocator>);
|
||||
|
||||
// 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);
|
||||
|
||||
v8::SharedArrayBuffer::Contents contents_;
|
||||
std::shared_ptr<v8::ArrayBuffer::Allocator> allocator_;
|
||||
};
|
||||
|
||||
} // namespace worker
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
|
||||
#endif // SRC_SHAREDARRAYBUFFER_METADATA_H_
|
||||
Reference in New Issue
Block a user