diff --git a/src/env-inl.h b/src/env-inl.h index 4b6f147f64778c..0a5ae528c3143d 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(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(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() : async_ids_stack_(env()->isolate(), 16 * 2), fields_(env()->isolate(), kFieldsCount), @@ -93,10 +65,6 @@ inline Environment::AsyncHooks::AsyncHooks() // which is different from a default context. async_id_fields_[AsyncHooks::kDefaultTriggerAsyncId] = -1; - // kAsyncIdCounter should start at 1 because that'll be the id the execution - // context during bootstrap (code that runs before entering uv_run()). - async_id_fields_[AsyncHooks::kAsyncIdCounter] = 1; - // Create all the provider strings that will be passed to JS. Place them in // an array so the array index matches the PROVIDER id offset. This way the // strings can be retrieved quickly. diff --git a/src/env.cc b/src/env.cc index e105fcd7c57ef1..5d1306ca3eb91d 100644 --- a/src/env.cc +++ b/src/env.cc @@ -1,6 +1,14 @@ #include "node_internals.h" #include "async_wrap.h" #include "v8-profiler.h" +#include "node_buffer.h" +#include "node_platform.h" + +#if defined(_MSC_VER) +#define getpid GetCurrentProcessId +#else +#include +#endif #include #include @@ -10,10 +18,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(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(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 9568b1545ba1d3..e8b1566df656c6 100644 --- a/src/env.h +++ b/src/env.h @@ -346,10 +346,13 @@ class Environment; 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) @@ -362,6 +365,7 @@ class IsolateData { #undef VP std::unordered_map> http2_static_strs; + inline v8::Isolate* isolate() const; private: #define VP(PropertyName, StringValue) V(v8::Private, PropertyName) @@ -374,8 +378,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 c1b574ac2e86de..ff15b4f6f414a6 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -314,7 +314,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 ac73f62d190aff..8eed856904f597 100644 --- a/src/node.cc +++ b/src/node.cc @@ -273,17 +273,17 @@ 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) { if (trace_enabled) { tracing_agent_.reset(new tracing::Agent(trace_file_pattern)); - platform_ = new NodePlatform(thread_pool_size, loop, + platform_ = new NodePlatform(thread_pool_size, tracing_agent_->GetTracingController()); V8::InitializePlatform(platform_); tracing::TraceEventHelper::SetTracingController( tracing_agent_->GetTracingController()); } else { tracing_agent_.reset(nullptr); - platform_ = new NodePlatform(thread_pool_size, loop, nullptr); + platform_ = new NodePlatform(thread_pool_size, nullptr); V8::InitializePlatform(platform_); tracing::TraceEventHelper::SetTracingController( new v8::TracingController()); @@ -297,8 +297,12 @@ static struct { tracing_agent_.reset(nullptr); } - void DrainVMTasks() { - platform_->DrainBackgroundTasks(); + void DrainVMTasks(Isolate* isolate) { + platform_->DrainBackgroundTasks(isolate); + } + + void CancelVMTasks(Isolate* isolate) { + platform_->CancelPendingDelayedTasks(isolate); } #if HAVE_INSPECTOR @@ -323,12 +327,17 @@ static struct { tracing_agent_->Stop(); } + NodePlatform* Platform() { + return platform_; + } + std::unique_ptr 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) {} + void CancelVMTasks(Isolate* isolate) {} bool StartInspector(Environment *env, const char* script_path, const node::DebugOptions& options) { env->ThrowError("Node compiled with NODE_USE_V8_PLATFORM=0"); @@ -340,6 +349,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 @@ -4734,7 +4747,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); } @@ -4802,7 +4822,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) @@ -4823,7 +4843,8 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, RunAtExit(&env); uv_key_delete(&thread_local_env); - v8_platform.DrainVMTasks(); + v8_platform.DrainVMTasks(isolate); + v8_platform.CancelVMTasks(isolate); WaitForInspectorDisconnect(&env); #if defined(LEAK_SANITIZER) __lsan_do_leak_check(); @@ -4866,7 +4887,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); } @@ -4913,7 +4938,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 78b2b2b64a6feb..a8f0fdca40400a 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) \ @@ -208,8 +209,28 @@ 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; + virtual void CancelPendingDelayedTasks(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 56b13b8437a0d2..0c50e8468d0f44 100644 --- a/src/node_platform.cc +++ b/src/node_platform.cc @@ -1,7 +1,10 @@ #include "node_platform.h" #include "node_internals.h" +#include "env.h" +#include "env-inl.h" #include "util.h" +#include namespace node { @@ -13,26 +16,64 @@ using v8::Platform; using v8::Task; using v8::TracingController; -static void FlushTasks(uv_async_t* handle) { - NodePlatform* platform = static_cast(handle->data); - platform->FlushForegroundTasksInternal(); -} - static void BackgroundRunner(void* data) { TaskQueue* background_tasks = static_cast*>(data); - while (Task* task = background_tasks->BlockingPop()) { + while (std::unique_ptr task = background_tasks->BlockingPop()) { task->Run(); - delete task; background_tasks->NotifyOfCompletion(); } } -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(this); - uv_unref(reinterpret_cast(&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(this); + uv_unref(reinterpret_cast(flush_tasks_)); +} + +void PerIsolatePlatformData::FlushTasks(uv_async_t* handle) { + auto platform_data = static_cast(handle->data); + platform_data->FlushForegroundTasksInternal(); +} + +void PerIsolatePlatformData::CallOnForegroundThread( + std::unique_ptr task) { + foreground_tasks_.Push(std::move(task)); + uv_async_send(flush_tasks_); +} + +void PerIsolatePlatformData::CallDelayedOnForegroundThread( + std::unique_ptr task, double delay_in_seconds) { + std::unique_ptr delayed(new DelayedTask()); + delayed->task = std::move(task); + delayed->platform_data = this; + delayed->timeout = delay_in_seconds; + foreground_delayed_tasks_.Push(std::move(delayed)); + uv_async_send(flush_tasks_); +} + +PerIsolatePlatformData::~PerIsolatePlatformData() { + FlushForegroundTasksInternal(); + CancelPendingDelayedTasks(); + + uv_close(reinterpret_cast(flush_tasks_), + [](uv_handle_t* handle) { + delete reinterpret_cast(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,86 +90,141 @@ 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(&flush_tasks_), nullptr); + Mutex::ScopedLock lock(per_isolate_mutex_); + for (const auto& pair : per_isolate_) + delete pair.second; } size_t NodePlatform::NumberOfAvailableBackgroundThreads() { return threads_.size(); } -static void RunForegroundTask(Task* task) { +void PerIsolatePlatformData::RunForegroundTask(std::unique_ptr task) { Isolate* isolate = Isolate::GetCurrent(); HandleScope scope(isolate); Environment* env = Environment::GetCurrent(isolate); InternalCallbackScope cb_scope(env, Local(), { 0, 0 }, InternalCallbackScope::kAllowEmptyResource); task->Run(); - delete task; } -static void RunForegroundTask(uv_timer_t* handle) { - Task* task = static_cast(handle->data); - RunForegroundTask(task); - uv_close(reinterpret_cast(handle), [](uv_handle_t* handle) { - delete reinterpret_cast(handle); - }); +void PerIsolatePlatformData::DeleteFromScheduledTasks(DelayedTask* task) { + auto it = std::find_if(scheduled_delayed_tasks_.begin(), + scheduled_delayed_tasks_.end(), + [task](const DelayedTaskPointer& delayed) -> bool { + return delayed.get() == task; + }); + CHECK_NE(it, scheduled_delayed_tasks_.end()); + scheduled_delayed_tasks_.erase(it); } -void NodePlatform::DrainBackgroundTasks() { +void PerIsolatePlatformData::RunForegroundTask(uv_timer_t* handle) { + DelayedTask* delayed = static_cast(handle->data); + RunForegroundTask(std::move(delayed->task)); + delayed->platform_data->DeleteFromScheduledTasks(delayed); +} + +void PerIsolatePlatformData::CancelPendingDelayedTasks() { + scheduled_delayed_tasks_.clear(); +} + +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()) { + + while (std::unique_ptr delayed = + foreground_delayed_tasks_.Pop()) { did_work = true; uint64_t delay_millis = - static_cast(delayed->second + 0.5) * 1000; - uv_timer_t* handle = new uv_timer_t(); - handle->data = static_cast(delayed->first); - uv_timer_init(loop_, handle); + static_cast(delayed->timeout + 0.5) * 1000; + delayed->timer.data = static_cast(delayed.get()); + uv_timer_init(loop_, &delayed->timer); // Timers may not guarantee queue ordering of events with the same delay if // the delay is non-zero. This should not be a problem in practice. - uv_timer_start(handle, RunForegroundTask, delay_millis, 0); - uv_unref(reinterpret_cast(handle)); - delete delayed; + uv_timer_start(&delayed->timer, RunForegroundTask, delay_millis, 0); + uv_unref(reinterpret_cast(&delayed->timer)); + + scheduled_delayed_tasks_.emplace_back(delayed.release(), + [](DelayedTask* delayed) { + uv_close(reinterpret_cast(&delayed->timer), + [](uv_handle_t* handle) { + delete static_cast(handle->data); + }); + }); } - while (Task* task = foreground_tasks_.Pop()) { + while (std::unique_ptr task = foreground_tasks_.Pop()) { did_work = true; - RunForegroundTask(task); + RunForegroundTask(std::move(task)); } return did_work; } void NodePlatform::CallOnBackgroundThread(Task* task, ExpectedRuntime expected_runtime) { - background_tasks_.Push(task); + background_tasks_.Push(std::unique_ptr(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(std::unique_ptr(task)); } void NodePlatform::CallDelayedOnForegroundThread(Isolate* isolate, Task* task, double delay_in_seconds) { - auto pair = new std::pair(task, delay_in_seconds); - foreground_delayed_tasks_.Push(pair); - uv_async_send(&flush_tasks_); + ForIsolate(isolate)->CallDelayedOnForegroundThread( + std::unique_ptr(task), delay_in_seconds); +} + +void NodePlatform::FlushForegroundTasks(v8::Isolate* isolate) { + ForIsolate(isolate)->FlushForegroundTasksInternal(); +} + +void NodePlatform::CancelPendingDelayedTasks(v8::Isolate* isolate) { + ForIsolate(isolate)->CancelPendingDelayedTasks(); } bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { return false; } @@ -148,34 +244,34 @@ TaskQueue::TaskQueue() outstanding_tasks_(0), stopped_(false), task_queue_() { } template -void TaskQueue::Push(T* task) { +void TaskQueue::Push(std::unique_ptr task) { Mutex::ScopedLock scoped_lock(lock_); outstanding_tasks_++; - task_queue_.push(task); + task_queue_.push(std::move(task)); tasks_available_.Signal(scoped_lock); } template -T* TaskQueue::Pop() { +std::unique_ptr TaskQueue::Pop() { Mutex::ScopedLock scoped_lock(lock_); - T* result = nullptr; - if (!task_queue_.empty()) { - result = task_queue_.front(); - task_queue_.pop(); + if (task_queue_.empty()) { + return std::unique_ptr(nullptr); } + std::unique_ptr result = std::move(task_queue_.front()); + task_queue_.pop(); return result; } template -T* TaskQueue::BlockingPop() { +std::unique_ptr TaskQueue::BlockingPop() { Mutex::ScopedLock scoped_lock(lock_); while (task_queue_.empty() && !stopped_) { tasks_available_.Wait(scoped_lock); } if (stopped_) { - return nullptr; + return std::unique_ptr(nullptr); } - T* result = task_queue_.front(); + std::unique_ptr result = std::move(task_queue_.front()); task_queue_.pop(); return result; } diff --git a/src/node_platform.h b/src/node_platform.h index 04927fccc3df66..48301a05a11d8c 100644 --- a/src/node_platform.h +++ b/src/node_platform.h @@ -2,23 +2,30 @@ #define SRC_NODE_PLATFORM_H_ #include +#include #include +#include #include "libplatform/libplatform.h" +#include "node.h" #include "node_mutex.h" #include "uv.h" namespace node { +class NodePlatform; +class IsolateData; +class PerIsolatePlatformData; + template class TaskQueue { public: TaskQueue(); ~TaskQueue() {} - void Push(T* task); - T* Pop(); - T* BlockingPop(); + void Push(std::unique_ptr task); + std::unique_ptr Pop(); + std::unique_ptr BlockingPop(); void NotifyOfCompletion(); void BlockingDrain(); void Stop(); @@ -29,18 +36,61 @@ class TaskQueue { ConditionVariable tasks_drained_; int outstanding_tasks_; bool stopped_; - std::queue task_queue_; + std::queue> task_queue_; +}; + +struct DelayedTask { + std::unique_ptr task; + uv_timer_t timer; + double timeout; + PerIsolatePlatformData* platform_data; }; -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(std::unique_ptr task); + void CallDelayedOnForegroundThread(std::unique_ptr task, + double delay_in_seconds); + + void Shutdown(); + + void ref(); + int unref(); - void DrainBackgroundTasks(); // Returns true iff work was dispatched or executed. bool FlushForegroundTasksInternal(); + void CancelPendingDelayedTasks(); + + private: + void DeleteFromScheduledTasks(DelayedTask* task); + + static void FlushTasks(uv_async_t* handle); + static void RunForegroundTask(std::unique_ptr task); + static void RunForegroundTask(uv_timer_t* timer); + + int ref_count_ = 1; + v8::Isolate* isolate_; + uv_loop_t* const loop_; + uv_async_t* flush_tasks_ = nullptr; + TaskQueue foreground_tasks_; + TaskQueue foreground_delayed_tasks_; + + // Use a custom deleter because libuv needs to close the handle first. + typedef std::unique_ptr> + DelayedTaskPointer; + std::vector scheduled_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 CancelPendingDelayedTasks(v8::Isolate* isolate) override; void Shutdown(); // v8::Platform implementation. @@ -54,11 +104,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 foreground_tasks_; - TaskQueue> foreground_delayed_tasks_; + PerIsolatePlatformData* ForIsolate(v8::Isolate* isolate); + + Mutex per_isolate_mutex_; + std::unordered_map per_isolate_; TaskQueue background_tasks_; std::vector> threads_; diff --git a/test/cctest/node_test_fixture.h b/test/cctest/node_test_fixture.h index 1d9e574a32f73d..6fb163bad9b6b9 100644 --- a/test/cctest/node_test_fixture.h +++ b/test/cctest/node_test_fixture.h @@ -59,12 +59,14 @@ class NodeTestFixture : public ::testing::Test { public: static uv_loop_t* CurrentLoop() { return ¤t_loop; } + node::MultiIsolatePlatform* Platform() const { return platform_; } + protected: v8::Isolate* isolate_; virtual void SetUp() { CHECK_EQ(0, uv_loop_init(¤t_loop)); - platform_ = new node::NodePlatform(8, ¤t_loop, nullptr); + platform_ = new node::NodePlatform(8, nullptr); v8::V8::InitializePlatform(platform_); v8::V8::Initialize(); v8::Isolate::CreateParams params_; @@ -101,14 +103,17 @@ class EnvironmentTestFixture : public NodeTestFixture { public: class Env { public: - Env(const v8::HandleScope& handle_scope, const Argv& argv) { + Env(const v8::HandleScope& handle_scope, + const Argv& argv, + NodeTestFixture* test_fixture) { auto isolate = handle_scope.GetIsolate(); context_ = v8::Context::New(isolate); CHECK(!context_.IsEmpty()); context_->Enter(); isolate_data_ = node::CreateIsolateData(isolate, - NodeTestFixture::CurrentLoop()); + NodeTestFixture::CurrentLoop(), + test_fixture->Platform()); CHECK_NE(nullptr, isolate_data_); environment_ = node::CreateEnvironment(isolate_data_, context_, diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc index 4575d3b65ae318..352fed1fb62ed9 100644 --- a/test/cctest/test_environment.cc +++ b/test/cctest/test_environment.cc @@ -32,7 +32,7 @@ class EnvironmentTest : public EnvironmentTestFixture { TEST_F(EnvironmentTest, AtExitWithEnvironment) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env {handle_scope, argv}; + Env env {handle_scope, argv, this}; AtExit(*env, at_exit_callback1); RunAtExit(*env); @@ -42,7 +42,7 @@ TEST_F(EnvironmentTest, AtExitWithEnvironment) { TEST_F(EnvironmentTest, AtExitWithArgument) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env {handle_scope, argv}; + Env env {handle_scope, argv, this}; std::string arg{"some args"}; AtExit(*env, at_exit_callback1, static_cast(&arg)); @@ -53,8 +53,8 @@ TEST_F(EnvironmentTest, AtExitWithArgument) { TEST_F(EnvironmentTest, MultipleEnvironmentsPerIsolate) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env1 {handle_scope, argv}; - Env env2 {handle_scope, argv}; + Env env1 {handle_scope, argv, this}; + Env env2 {handle_scope, argv, this}; AtExit(*env1, at_exit_callback1); AtExit(*env2, at_exit_callback2); diff --git a/test/cctest/test_node_postmortem_metadata.cc b/test/cctest/test_node_postmortem_metadata.cc index 1153ebf2811f7f..3893f31d6ce4b1 100644 --- a/test/cctest/test_node_postmortem_metadata.cc +++ b/test/cctest/test_node_postmortem_metadata.cc @@ -50,7 +50,7 @@ TEST_F(DebugSymbolsTest, ExternalStringDataOffset) { TEST_F(DebugSymbolsTest, BaseObjectPersistentHandle) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env{handle_scope, argv}; + Env env{handle_scope, argv, this}; v8::Local object = v8::Object::New(isolate_); node::BaseObject obj(*env, object); @@ -67,7 +67,7 @@ TEST_F(DebugSymbolsTest, BaseObjectPersistentHandle) { TEST_F(DebugSymbolsTest, EnvironmentHandleWrapQueue) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env{handle_scope, argv}; + Env env{handle_scope, argv, this}; auto expected = reinterpret_cast((*env)->handle_wrap_queue()); auto calculated = reinterpret_cast(*env) + @@ -78,7 +78,7 @@ TEST_F(DebugSymbolsTest, EnvironmentHandleWrapQueue) { TEST_F(DebugSymbolsTest, EnvironmentReqWrapQueue) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env{handle_scope, argv}; + Env env{handle_scope, argv, this}; auto expected = reinterpret_cast((*env)->req_wrap_queue()); auto calculated = reinterpret_cast(*env) + @@ -89,7 +89,7 @@ TEST_F(DebugSymbolsTest, EnvironmentReqWrapQueue) { TEST_F(DebugSymbolsTest, HandleWrapList) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env{handle_scope, argv}; + Env env{handle_scope, argv, this}; uv_tcp_t handle; @@ -118,7 +118,7 @@ TEST_F(DebugSymbolsTest, HandleWrapList) { TEST_F(DebugSymbolsTest, ReqWrapList) { const v8::HandleScope handle_scope(isolate_); const Argv argv; - Env env{handle_scope, argv}; + Env env{handle_scope, argv, this}; auto obj_template = v8::FunctionTemplate::New(isolate_); obj_template->InstanceTemplate()->SetInternalFieldCount(1);