Skip to content

Commit

Permalink
src: prepare v8 platform for multi-isolate support
Browse files Browse the repository at this point in the history
This splits the task queue used for asynchronous tasks scheduled
by V8 in per-isolate queues, so that multiple threads can be supported.

Original-PR-URL: ayojs/ayo#89
Original-Reviewed-By: Timothy Gu <timothygu99@gmail.com>
PR-URL: #16700
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
addaleax authored and MylesBorins committed Dec 11, 2017
1 parent 121245f commit 37a60a8
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 93 deletions.
42 changes: 7 additions & 35 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,41 +37,9 @@

namespace node {

inline IsolateData::IsolateData(v8::Isolate* isolate, uv_loop_t* event_loop,
uint32_t* zero_fill_field) :

// Create string and private symbol properties as internalized one byte strings.
//
// Internalized because it makes property lookups a little faster and because
// the string is created in the old space straight away. It's going to end up
// in the old space sooner or later anyway but now it doesn't go through
// v8::Eternal's new space handling first.
//
// One byte because our strings are ASCII and we can safely skip V8's UTF-8
// decoding step. It's a one-time cost, but why pay it when you don't have to?
#define V(PropertyName, StringValue) \
PropertyName ## _( \
isolate, \
v8::Private::New( \
isolate, \
v8::String::NewFromOneByte( \
isolate, \
reinterpret_cast<const uint8_t*>(StringValue), \
v8::NewStringType::kInternalized, \
sizeof(StringValue) - 1).ToLocalChecked())),
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
#undef V
#define V(PropertyName, StringValue) \
PropertyName ## _( \
isolate, \
v8::String::NewFromOneByte( \
isolate, \
reinterpret_cast<const uint8_t*>(StringValue), \
v8::NewStringType::kInternalized, \
sizeof(StringValue) - 1).ToLocalChecked()),
PER_ISOLATE_STRING_PROPERTIES(V)
#undef V
event_loop_(event_loop), zero_fill_field_(zero_fill_field) {}
inline v8::Isolate* IsolateData::isolate() const {
return isolate_;
}

inline uv_loop_t* IsolateData::event_loop() const {
return event_loop_;
Expand All @@ -81,6 +49,10 @@ inline uint32_t* IsolateData::zero_fill_field() const {
return zero_fill_field_;
}

inline MultiIsolatePlatform* IsolateData::platform() const {
return platform_;
}

inline Environment::AsyncHooks::AsyncHooks(v8::Isolate* isolate)
: isolate_(isolate),
fields_(isolate, kFieldsCount),
Expand Down
52 changes: 52 additions & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,62 @@ namespace node {
using v8::Context;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Message;
using v8::Private;
using v8::StackFrame;
using v8::StackTrace;
using v8::String;

IsolateData::IsolateData(Isolate* isolate,
uv_loop_t* event_loop,
MultiIsolatePlatform* platform,
uint32_t* zero_fill_field) :

// Create string and private symbol properties as internalized one byte strings.
//
// Internalized because it makes property lookups a little faster and because
// the string is created in the old space straight away. It's going to end up
// in the old space sooner or later anyway but now it doesn't go through
// v8::Eternal's new space handling first.
//
// One byte because our strings are ASCII and we can safely skip V8's UTF-8
// decoding step. It's a one-time cost, but why pay it when you don't have to?
#define V(PropertyName, StringValue) \
PropertyName ## _( \
isolate, \
Private::New( \
isolate, \
String::NewFromOneByte( \
isolate, \
reinterpret_cast<const uint8_t*>(StringValue), \
v8::NewStringType::kInternalized, \
sizeof(StringValue) - 1).ToLocalChecked())),
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V)
#undef V
#define V(PropertyName, StringValue) \
PropertyName ## _( \
isolate, \
String::NewFromOneByte( \
isolate, \
reinterpret_cast<const uint8_t*>(StringValue), \
v8::NewStringType::kInternalized, \
sizeof(StringValue) - 1).ToLocalChecked()),
PER_ISOLATE_STRING_PROPERTIES(V)
#undef V
isolate_(isolate),
event_loop_(event_loop),
zero_fill_field_(zero_fill_field),
platform_(platform) {
if (platform_ != nullptr)
platform_->RegisterIsolate(this, event_loop);
}

