Skip to content

Commit

Permalink
src: implement IsolateData serialization and deserialization
Browse files Browse the repository at this point in the history
This patch allows serializing per-isolate data into an isolate
snapshot and deserializing them from an isolate snapthot.

PR-URL: #27321
Refs: #17058
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  • Loading branch information
joyeecheung committed Apr 23, 2019
1 parent 70d887e commit ffcc949
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 50 deletions.
147 changes: 104 additions & 43 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ using v8::NewStringType;
using v8::Number;
using v8::Object;
using v8::Private;
using v8::SnapshotCreator;
using v8::StackTrace;
using v8::String;
using v8::Symbol;
Expand All @@ -49,22 +50,58 @@ int const Environment::kNodeContextTag = 0x6e6f64;
void* const Environment::kNodeContextTagPtr = const_cast<void*>(
static_cast<const void*>(&Environment::kNodeContextTag));

IsolateData::IsolateData(Isolate* isolate,
uv_loop_t* event_loop,
MultiIsolatePlatform* platform,
ArrayBufferAllocator* node_allocator)
: isolate_(isolate),
event_loop_(event_loop),
allocator_(isolate->GetArrayBufferAllocator()),
node_allocator_(node_allocator == nullptr ?
nullptr : node_allocator->GetImpl()),
uses_node_allocator_(allocator_ == node_allocator_),
platform_(platform) {
CHECK_NOT_NULL(allocator_);
std::vector<size_t> IsolateData::Serialize(SnapshotCreator* creator) {
Isolate* isolate = creator->GetIsolate();
std::vector<size_t> indexes;
HandleScope handle_scope(isolate);
// XXX(joyeecheung): technically speaking, the indexes here should be
// consecutive and we could just return a range instead of an array,
// but that's not part of the V8 API contract so we use an array
// just to be safe.

#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
#define V(TypeName, PropertyName) \
indexes.push_back(creator->AddData(PropertyName##_.Get(isolate)));
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
PER_ISOLATE_SYMBOL_PROPERTIES(VY)
PER_ISOLATE_STRING_PROPERTIES(VS)
#undef V
#undef VY
#undef VS
#undef VP

options_.reset(
new PerIsolateOptions(*(per_process::cli_options->per_isolate)));
return indexes;
}

void IsolateData::DeserializeProperties(
const NodeMainInstance::IndexArray* indexes) {
size_t i = 0;
HandleScope handle_scope(isolate_);

#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
#define V(TypeName, PropertyName) \
do { \
MaybeLocal<TypeName> field = \
isolate_->GetDataFromSnapshotOnce<TypeName>(indexes->Get(i++)); \
if (field.IsEmpty()) { \
fprintf(stderr, "Failed to deserialize " #PropertyName "\n"); \
} \
PropertyName##_.Set(isolate_, field.ToLocalChecked()); \
} while (0);
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
PER_ISOLATE_SYMBOL_PROPERTIES(VY)
PER_ISOLATE_STRING_PROPERTIES(VS)
#undef V
#undef VY
#undef VS
#undef VP
}

void IsolateData::CreateProperties() {
// Create string and private symbol properties as internalized one byte
// strings after the platform is properly initialized.
//
Expand All @@ -76,44 +113,68 @@ IsolateData::IsolateData(Isolate* isolate,
// One byte because our strings are ASCII and we can safely skip V8's UTF-8
// decoding step.

HandleScope handle_scope(isolate);
HandleScope handle_scope(isolate_);

#define V(PropertyName, StringValue) \
PropertyName ## _.Set( \
isolate, \
Private::New( \
isolate, \
String::NewFromOneByte( \
isolate, \
reinterpret_cast<const uint8_t*>(StringValue), \
NewStringType::kInternalized, \
sizeof(StringValue) - 1).ToLocalChecked()));
#define V(PropertyName, StringValue) \
PropertyName##_.Set( \
isolate_, \
Private::New(isolate_, \
String::NewFromOneByte( \
isolate_, \
reinterpret_cast<const uint8_t*>(StringValue), \
NewStringType::kInternalized, \
sizeof(StringValue) - 1) \
.ToLocalChecked()));
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
#undef V
#define V(PropertyName, StringValue) \
PropertyName ## _.Set( \
isolate, \
Symbol::New( \
isolate, \
String::NewFromOneByte( \
isolate, \
reinterpret_cast<const uint8_t*>(StringValue), \
NewStringType::kInternalized, \
sizeof(StringValue) - 1).ToLocalChecked()));
#define V(PropertyName, StringValue) \
PropertyName##_.Set( \
isolate_, \
Symbol::New(isolate_, \
String::NewFromOneByte( \
isolate_, \
reinterpret_cast<const uint8_t*>(StringValue), \
NewStringType::kInternalized, \
sizeof(StringValue) - 1) \
.ToLocalChecked()));
PER_ISOLATE_SYMBOL_PROPERTIES(V)
#undef V
#define V(PropertyName, StringValue) \
PropertyName ## _.Set( \
isolate, \
String::NewFromOneByte( \
isolate, \
reinterpret_cast<const uint8_t*>(StringValue), \
NewStringType::kInternalized, \
sizeof(StringValue) - 1).ToLocalChecked());
#define V(PropertyName, StringValue) \
PropertyName##_.Set( \
isolate_, \
String::NewFromOneByte(isolate_, \
reinterpret_cast<const uint8_t*>(StringValue), \
NewStringType::kInternalized, \
sizeof(StringValue) - 1) \
.ToLocalChecked());
PER_ISOLATE_STRING_PROPERTIES(V)
#undef V
}

