summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/env-inl.h42
-rw-r--r--src/env.cc54
-rw-r--r--src/env.h10
-rw-r--r--src/inspector_agent.cc2
-rw-r--r--src/node.cc41
-rw-r--r--src/node.h24
-rw-r--r--src/node_platform.cc120
-rw-r--r--src/node_platform.h51
8 files changed, 258 insertions, 86 deletions
diff --git a/src/env-inl.h b/src/env-inl.h
index 83b87fadac..5c28b2334b 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -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_;
@@ -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),
diff --git a/src/env.cc b/src/env.cc
index 6abb224293..01bfb8a95e 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -2,6 +2,8 @@
#include "async-wrap.h"
#include "v8-profiler.h"
#include "node_buffer.h"
+#include "req-wrap-inl.h"
+#include "node_platform.h"
#if defined(_MSC_VER)
#define getpid GetCurrentProcessId
@@ -17,10 +19,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,
diff --git a/src/env.h b/src/env.h
index 37462ac9d0..296fcf4877 100644
--- a/src/env.h
+++ b/src/env.h
@@ -342,10 +342,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)
@@ -358,6 +361,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)
@@ -370,8 +374,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);
};
diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc
index efc0ffc15c..d5b3b43ec2 100644
--- a/src/inspector_agent.cc
+++ b/src/inspector_agent.cc
@@ -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;
diff --git a/src/node.cc b/src/node.cc
index 563dec7896..3ff1435aa6 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -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(
@@ -280,8 +280,8 @@ static struct {
tracing_agent_ = nullptr;
}
- void DrainVMTasks() {
- platform_->DrainBackgroundTasks();
+ void DrainVMTasks(Isolate* isolate) {
+ platform_->DrainBackgroundTasks(isolate);
}
#if HAVE_INSPECTOR
@@ -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");
@@ -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
@@ -4441,7 +4449,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);
}
@@ -4526,7 +4541,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)
@@ -4547,7 +4562,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();
@@ -4590,7 +4605,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);
}
@@ -4637,7 +4656,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 "
diff --git a/src/node.h b/src/node.h
index aa8738a567..0656428c0b 100644
--- a/src/node.h
+++ b/src/node.h
@@ -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) \
@@ -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,
diff --git a/src/node_platform.cc b/src/node_platform.cc
index 56b13b8437..ec2fca6c41 100644
--- a/src/node_platform.cc
+++ b/src/node_platform.cc
@@ -1,6 +1,8 @@
#include "node_platform.h"
#include "node_internals.h"
+#include "env.h"
+#include "env-inl.h"
#include "util.h"
namespace node {
@@ -13,11 +15,6 @@ using v8::Platform;
using v8::Task;
using v8::TracingController;
-static void FlushTasks(uv_async_t* handle) {
- NodePlatform* platform = static_cast<NodePlatform*>(handle->data);
- platform->FlushForegroundTasksInternal();
-}
-
static void BackgroundRunner(void* data) {
TaskQueue<Task>* background_tasks = static_cast<TaskQueue<Task>*>(data);
while (Task* task = background_tasks->BlockingPop()) {
@@ -27,12 +24,51 @@ static void BackgroundRunner(void* data) {
}
}
-NodePlatform::NodePlatform(int thread_pool_size, uv_loop_t* loop,
- TracingController* tracing_controller)
- : loop_(loop) {
- CHECK_EQ(0, uv_async_init(loop, &flush_tasks_, FlushTasks));
- flush_tasks_.data = static_cast<void*>(this);
- uv_unref(reinterpret_cast<uv_handle_t*>(&flush_tasks_));
+PerIsolatePlatformData::PerIsolatePlatformData(
+ v8::Isolate* isolate, uv_loop_t* loop)
+ : isolate_(isolate), loop_(loop) {
+ flush_tasks_ = new uv_async_t();
+ CHECK_EQ(0, uv_async_init(loop, flush_tasks_, FlushTasks));
+ flush_tasks_->data = static_cast<void*>(this);
+ uv_unref(reinterpret_cast<uv_handle_t*>(flush_tasks_));
+}
+
+void PerIsolatePlatformData::FlushTasks(uv_async_t* handle) {
+ auto platform_data = static_cast<PerIsolatePlatformData*>(handle->data);
+ platform_data->FlushForegroundTasksInternal();
+}
+
+void PerIsolatePlatformData::CallOnForegroundThread(Task* task) {
+ foreground_tasks_.Push(task);
+ uv_async_send(flush_tasks_);
+}
+
+void PerIsolatePlatformData::CallDelayedOnForegroundThread(
+ Task* task, double delay_in_seconds) {
+ auto pair = new std::pair<Task*, double>(task, delay_in_seconds);
+ foreground_delayed_tasks_.Push(pair);
+ uv_async_send(flush_tasks_);
+}
+
+PerIsolatePlatformData::~PerIsolatePlatformData() {
+ FlushForegroundTasksInternal();
+
+ uv_close(reinterpret_cast<uv_handle_t*>(flush_tasks_),
+ [](uv_handle_t* handle) {
+ delete reinterpret_cast<uv_async_t*>(handle);
+ });
+}
+
+void PerIsolatePlatformData::ref() {
+ ref_count_++;
+}
+
+int PerIsolatePlatformData::unref() {
+ return --ref_count_;
+}
+
+NodePlatform::NodePlatform(int thread_pool_size,
+ TracingController* tracing_controller) {
if (tracing_controller) {
tracing_controller_.reset(tracing_controller);
} else {
@@ -49,18 +85,35 @@ NodePlatform::NodePlatform(int thread_pool_size, uv_loop_t* loop,
}
}
+void NodePlatform::RegisterIsolate(IsolateData* isolate_data, uv_loop_t* loop) {
+ Isolate* isolate = isolate_data->isolate();
+ Mutex::ScopedLock lock(per_isolate_mutex_);
+ PerIsolatePlatformData* existing = per_isolate_[isolate];
+ if (existing != nullptr)
+ existing->ref();
+ else
+ per_isolate_[isolate] = new PerIsolatePlatformData(isolate, loop);
+}
+
+void NodePlatform::UnregisterIsolate(IsolateData* isolate_data) {
+ Isolate* isolate = isolate_data->isolate();
+ Mutex::ScopedLock lock(per_isolate_mutex_);
+ PerIsolatePlatformData* existing = per_isolate_[isolate];
+ CHECK_NE(existing, nullptr);
+ if (existing->unref() == 0) {
+ delete existing;
+ per_isolate_.erase(isolate);
+ }
+}
+
void NodePlatform::Shutdown() {
background_tasks_.Stop();
for (size_t i = 0; i < threads_.size(); i++) {
CHECK_EQ(0, uv_thread_join(threads_[i].get()));
}
- // uv_run cannot be called from the time before the beforeExit callback
- // runs until the program exits unless the event loop has any referenced
- // handles after beforeExit terminates. This prevents unrefed timers
- // that happen to terminate during shutdown from being run unsafely.
- // Since uv_run cannot be called, this handle will never be fully cleaned
- // up.
- uv_close(reinterpret_cast<uv_handle_t*>(&flush_tasks_), nullptr);
+ Mutex::ScopedLock lock(per_isolate_mutex_);
+ for (const auto& pair : per_isolate_)
+ delete pair.second;
}
size_t NodePlatform::NumberOfAvailableBackgroundThreads() {
@@ -85,13 +138,19 @@ static void RunForegroundTask(uv_timer_t* handle) {
});
}
-void NodePlatform::DrainBackgroundTasks() {
+void NodePlatform::DrainBackgroundTasks(Isolate* isolate) {
+ PerIsolatePlatformData* per_isolate = ForIsolate(isolate);
+
do {
+ // Right now, there is no way to drain only background tasks associated with
+ // a specific isolate, so this sometimes does more work than necessary.
+ // In the long run, that functionality is probably going to be available
+ // anyway, though.
background_tasks_.BlockingDrain();
- } while (FlushForegroundTasksInternal());
+ } while (per_isolate->FlushForegroundTasksInternal());
}
-bool NodePlatform::FlushForegroundTasksInternal() {
+bool PerIsolatePlatformData::FlushForegroundTasksInternal() {
bool did_work = false;
while (auto delayed = foreground_delayed_tasks_.Pop()) {
did_work = true;
@@ -118,17 +177,26 @@ void NodePlatform::CallOnBackgroundThread(Task* task,
background_tasks_.Push(task);
}
+PerIsolatePlatformData* NodePlatform::ForIsolate(Isolate* isolate) {
+ Mutex::ScopedLock lock(per_isolate_mutex_);
+ PerIsolatePlatformData* data = per_isolate_[isolate];
+ CHECK_NE(data, nullptr);
+ return data;
+}
+
void NodePlatform::CallOnForegroundThread(Isolate* isolate, Task* task) {
- foreground_tasks_.Push(task);
- uv_async_send(&flush_tasks_);
+ ForIsolate(isolate)->CallOnForegroundThread(task);
}
void NodePlatform::CallDelayedOnForegroundThread(Isolate* isolate,
Task* task,
double delay_in_seconds) {
- auto pair = new std::pair<Task*, double>(task, delay_in_seconds);
- foreground_delayed_tasks_.Push(pair);
- uv_async_send(&flush_tasks_);
+ ForIsolate(isolate)->CallDelayedOnForegroundThread(task,
+ delay_in_seconds);
+}
+
+void NodePlatform::FlushForegroundTasks(v8::Isolate* isolate) {
+ ForIsolate(isolate)->FlushForegroundTasksInternal();
}
bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { return false; }
diff --git a/src/node_platform.h b/src/node_platform.h
index 04927fccc3..aa9bf327d7 100644
--- a/src/node_platform.h
+++ b/src/node_platform.h
@@ -2,14 +2,19 @@
#define SRC_NODE_PLATFORM_H_
#include <queue>
+#include <unordered_map>
#include <vector>
#include "libplatform/libplatform.h"
+#include "node.h"
#include "node_mutex.h"
#include "uv.h"
namespace node {
+class NodePlatform;
+class IsolateData;
+
template <class T>
class TaskQueue {
public:
@@ -32,15 +37,38 @@ class TaskQueue {
std::queue<T*> task_queue_;
};
-class NodePlatform : public v8::Platform {
+class PerIsolatePlatformData {
public:
- NodePlatform(int thread_pool_size, uv_loop_t* loop,
- v8::TracingController* tracing_controller);
- virtual ~NodePlatform() {}
+ PerIsolatePlatformData(v8::Isolate* isolate, uv_loop_t* loop);
+ ~PerIsolatePlatformData();
+
+ void CallOnForegroundThread(v8::Task* task);
+ void CallDelayedOnForegroundThread(v8::Task* task, double delay_in_seconds);
+
+ void Shutdown();
+
+ void ref();
+ int unref();
- void DrainBackgroundTasks();
// Returns true iff work was dispatched or executed.
bool FlushForegroundTasksInternal();
+ private:
+ static void FlushTasks(uv_async_t* handle);
+
+ int ref_count_ = 1;
+ v8::Isolate* isolate_;
+ uv_loop_t* const loop_;
+ uv_async_t* flush_tasks_ = nullptr;
+ TaskQueue<v8::Task> foreground_tasks_;
+ TaskQueue<std::pair<v8::Task*, double>> foreground_delayed_tasks_;
+};
+
+class NodePlatform : public MultiIsolatePlatform {
+ public:
+ NodePlatform(int thread_pool_size, v8::TracingController* tracing_controller);
+ virtual ~NodePlatform() {}
+
+ void DrainBackgroundTasks(v8::Isolate* isolate) override;
void Shutdown();
// v8::Platform implementation.
@@ -54,11 +82,16 @@ class NodePlatform : public v8::Platform {
double MonotonicallyIncreasingTime() override;
v8::TracingController* GetTracingController() override;
+ void FlushForegroundTasks(v8::Isolate* isolate);
+
+ void RegisterIsolate(IsolateData* isolate_data, uv_loop_t* loop) override;
+ void UnregisterIsolate(IsolateData* isolate_data) override;
+
private:
- uv_loop_t* const loop_;
- uv_async_t flush_tasks_;
- TaskQueue<v8::Task> foreground_tasks_;
- TaskQueue<std::pair<v8::Task*, double>> foreground_delayed_tasks_;
+ PerIsolatePlatformData* ForIsolate(v8::Isolate* isolate);
+
+ Mutex per_isolate_mutex_;
+ std::unordered_map<v8::Isolate*, PerIsolatePlatformData*> per_isolate_;
TaskQueue<v8::Task> background_tasks_;
std::vector<std::unique_ptr<uv_thread_t>> threads_;