diff --git a/node.gyp b/node.gyp index f50aea3475598d..2343c118be4560 100644 --- a/node.gyp +++ b/node.gyp @@ -182,6 +182,7 @@ 'src/node_url.cc', 'src/node_util.cc', 'src/node_v8.cc', + 'src/node_v8platform.cc', 'src/node_stat_watcher.cc', 'src/node_watchdog.cc', 'src/node_zlib.cc', @@ -222,7 +223,9 @@ 'src/node_internals.h', 'src/node_javascript.h', 'src/node_mutex.h', + 'src/node_platform.h', 'src/node_root_certs.h', + 'src/node_v8platform.h', 'src/node_version.h', 'src/node_watchdog.h', 'src/node_wrap.h', diff --git a/src/node.cc b/src/node.cc index fb98fcebc2a4a5..83a21b13f9df5c 100644 --- a/src/node.cc +++ b/src/node.cc @@ -27,6 +27,7 @@ #include "node_internals.h" #include "node_revert.h" #include "node_debug_options.h" +#include "node_v8platform.h" #if defined HAVE_PERFCTR #include "node_counters.h" @@ -241,14 +242,19 @@ static node::DebugOptions debug_options; static struct { #if NODE_USE_V8_PLATFORM - void Initialize(int thread_pool_size) { - platform_ = v8::platform::CreateDefaultPlatform(thread_pool_size); + void Initialize(int thread_pool_size, uv_loop_t* loop) { + platform_ = new node::platform::NodePlatform(thread_pool_size, loop); + // TODO: the following methods expect v8::Platform, how do we deal with that? V8::InitializePlatform(platform_); tracing::TraceEventHelper::SetCurrentPlatform(platform_); } - void PumpMessageLoop(Isolate* isolate) { - v8::platform::PumpMessageLoop(platform_, isolate); + void PumpMessageLoop() { + platform_->PumpMessageLoop(); + } + + void RunIdle(double budgetSeconds) { + platform_->RunIdle(budgetSeconds); } void Dispose() { @@ -266,6 +272,7 @@ static struct { void StartTracingAgent() { CHECK(tracing_agent_ == nullptr); tracing_agent_ = new tracing::Agent(); + // TODO: this method expects v8::Platform, how do we deal with that? tracing_agent_->Start(platform_, trace_enabled_categories); } @@ -273,10 +280,10 @@ static struct { tracing_agent_->Stop(); } - v8::Platform* platform_; tracing::Agent* tracing_agent_; + node::platform::NodePlatform* platform_; #else // !NODE_USE_V8_PLATFORM - void Initialize(int thread_pool_size) {} + void Initialize(int thread_pool_size, uv_loop_t* loop) {} void PumpMessageLoop(Isolate* isolate) {} void Dispose() {} bool StartInspector(Environment *env, const char* script_path, @@ -4481,11 +4488,11 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data, SealHandleScope seal(isolate); bool more; do { - v8_platform.PumpMessageLoop(isolate); + v8_platform.PumpMessageLoop(); more = uv_run(env.event_loop(), UV_RUN_ONCE); if (more == false) { - v8_platform.PumpMessageLoop(isolate); + v8_platform.PumpMessageLoop(); EmitBeforeExit(&env); // Emit `beforeExit` if the loop became alive either after emitting @@ -4591,7 +4598,7 @@ int Start(int argc, char** argv) { V8::SetEntropySource(crypto::EntropySource); #endif // HAVE_OPENSSL - v8_platform.Initialize(v8_thread_pool_size); + v8_platform.Initialize(v8_thread_pool_size, uv_default_loop()); // 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_platform.h b/src/node_platform.h new file mode 100644 index 00000000000000..c0847ea68e5584 --- /dev/null +++ b/src/node_platform.h @@ -0,0 +1,234 @@ +// Original source: https://github.com/v8/v8/blob/a9e56f4f36d70d16a956367133dda258c4f52bf4/include/v8-platform.h +// Original headers: +// Copyright 2013 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SRC_NODE_PLATFORM_H_ +#define SRC_NODE_PLATFORM_H_ + +#include "v8.h" + +#include +#include +#include +#include + + +namespace node { +namespace platform { + +class v8::Isolate; + +/** + * A Task represents a unit of work. + */ +class Task { + public: + virtual ~Task() = default; + + virtual void Run() = 0; +}; + +/** + * An IdleTask represents a unit of work to be performed in idle time. + * The Run method is invoked with an argument that specifies the deadline in + * seconds returned by MonotonicallyIncreasingTime(). + * The idle task is expected to complete by this deadline. + */ +class IdleTask { + public: + virtual ~IdleTask() = default; + virtual void Run(double deadline_in_seconds) = 0; +}; + +/** + * The interface represents complex arguments to trace events. + */ +class ConvertableToTraceFormat { + public: + virtual ~ConvertableToTraceFormat() = default; + + /** + * Append the class info to the provided |out| string. The appended + * data must be a valid JSON object. Strings must be properly quoted, and + * escaped. There is no processing applied to the content after it is + * appended. + */ + virtual void AppendAsTraceFormat(std::string* out) const = 0; +}; + +/** + * V8 Platform abstraction layer. + * + * The embedder has to provide an implementation of this interface before + * initializing the rest of V8. + */ +class Platform { + public: + /** + * This enum is used to indicate whether a task is potentially long running, + * or causes a long wait. The embedder might want to use this hint to decide + * whether to execute the task on a dedicated thread. + */ + enum ExpectedRuntime { + kShortRunningTask, + kLongRunningTask + }; + + virtual ~Platform() = default; + + /** + * Gets the number of threads that are used to execute background tasks. Is + * used to estimate the number of tasks a work package should be split into. + * A return value of 0 means that there are no background threads available. + * Note that a value of 0 won't prohibit V8 from posting tasks using + * |CallOnBackgroundThread|. + */ + virtual size_t NumberOfAvailableBackgroundThreads() { return 0; } + + /** + * Schedules a task to be invoked on a background thread. |expected_runtime| + * indicates that the task will run a long time. The Platform implementation + * takes ownership of |task|. There is no guarantee about order of execution + * of tasks wrt order of scheduling, nor is there a guarantee about the + * thread the task will be run on. + */ + virtual void CallOnBackgroundThread(Task* task, + ExpectedRuntime expected_runtime) = 0; + + /** + * Schedules a task to be invoked on a foreground thread wrt a specific + * |isolate|. Tasks posted for the same isolate should be execute in order of + * scheduling. The definition of "foreground" is opaque to V8. + */ + virtual void CallOnForegroundThread(Isolate* isolate, Task* task) = 0; + + /** + * Schedules a task to be invoked on a foreground thread wrt a specific + * |isolate| after the given number of seconds |delay_in_seconds|. + * Tasks posted for the same isolate should be execute in order of + * scheduling. The definition of "foreground" is opaque to V8. + */ + virtual void CallDelayedOnForegroundThread(Isolate* isolate, Task* task, + double delay_in_seconds) = 0; + + /** + * Schedules a task to be invoked on a foreground thread wrt a specific + * |isolate| when the embedder is idle. + * Requires that SupportsIdleTasks(isolate) is true. + * Idle tasks may be reordered relative to other task types and may be + * starved for an arbitrarily long time if no idle time is available. + * The definition of "foreground" is opaque to V8. + */ + virtual void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) { + // TODO(ulan): Make this function abstract after V8 roll in Chromium. + } + + /** + * Returns true if idle tasks are enabled for the given |isolate|. + */ + virtual bool IdleTasksEnabled(Isolate* isolate) { + // TODO(ulan): Make this function abstract after V8 roll in Chromium. + return false; + } + + /** + * Monotonically increasing time in seconds from an arbitrary fixed point in + * the past. This function is expected to return at least + * millisecond-precision values. For this reason, + * it is recommended that the fixed point be no further in the past than + * the epoch. + **/ + virtual double MonotonicallyIncreasingTime() = 0; + + /** + * Called by TRACE_EVENT* macros, don't call this directly. + * The name parameter is a category group for example: + * TRACE_EVENT0("v8,parse", "V8.Parse") + * The pointer returned points to a value with zero or more of the bits + * defined in CategoryGroupEnabledFlags. + **/ + virtual const uint8_t* GetCategoryGroupEnabled(const char* name) { + static uint8_t no = 0; + return &no; + } + + /** + * Gets the category group name of the given category_enabled_flag pointer. + * Usually used while serliazing TRACE_EVENTs. + **/ + virtual const char* GetCategoryGroupName( + const uint8_t* category_enabled_flag) { + static const char dummy[] = "dummy"; + return dummy; + } + + /** + * Adds a trace event to the platform tracing system. This function call is + * usually the result of a TRACE_* macro from trace_event_common.h when + * tracing and the category of the particular trace are enabled. It is not + * advisable to call this function on its own; it is really only meant to be + * used by the trace macros. The returned handle can be used by + * UpdateTraceEventDuration to update the duration of COMPLETE events. + */ + virtual uint64_t AddTraceEvent( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, unsigned int flags) { + return 0; + } + + /** + * Adds a trace event to the platform tracing system. This function call is + * usually the result of a TRACE_* macro from trace_event_common.h when + * tracing and the category of the particular trace are enabled. It is not + * advisable to call this function on its own; it is really only meant to be + * used by the trace macros. The returned handle can be used by + * UpdateTraceEventDuration to update the duration of COMPLETE events. + */ + virtual uint64_t AddTraceEvent( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, + std::unique_ptr* arg_convertables, + unsigned int flags) { + return AddTraceEvent(phase, category_enabled_flag, name, scope, id, bind_id, + num_args, arg_names, arg_types, arg_values, flags); + } + + /** + * Sets the duration field of a COMPLETE trace event. It must be called with + * the handle returned from AddTraceEvent(). + **/ + virtual void UpdateTraceEventDuration(const uint8_t* category_enabled_flag, + const char* name, uint64_t handle) {} + + class TraceStateObserver { + public: + virtual ~TraceStateObserver() = default; + virtual void OnTraceEnabled() = 0; + virtual void OnTraceDisabled() = 0; + }; + + /** Adds tracing state change observer. */ + virtual void AddTraceStateObserver(TraceStateObserver*) {} + + /** Removes tracing state change observer. */ + virtual void RemoveTraceStateObserver(TraceStateObserver*) {} + + typedef void (*StackTracePrinter)(); + + /** + * Returns a function pointer that print a stack trace of the current stack + * on invocation. Disables printing of the stack trace if nullptr. + */ + virtual StackTracePrinter GetStackTracePrinter() { return nullptr; } +}; + +} // namespace platform +} // namespace v8 + +#endif // V8_V8_PLATFORM_H_ diff --git a/src/node_v8platform.cc b/src/node_v8platform.cc new file mode 100644 index 00000000000000..9144297730d30f --- /dev/null +++ b/src/node_v8platform.cc @@ -0,0 +1,184 @@ +// original source: +// https://github.com/matthewloring/node/blob/c76caae8de9c689db1018506b2fa8d3ac4d38e3e/src/node_platform.cc + +#include "node_v8platform.h" + +namespace node { +namespace platform { + +NodePlatform::NodePlatform(int thread_pool_size, uv_loop_t* loop) + : threads_(thread_pool_size), loop_(loop) {} + +NodePlatform::~NodePlatform() {} + +Task* NodePlatform::PopTaskInMainThreadQueue() { + if (main_thread_queue_.empty()) { + return NULL; + } + Task* task = main_thread_queue_.front(); + main_thread_queue_.pop(); + return task; +} + + +Task* NodePlatform::PopTaskInMainThreadDelayedQueue() { + if (main_thread_delayed_queue_.empty()) { + return NULL; + } + double now = MonotonicallyIncreasingTime(); + std::pair deadline_and_task = main_thread_delayed_queue_.top(); + if (deadline_and_task.first > now) { + return NULL; + } + main_thread_delayed_queue_.pop(); + return deadline_and_task.second; +} + +IdleTask* NodePlatform::PopTaskInMainThreadIdleQueue() { + if (main_thread_idle_queue_.empty()) { + return NULL; + } + IdleTask* task = main_thread_idle_queue_.front(); + main_thread_idle_queue_.pop(); + return task; +} + +bool NodePlatform::PumpMessageLoop() { + Task* task = NULL; + { + Mutex::ScopedLock scoped_lock(mutex_); + + // Move delayed tasks that hit their deadline to the main queue. + task = PopTaskInMainThreadDelayedQueue(); + while (task != NULL) { + main_thread_queue_.push(task); + task = PopTaskInMainThreadDelayedQueue(); + } + + task = PopTaskInMainThreadQueue(); + + if (task == NULL) { + return false; + } + } + task->Run(); + delete task; + return true; +} + +bool NodePlatform::RunIdle(double budgetSeconds) { + IdleTask* task = NULL; + { + task = PopTaskInMainThreadIdleQueue(); + + if (task == NULL) { + return false; + } + } + task->Run(MonotonicallyIncreasingTime() + budgetSeconds); + delete task; + return true; +} + +size_t NodePlatform::NumberOfAvailableBackgroundThreads() { + return static_cast(threads_); +} + +void RunTask(uv_work_t *req) { + Task* task = (Task*) req->data; + task->Run(); + delete task; +} + +void AfterTask(uv_work_t *req, int status) { + delete req; +} + +void NodePlatform::CallOnBackgroundThread(Task* task, + ExpectedRuntime expected_runtime) { + uv_work_t* req = new uv_work_t; + req->data = (void*) task; + uv_queue_work(loop_, req, RunTask, AfterTask); +} + +void NodePlatform::CallOnForegroundThread(v8::Isolate* isolate, Task* task) { + Mutex::ScopedLock scoped_lock(mutex_); + main_thread_queue_.push(task); +} + +void NodePlatform::CallDelayedOnForegroundThread(Isolate* isolate, + Task* task, + double delay_in_seconds) { + Mutex::ScopedLock scoped_lock(mutex_); + double deadline = MonotonicallyIncreasingTime() + delay_in_seconds; + main_thread_delayed_queue_.push(std::make_pair(deadline, task)); +} + +void NodePlatform::CallIdleOnForegroundThread(Isolate* isolate, + IdleTask* task) { + Mutex::ScopedLock scoped_lock(mutex_); + main_thread_idle_queue_.push(task); +} + +bool NodePlatform::IdleTasksEnabled(Isolate* isolate) { return true; } + +double NodePlatform::MonotonicallyIncreasingTime() { + // Convert nanos to seconds + return uv_hrtime() / 1000000000.0; +} + +const uint8_t* NodePlatform::GetCategoryGroupEnabled(const char* name) { + if (tracing_controller_) { + return tracing_controller_->GetCategoryGroupEnabled(name); + } + static uint8_t no = 0; + return &no; +} + +const char* NodePlatform::GetCategoryGroupName( + const uint8_t* category_enabled_flag) { + static const char dummy[] = "dummy"; + return dummy; +} + +uint64_t NodePlatform::AddTraceEvent( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, + std::unique_ptr* arg_convertables, + unsigned int flags) { + if (tracing_controller_) { + return tracing_controller_->AddTraceEvent( + phase, category_enabled_flag, name, scope, id, bind_id, num_args, + arg_names, arg_types, arg_values, arg_convertables, flags); + } + + return 0; +} + +void NodePlatform::UpdateTraceEventDuration( + const uint8_t* category_enabled_flag, const char* name, uint64_t handle) { + if (tracing_controller_) { + tracing_controller_->UpdateTraceEventDuration(category_enabled_flag, name, + handle); + } +} + +void NodePlatform::SetTracingController( + TracingController* tracing_controller) { + tracing_controller_.reset(tracing_controller); +} + +void NodePlatform::AddTraceStateObserver(TraceStateObserver* observer) { + if (!tracing_controller_) return; + tracing_controller_->AddTraceStateObserver(observer); +} + +void NodePlatform::RemoveTraceStateObserver(TraceStateObserver* observer) { + if (!tracing_controller_) return; + tracing_controller_->RemoveTraceStateObserver(observer); +} + +} // namespace platform +} // namespace node diff --git a/src/node_v8platform.h b/src/node_v8platform.h new file mode 100644 index 00000000000000..4cdf66589dc37c --- /dev/null +++ b/src/node_v8platform.h @@ -0,0 +1,84 @@ +// original source: https://github.com/matthewloring/node/blob/c76caae8de9c689db1018506b2fa8d3ac4d38e3e/src/node_platform.h + +#ifndef SRC_NODE_V8PLATFORM_H_ +#define SRC_NODE_V8PLATFORM_H_ + +#include "node_platform.h" +#include "node_mutex.h" + +#include "libplatform/libplatform.h" +#include "uv.h" + +#include + +namespace node { +namespace platform { + +using v8::platform::tracing::TracingController; +using v8::IdleTask; +using v8::Isolate; +using v8::Platform; +using v8::Task; + +class NodePlatform : public node::platform::Platform { + public: + NodePlatform(int thread_pool_size, uv_loop_t* loop); + virtual ~NodePlatform(); + + bool PumpMessageLoop(); + + bool RunIdle(double budgetSeconds); + + // v8::Platform implementation. + size_t NumberOfAvailableBackgroundThreads() override; + void CallOnBackgroundThread(Task* task, + ExpectedRuntime expected_runtime) override; + void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override; + void CallDelayedOnForegroundThread(Isolate* isolate, Task* task, + double delay_in_seconds) override; + void CallIdleOnForegroundThread(Isolate* isolate, IdleTask* task) override; + bool IdleTasksEnabled(Isolate* isolate) override; + double MonotonicallyIncreasingTime() override; + const uint8_t* GetCategoryGroupEnabled(const char* name) override; + const char* GetCategoryGroupName( + const uint8_t* category_enabled_flag) override; + uint64_t AddTraceEvent( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, + std::unique_ptr* arg_convertables, + unsigned int flags) override; + void UpdateTraceEventDuration(const uint8_t* category_enabled_flag, + const char* name, uint64_t handle) override; + void SetTracingController(TracingController* tracing_controller); + + void AddTraceStateObserver(TraceStateObserver* observer) override; + void RemoveTraceStateObserver(TraceStateObserver* observer) override; + + private: + Task* PopTaskInMainThreadQueue(); + Task* PopTaskInMainThreadDelayedQueue(); + IdleTask* PopTaskInMainThreadIdleQueue(); + + int threads_; + uv_loop_t* loop_; + + Mutex mutex_; + + std::queue main_thread_queue_; + + typedef std::pair DelayedEntry; + std::priority_queue, + std::greater > + main_thread_delayed_queue_; + + std::queue main_thread_idle_queue_; + + std::unique_ptr tracing_controller_; +}; + +} // namespace platform +} // namespace node + +#endif // SRC_NODE_PLATFORM_H_