Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
src: implement IsolateData serialization and deserialization
This patch allows serializing per-isolate data into an isolate
snapshot and deserializing them from an isolate snapthot.
  • Loading branch information
joyeecheung committed Apr 20, 2019
commit e354795ae2275a6438d686cfb52e3e59b0ae359c
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 {
Comment thread
joyeecheung marked this conversation as resolved.
Outdated
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