This repository has been archived by the owner on Aug 31, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 97
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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. PR-URL: #106 Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
- Loading branch information
Showing
6 changed files
with
250 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
#include "sharedarraybuffer-metadata.h" | ||
#include "base-object.h" | ||
#include "base-object-inl.h" | ||
|
||
using v8::Context; | ||
using v8::Function; | ||
using v8::FunctionCallbackInfo; | ||
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(); | ||
|
||
{ | ||
Local<FunctionTemplate> m = env->NewFunctionTemplate( | ||
[](const FunctionCallbackInfo<Value>& info) { | ||
CHECK(info.IsConstructCall()); | ||
}); | ||
m->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), | ||
"SABLifetimePartner")); | ||
m->InstanceTemplate()->SetInternalFieldCount(1); | ||
|
||
env->set_sab_lifetimepartner_constructor_template(m); | ||
} | ||
|
||
return GetSABLifetimePartnerConstructor(env, context); | ||
} | ||
|
||
class SABLifetimePartner : public BaseObject { | ||
public: | ||
SABLifetimePartner(Environment* env, | ||
Local<Object> obj, | ||
SharedArrayBufferMetadataReference r) | ||
: BaseObject(env, obj), | ||
reference(r) { | ||
MakeWeak<SABLifetimePartner>(this); | ||
} | ||
|
||
SharedArrayBufferMetadataReference reference; | ||
}; | ||
|
||
} // anonymous namespace | ||
|
||
SharedArrayBufferMetadataReference | ||
SharedArrayBufferMetadata::ForIncomingSharedArrayBuffer( | ||
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)) { | ||
if (!source->IsExternal()) { | ||
env->ThrowError("Found internalized SharedArrayBuffer with " | ||
"lifetime partner object"); | ||
return nullptr; | ||
} | ||
|
||
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, we did not externalize it. In that case, there is no | ||
// way to serialize it. | ||
env->ThrowError("Cannot serialize externalized SharedArrayBuffer"); | ||
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) { | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
#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 ForIncomingSharedArrayBuffer( | ||
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_ |