worker: add `SharedArrayBuffer` sharing · nodejs/node@d1f372f
1+#include "sharedarraybuffer_metadata.h"
2+#include "base_object.h"
3+#include "base_object-inl.h"
4+#include "node_errors.h"
5+6+using v8::Context;
7+using v8::Function;
8+using v8::FunctionTemplate;
9+using v8::Local;
10+using v8::Maybe;
11+using v8::MaybeLocal;
12+using v8::Nothing;
13+using v8::Object;
14+using v8::SharedArrayBuffer;
15+using v8::Value;
16+17+namespace node {
18+namespace worker {
19+20+namespace {
21+22+// Yield a JS constructor for SABLifetimePartner objects in the form of a
23+// standard API object, that has a single field for containing the raw
24+// SABLiftimePartner* pointer.
25+Local<Function> GetSABLifetimePartnerConstructor(
26+ Environment* env, Local<Context> context) {
27+ Local<FunctionTemplate> templ;
28+ templ = env->sab_lifetimepartner_constructor_template();
29+if (!templ.IsEmpty())
30+return templ->GetFunction(context).ToLocalChecked();
31+32+ templ = BaseObject::MakeLazilyInitializedJSTemplate(env);
33+ templ->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(),
34+"SABLifetimePartner"));
35+ env->set_sab_lifetimepartner_constructor_template(templ);
36+37+return GetSABLifetimePartnerConstructor(env, context);
38+}
39+40+class SABLifetimePartner : public BaseObject {
41+public:
42+SABLifetimePartner(Environment* env,
43+ Local<Object> obj,
44+ SharedArrayBufferMetadataReference r)
45+ : BaseObject(env, obj),
46+reference(r) {
47+MakeWeak();
48+ }
49+50+ SharedArrayBufferMetadataReference reference;
51+};
52+53+} // anonymous namespace
54+55+SharedArrayBufferMetadataReference
56+SharedArrayBufferMetadata::ForSharedArrayBuffer(
57+ Environment* env,
58+ Local<Context> context,
59+ Local<SharedArrayBuffer> source) {
60+ Local<Value> lifetime_partner;
61+62+if (!source->GetPrivate(context,
63+ env->sab_lifetimepartner_symbol())
64+ .ToLocal(&lifetime_partner)) {
65+return nullptr;
66+ }
67+68+if (lifetime_partner->IsObject() &&
69+ env->sab_lifetimepartner_constructor_template()
70+ ->HasInstance(lifetime_partner)) {
71+CHECK(source->IsExternal());
72+ SABLifetimePartner* partner =
73+ Unwrap<SABLifetimePartner>(lifetime_partner.As<Object>());
74+CHECK_NE(partner, nullptr);
75+return partner->reference;
76+ }
77+78+if (source->IsExternal()) {
79+// If this is an external SharedArrayBuffer but we do not see a lifetime
80+// partner object, it was not us who externalized it. In that case, there
81+// is no way to serialize it, because it's unclear how the memory
82+// is actually owned.
83+THROW_ERR_TRANSFERRING_EXTERNALIZED_SHAREDARRAYBUFFER(env);
84+return nullptr;
85+ }
86+87+ SharedArrayBuffer::Contents contents = source->Externalize();
88+ SharedArrayBufferMetadataReference r(new SharedArrayBufferMetadata(
89+ contents.Data(), contents.ByteLength()));
90+if (r->AssignToSharedArrayBuffer(env, context, source).IsNothing())
91+return nullptr;
92+return r;
93+}
94+95+Maybe<bool> SharedArrayBufferMetadata::AssignToSharedArrayBuffer(
96+ Environment* env, Local<Context> context,
97+ Local<SharedArrayBuffer> target) {
98+CHECK(target->IsExternal());
99+ Local<Function> ctor = GetSABLifetimePartnerConstructor(env, context);
100+ Local<Object> obj;
101+if (!ctor->NewInstance(context).ToLocal(&obj))
102+return Nothing<bool>();
103+104+new SABLifetimePartner(env, obj, shared_from_this());
105+return target->SetPrivate(context,
106+ env->sab_lifetimepartner_symbol(),
107+ obj);
108+}
109+110+SharedArrayBufferMetadata::SharedArrayBufferMetadata(void* data, size_t size)
111+ : data(data), size(size) { }
112+113+SharedArrayBufferMetadata::~SharedArrayBufferMetadata() {
114+free(data);
115+}
116+117+MaybeLocal<SharedArrayBuffer> SharedArrayBufferMetadata::GetSharedArrayBuffer(
118+ Environment* env, Local<Context> context) {
119+ Local<SharedArrayBuffer> obj =
120+SharedArrayBuffer::New(env->isolate(), data, size);
121+122+if (AssignToSharedArrayBuffer(env, context, obj).IsNothing())
123+return MaybeLocal<SharedArrayBuffer>();
124+125+return obj;
126+}
127+128+} // namespace worker
129+} // namespace node