From 631bea8fd2c3e9a344a8e2c53b6c48007c650685 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 18 Apr 2019 16:25:32 +0800 Subject: [PATCH] src: implement IsolateData serialization and deserialization This patch allows serializing per-isolate data into an isolate snapshot and deserializing them from an isolate snapthot. PR-URL: https://github.com/nodejs/node/pull/27321 Refs: https://github.com/nodejs/node/issues/17058 Reviewed-By: Anna Henningsen Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis --- src/env.cc | 147 +++++++++++++++++++++++++++----------- src/env.h | 8 ++- src/node_main_instance.cc | 25 +++++-- src/node_main_instance.h | 19 ++++- 4 files changed, 149 insertions(+), 50 deletions(-) diff --git a/src/env.cc b/src/env.cc index cbb8ab6fe27c02..5ef32e2b0ef80c 100644 --- a/src/env.cc +++ b/src/env.cc @@ -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; @@ -49,22 +50,58 @@ int const Environment::kNodeContextTag = 0x6e6f64; void* const Environment::kNodeContextTagPtr = const_cast( static_cast(&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 IsolateData::Serialize(SnapshotCreator* creator) { + Isolate* isolate = creator->GetIsolate(); + std::vector 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 field = \ + isolate_->GetDataFromSnapshotOnce(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. // @@ -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(StringValue), \ - NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked())); +#define V(PropertyName, StringValue) \ + PropertyName##_.Set( \ + isolate_, \ + Private::New(isolate_, \ + String::NewFromOneByte( \ + isolate_, \ + reinterpret_cast(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(StringValue), \ - NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked())); +#define V(PropertyName, StringValue) \ + PropertyName##_.Set( \ + isolate_, \ + Symbol::New(isolate_, \ + String::NewFromOneByte( \ + isolate_, \ + reinterpret_cast(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(StringValue), \ - NewStringType::kInternalized, \ - sizeof(StringValue) - 1).ToLocalChecked()); +#define V(PropertyName, StringValue) \ + PropertyName##_.Set( \ + isolate_, \ + String::NewFromOneByte(isolate_, \ + reinterpret_cast(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())); diff --git a/src/env.h b/src/env.h index def7cba3886ac2..24050cc9f4404f 100644 --- a/src/env.h +++ b/src/env.h @@ -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" @@ -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 Serialize(v8::SnapshotCreator* creator); inline uv_loop_t* event_loop() const; inline MultiIsolatePlatform* platform() const; @@ -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) diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 4b05149b23c163..11dfadabb9422d 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -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); } @@ -41,7 +42,8 @@ NodeMainInstance::NodeMainInstance(Isolate::CreateParams* params, uv_loop_t* event_loop, MultiIsolatePlatform* platform, const std::vector& args, - const std::vector& exec_args) + const std::vector& exec_args, + const IndexArray* per_isolate_data_indexes) : args_(args), exec_args_(exec_args), array_buffer_allocator_(ArrayBufferAllocator::Create()), @@ -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() { @@ -160,6 +172,9 @@ std::unique_ptr NodeMainInstance::CreateMainEnvironment( } Local context = NewContext(isolate_); + if (deserialize_mode_) { + SetIsolateUpForNode(isolate_, IsolateSettingCategories::kErrorHandlers); + } CHECK(!context.IsEmpty()); Context::Scope context_scope(context); diff --git a/src/node_main_instance.h b/src/node_main_instance.h index 0a8be137a0d3ee..615fda1f4945c3 100644 --- a/src/node_main_instance.h +++ b/src/node_main_instance.h @@ -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: // @@ -45,12 +57,15 @@ class NodeMainInstance { uv_loop_t* event_loop, MultiIsolatePlatform* platform, const std::vector& args, - const std::vector& exec_args); + const std::vector& 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 CreateMainEnvironment(int* exit_code); @@ -66,6 +81,7 @@ class NodeMainInstance { MultiIsolatePlatform* platform, const std::vector& args, const std::vector& exec_args); + std::vector args_; std::vector exec_args_; std::unique_ptr array_buffer_allocator_; @@ -73,6 +89,7 @@ class NodeMainInstance { MultiIsolatePlatform* platform_; std::unique_ptr isolate_data_; bool owns_isolate_ = false; + bool deserialize_mode_ = false; }; } // namespace node