IsolateData::~IsolateData() {
if (platform_ != nullptr)
platform_->UnregisterIsolate(this);
}

void Environment::Start(int argc,
const char* const* argv,
Expand Down
10 changes: 8 additions & 2 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,13 @@ struct node_async_ids {

class IsolateData {
public:
inline IsolateData(v8::Isolate* isolate, uv_loop_t* event_loop,
uint32_t* zero_fill_field = nullptr);
IsolateData(v8::Isolate* isolate, uv_loop_t* event_loop,
MultiIsolatePlatform* platform = nullptr,
uint32_t* zero_fill_field = nullptr);
~IsolateData();
inline uv_loop_t* event_loop() const;
inline uint32_t* zero_fill_field() const;
inline MultiIsolatePlatform* platform() const;

#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)
Expand All @@ -360,6 +363,7 @@ class IsolateData {
#undef VP

std::unordered_map<nghttp2_rcbuf*, v8::Eternal<v8::String>> http2_static_strs;
inline v8::Isolate* isolate() const;

private:
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
Expand All @@ -372,8 +376,10 @@ class IsolateData {
#undef VS
#undef VP

v8::Isolate* const isolate_;
uv_loop_t* const event_loop_;
uint32_t* const zero_fill_field_;
MultiIsolatePlatform* platform_;

DISALLOW_COPY_AND_ASSIGN(IsolateData);
};
Expand Down
2 changes: 1 addition & 1 deletion src/inspector_agent.cc
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ class NodeInspectorClient : public V8InspectorClient {
terminated_ = false;
running_nested_loop_ = true;
while (!terminated_ && channel_->waitForFrontendMessage()) {
platform_->FlushForegroundTasksInternal();
platform_->FlushForegroundTasks(env_->isolate());
}
terminated_ = false;
running_nested_loop_ = false;
Expand Down
41 changes: 30 additions & 11 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,10 @@ node::DebugOptions debug_options;

static struct {
#if NODE_USE_V8_PLATFORM
void Initialize(int thread_pool_size, uv_loop_t* loop) {
void Initialize(int thread_pool_size) {
tracing_agent_ =
trace_enabled ? new tracing::Agent() : nullptr;
platform_ = new NodePlatform(thread_pool_size, loop,
platform_ = new NodePlatform(thread_pool_size,
trace_enabled ? tracing_agent_->GetTracingController() : nullptr);
V8::InitializePlatform(platform_);
tracing::TraceEventHelper::SetTracingController(
Expand All @@ -280,8 +280,8 @@ static struct {
tracing_agent_ = nullptr;
}

void DrainVMTasks() {
platform_->DrainBackgroundTasks();
void DrainVMTasks(Isolate* isolate) {
platform_->DrainBackgroundTasks(isolate);
}

#if HAVE_INSPECTOR
Expand All @@ -306,12 +306,16 @@ static struct {
tracing_agent_->Stop();
}

NodePlatform* Platform() {
return platform_;
}

tracing::Agent* tracing_agent_;
NodePlatform* platform_;
#else // !NODE_USE_V8_PLATFORM
void Initialize(int thread_pool_size, uv_loop_t* loop) {}
void Initialize(int thread_pool_size) {}
void Dispose() {}
void DrainVMTasks() {}
void DrainVMTasks(Isolate* isolate) {}
bool StartInspector(Environment *env, const char* script_path,
const node::DebugOptions& options) {
env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0");
Expand All @@ -323,6 +327,10 @@ static struct {
"so event tracing is not available.\n");
}
void StopTracingAgent() {}

NodePlatform* Platform() {
return nullptr;
}
#endif // !NODE_USE_V8_PLATFORM

#if !NODE_USE_V8_PLATFORM || !HAVE_INSPECTOR
Expand Down Expand Up @@ -4769,7 +4777,14 @@ int EmitExit(Environment* env) {


IsolateData* CreateIsolateData(Isolate* isolate, uv_loop_t* loop) {
return new IsolateData(isolate, loop);
return new IsolateData(isolate, loop, nullptr);
}

IsolateData* CreateIsolateData(
Isolate* isolate,
uv_loop_t* loop,
MultiIsolatePlatform* platform) {
return new IsolateData(isolate, loop, platform);
}


Expand Down Expand Up @@ -4854,7 +4869,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
do {
uv_run(env.event_loop(), UV_RUN_DEFAULT);

v8_platform.DrainVMTasks();
v8_platform.DrainVMTasks(isolate);

more = uv_loop_alive(env.event_loop());
if (more)
Expand All @@ -4875,7 +4890,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
RunAtExit(&env);
uv_key_delete(&thread_local_env);

v8_platform.DrainVMTasks();
v8_platform.DrainVMTasks(isolate);
WaitForInspectorDisconnect(&env);
#if defined(LEAK_SANITIZER)
__lsan_do_leak_check();
Expand Down Expand Up @@ -4918,7 +4933,11 @@ inline int Start(uv_loop_t* event_loop,
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
IsolateData isolate_data(isolate, event_loop, allocator.zero_fill_field());
IsolateData isolate_data(
isolate,
event_loop,
v8_platform.Platform(),
allocator.zero_fill_field());
exit_code = Start(isolate, &isolate_data, argc, argv, exec_argc, exec_argv);
}

Expand Down Expand Up @@ -4965,7 +4984,7 @@ int Start(int argc, char** argv) {
V8::SetEntropySource(crypto::EntropySource);
#endif // HAVE_OPENSSL

v8_platform.Initialize(v8_thread_pool_size, uv_default_loop());
v8_platform.Initialize(v8_thread_pool_size);
// Enable tracing when argv has --trace-events-enabled.
if (trace_enabled) {
fprintf(stderr, "Warning: Trace event is an experimental feature "
Expand Down
24 changes: 22 additions & 2 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#endif

#include "v8.h" // NOLINT(build/include_order)
#include "v8-platform.h" // NOLINT(build/include_order)
#include "node_version.h" // NODE_MODULE_VERSION

#define NODE_MAKE_VERSION(major, minor, patch) \
Expand Down Expand Up @@ -209,8 +210,27 @@ NODE_EXTERN void Init(int* argc,
class IsolateData;
class Environment;

NODE_EXTERN IsolateData* CreateIsolateData(v8::Isolate* isolate,
struct uv_loop_s* loop);
class MultiIsolatePlatform : public v8::Platform {
public:
virtual ~MultiIsolatePlatform() { }
virtual void DrainBackgroundTasks(v8::Isolate* isolate) = 0;

// These will be called by the `IsolateData` creation/destruction functions.
virtual void RegisterIsolate(IsolateData* isolate_data,
struct uv_loop_s* loop) = 0;
virtual void UnregisterIsolate(IsolateData* isolate_data) = 0;
};

// If `platform` is passed, it will be used to register new Worker instances.
// It can be `nullptr`, in which case creating new Workers inside of
// Environments that use this `IsolateData` will not work.
NODE_EXTERN IsolateData* CreateIsolateData(
v8::Isolate* isolate,
struct uv_loop_s* loop);
NODE_EXTERN IsolateData* CreateIsolateData(
v8::Isolate* isolate,
struct uv_loop_s* loop,
MultiIsolatePlatform* platform);
NODE_EXTERN void FreeIsolateData(IsolateData* isolate_data);

NODE_EXTERN Environment* CreateEnvironment(IsolateData* isolate_data,
Expand Down
Loading

0 comments on commit 37a60a8

Please sign in to comment.