IsolateData::IsolateData(Isolate* isolate,
uv_loop_t* event_loop,
MultiIsolatePlatform* platform,
ArrayBufferAllocator* node_allocator,
const NodeMainInstance::IndexArray* indexes)
: isolate_(isolate),
event_loop_(event_loop),
allocator_(isolate->GetArrayBufferAllocator()),
node_allocator_(node_allocator == nullptr ? nullptr
: node_allocator->GetImpl()),
uses_node_allocator_(allocator_ == node_allocator_),
platform_(platform) {
CHECK_NOT_NULL(allocator_);

options_.reset(
new PerIsolateOptions(*(per_process::cli_options->per_isolate)));

if (indexes == nullptr) {
CreateProperties();
} else {
DeserializeProperties(indexes);
}
}

void IsolateData::MemoryInfo(MemoryTracker* tracker) const {
#define V(PropertyName, StringValue) \
tracker->TrackField(#PropertyName, PropertyName(isolate()));
Expand Down
8 changes: 7 additions & 1 deletion src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "node.h"
#include "node_binding.h"
#include "node_http2_state.h"
#include "node_main_instance.h"
#include "node_options.h"
#include "req_wrap.h"
#include "util.h"
Expand Down Expand Up @@ -418,10 +419,12 @@ class IsolateData : public MemoryRetainer {
IsolateData(v8::Isolate* isolate,
uv_loop_t* event_loop,
MultiIsolatePlatform* platform = nullptr,
ArrayBufferAllocator* node_allocator = nullptr);
ArrayBufferAllocator* node_allocator = nullptr,
const NodeMainInstance::IndexArray* indexes = nullptr);
SET_MEMORY_INFO_NAME(IsolateData);
SET_SELF_SIZE(IsolateData);
void MemoryInfo(MemoryTracker* tracker) const override;
std::vector<size_t> Serialize(v8::SnapshotCreator* creator);

inline uv_loop_t* event_loop() const;
inline MultiIsolatePlatform* platform() const;
Expand Down Expand Up @@ -451,6 +454,9 @@ class IsolateData : public MemoryRetainer {
IsolateData& operator=(const IsolateData&) = delete;

private:
void DeserializeProperties(const NodeMainInstance::IndexArray* indexes);
void CreateProperties();

#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
Expand Down
25 changes: 20 additions & 5 deletions src/node_main_instance.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ NodeMainInstance::NodeMainInstance(Isolate* isolate,
isolate_(isolate),
platform_(platform),
isolate_data_(nullptr),
owns_isolate_(false) {
owns_isolate_(false),
deserialize_mode_(false) {
isolate_data_.reset(new IsolateData(isolate_, event_loop, platform, nullptr));
SetIsolateUpForNode(isolate_, IsolateSettingCategories::kMisc);
}
Expand All @@ -41,7 +42,8 @@ NodeMainInstance::NodeMainInstance(Isolate::CreateParams* params,
uv_loop_t* event_loop,
MultiIsolatePlatform* platform,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args)
const std::vector<std::string>& exec_args,
const IndexArray* per_isolate_data_indexes)
: args_(args),
exec_args_(exec_args),
array_buffer_allocator_(ArrayBufferAllocator::Create()),
Expand All @@ -58,10 +60,20 @@ NodeMainInstance::NodeMainInstance(Isolate::CreateParams* params,
SetIsolateCreateParamsForNode(params);
Isolate::Initialize(isolate_, *params);

isolate_data_.reset(new IsolateData(
isolate_, event_loop, platform, array_buffer_allocator_.get()));
deserialize_mode_ = per_isolate_data_indexes != nullptr;
// If the indexes are not nullptr, we are not deserializing
CHECK_IMPLIES(deserialize_mode_, params->external_references != nullptr);
isolate_data_.reset(new IsolateData(isolate_,
event_loop,
platform,
array_buffer_allocator_.get(),
per_isolate_data_indexes));
SetIsolateUpForNode(isolate_, IsolateSettingCategories::kMisc);
SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers);
if (!deserialize_mode_) {
// If in deserialize mode, delay until after the deserialization is
// complete.
SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers);
}
}

void NodeMainInstance::Dispose() {
Expand Down Expand Up @@ -160,6 +172,9 @@ std::unique_ptr<Environment> NodeMainInstance::CreateMainEnvironment(
}

Local<Context> context = NewContext(isolate_);
if (deserialize_mode_) {
SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers);
}
CHECK(!context.IsEmpty());
Context::Scope context_scope(context);

Expand Down
19 changes: 18 additions & 1 deletion src/node_main_instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ namespace node {
// We may be able to create an abstract class to reuse some of the routines.
class NodeMainInstance {
public:
// An array of indexes that can be used to deserialize data from a V8
// snapshot.
struct IndexArray {
const size_t* data;
size_t length;

size_t Get(size_t index) const {
DCHECK_LT(index, length);
return data[index];
}
};

// To create a main instance that does not own the isoalte,
// The caller needs to do:
//
Expand Down Expand Up @@ -45,12 +57,15 @@ class NodeMainInstance {
uv_loop_t* event_loop,
MultiIsolatePlatform* platform,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args);
const std::vector<std::string>& exec_args,
const IndexArray* per_isolate_data_indexes = nullptr);
~NodeMainInstance();

// Start running the Node.js instances, return the exit code when finished.
int Run();

IsolateData* isolate_data() { return isolate_data_.get(); }

// TODO(joyeecheung): align this with the CreateEnvironment exposed in node.h
// and the environment creation routine in workers somehow.
std::unique_ptr<Environment> CreateMainEnvironment(int* exit_code);
Expand All @@ -66,13 +81,15 @@ class NodeMainInstance {
MultiIsolatePlatform* platform,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args);

std::vector<std::string> args_;
std::vector<std::string> exec_args_;
std::unique_ptr<ArrayBufferAllocator> array_buffer_allocator_;
v8::Isolate* isolate_;
MultiIsolatePlatform* platform_;
std::unique_ptr<IsolateData> isolate_data_;
bool owns_isolate_ = false;
bool deserialize_mode_ = false;
};

} // namespace node
Expand Down

0 comments on commit ffcc949

Please sign in to comment.