mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
Store the full information we have on a given `SharedArrayBuffer`, and use the deleter provided by the JS engine to free the memory when that is needed. This fixes memory lifetime management for WASM buffers that are passed through a `MessageChannel` (e.g. between threads). PR-URL: https://github.com/nodejs/node/pull/25307 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
138 lines
4.2 KiB
C++
138 lines
4.2 KiB
C++
#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
|
|
// 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(r) {
|
|
MakeWeak();
|
|
}
|
|
|
|
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_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));
|
|
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)
|
|
: contents_(contents) { }
|
|
|
|
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
|