blob: fec3ab2365021ea1302a15d2a87c774ad167416c [file] [log] [blame]
// 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.
#include "src/libplatform/default-platform.h"
#include <algorithm>
#include <queue>
#include "include/libplatform/libplatform.h"
#include "src/base/debug/stack_trace.h"
#include "src/base/logging.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "src/base/sys-info.h"
#include "src/libplatform/worker-thread.h"
namespace v8 {
namespace platform {
namespace {
void PrintStackTrace() {
v8::base::debug::StackTrace trace;
trace.Print();
// Avoid dumping duplicate stack trace on abort signal.
v8::base::debug::DisableSignalStackDump();
}
} // namespace
v8::Platform* CreateDefaultPlatform(
int thread_pool_size, IdleTaskSupport idle_task_support,
InProcessStackDumping in_process_stack_dumping,
v8::TracingController* tracing_controller) {
if (in_process_stack_dumping == InProcessStackDumping::kEnabled) {
v8::base::debug::EnableInProcessStackDumping();
}
DefaultPlatform* platform =
new DefaultPlatform(idle_task_support, tracing_controller);
platform->SetThreadPoolSize(thread_pool_size);
platform->EnsureInitialized();
return platform;
}
bool PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate,
MessageLoopBehavior behavior) {
return static_cast<DefaultPlatform*>(platform)->PumpMessageLoop(isolate,
behavior);
}
void EnsureEventLoopInitialized(v8::Platform* platform, v8::Isolate* isolate) {
return static_cast<DefaultPlatform*>(platform)->EnsureEventLoopInitialized(
isolate);
}
void RunIdleTasks(v8::Platform* platform, v8::Isolate* isolate,
double idle_time_in_seconds) {
static_cast<DefaultPlatform*>(platform)->RunIdleTasks(isolate,
idle_time_in_seconds);
}
void SetTracingController(
v8::Platform* platform,
v8::platform::tracing::TracingController* tracing_controller) {
static_cast<DefaultPlatform*>(platform)->SetTracingController(
tracing_controller);
}
const int DefaultPlatform::kMaxThreadPoolSize = 8;
DefaultPlatform::DefaultPlatform(IdleTaskSupport idle_task_support,
v8::TracingController* tracing_controller)
: initialized_(false),
thread_pool_size_(0),
idle_task_support_(idle_task_support) {
if (tracing_controller) {
tracing_controller_.reset(tracing_controller);
} else {
tracing::TracingController* controller = new tracing::TracingController();
controller->Initialize(nullptr);
tracing_controller_.reset(controller);
}
}
DefaultPlatform::~DefaultPlatform() {
base::LockGuard<base::Mutex> guard(&lock_);
queue_.Terminate();
if (initialized_) {
for (auto i = thread_pool_.begin(); i != thread_pool_.end(); ++i) {
delete *i;
}
}
for (auto i = main_thread_queue_.begin(); i != main_thread_queue_.end();
++i) {
while (!i->second.empty()) {
delete i->second.front();
i->second.pop();
}
}
for (auto i = main_thread_delayed_queue_.begin();
i != main_thread_delayed_queue_.end(); ++i) {
while (!i->second.empty()) {
delete i->second.top().second;
i->second.pop();
}
}
for (auto& i : main_thread_idle_queue_) {
while (!i.second.empty()) {
delete i.second.front();
i.second.pop();
}
}
}
void DefaultPlatform::SetThreadPoolSize(int thread_pool_size) {
base::LockGuard<base::Mutex> guard(&lock_);
DCHECK(thread_pool_size >= 0);
if (thread_pool_size < 1) {
thread_pool_size = base::SysInfo::NumberOfProcessors() - 1;
}
thread_pool_size_ =
std::max(std::min(thread_pool_size, kMaxThreadPoolSize), 1);
}
void DefaultPlatform::EnsureInitialized() {
base::LockGuard<base::Mutex> guard(&lock_);
if (initialized_) return;
initialized_ = true;
for (int i = 0; i < thread_pool_size_; ++i)
thread_pool_.push_back(new WorkerThread(&queue_));
}
Task* DefaultPlatform::PopTaskInMainThreadQueue(v8::Isolate* isolate) {
auto it = main_thread_queue_.find(isolate);
if (it == main_thread_queue_.end() || it->second.empty()) {
return NULL;
}
Task* task = it->second.front();
it->second.pop();
return task;
}
Task* DefaultPlatform::PopTaskInMainThreadDelayedQueue(v8::Isolate* isolate) {
auto it = main_thread_delayed_queue_.find(isolate);
if (it == main_thread_delayed_queue_.end() || it->second.empty()) {
return NULL;
}
double now = MonotonicallyIncreasingTime();
std::pair<double, Task*> deadline_and_task = it->second.top();
if (deadline_and_task.first > now) {
return NULL;
}
it->second.pop();
return deadline_and_task.second;
}
IdleTask* DefaultPlatform::PopTaskInMainThreadIdleQueue(v8::Isolate* isolate) {
auto it = main_thread_idle_queue_.find(isolate);
if (it == main_thread_idle_queue_.end() || it->second.empty()) {
return nullptr;
}
IdleTask* task = it->second.front();
it->second.pop();
return task;
}
void DefaultPlatform::EnsureEventLoopInitialized(v8::Isolate* isolate) {
base::LockGuard<base::Mutex> guard(&lock_);
if (event_loop_control_.count(isolate) == 0) {
event_loop_control_.insert(std::make_pair(
isolate, std::unique_ptr<base::Semaphore>(new base::Semaphore(0))));
}
}
void DefaultPlatform::WaitForForegroundWork(v8::Isolate* isolate) {
base::Semaphore* semaphore = nullptr;
{
base::LockGuard<base::Mutex> guard(&lock_);
DCHECK_EQ(event_loop_control_.count(isolate), 1);
semaphore = event_loop_control_[isolate].get();
}
DCHECK_NOT_NULL(semaphore);
semaphore->Wait();
}
bool DefaultPlatform::PumpMessageLoop(v8::Isolate* isolate,
MessageLoopBehavior behavior) {
if (behavior == MessageLoopBehavior::kWaitForWork) {
WaitForForegroundWork(isolate);
}
Task* task = NULL;
{
base::LockGuard<base::Mutex> guard(&lock_);
// Move delayed tasks that hit their deadline to the main queue.
task = PopTaskInMainThreadDelayedQueue(isolate);
while (task != NULL) {
ScheduleOnForegroundThread(isolate, task);
task = PopTaskInMainThreadDelayedQueue(isolate);
}
task = PopTaskInMainThreadQueue(isolate);
if (task == NULL) {
return behavior == MessageLoopBehavior::kWaitForWork;
}
}
task->Run();
delete task;
return true;
}
void DefaultPlatform::RunIdleTasks(v8::Isolate* isolate,
double idle_time_in_seconds) {
DCHECK(IdleTaskSupport::kEnabled == idle_task_support_);
double deadline_in_seconds =
MonotonicallyIncreasingTime() + idle_time_in_seconds;
while (deadline_in_seconds > MonotonicallyIncreasingTime()) {
{
IdleTask* task;
{
base::LockGuard<base::Mutex> guard(&lock_);
task = PopTaskInMainThreadIdleQueue(isolate);
}
if (task == nullptr) return;
task->Run(deadline_in_seconds);
delete task;
}
}
}
void DefaultPlatform::CallOnBackgroundThread(Task* task,
ExpectedRuntime expected_runtime) {
EnsureInitialized();
queue_.Append(task);
}
void DefaultPlatform::ScheduleOnForegroundThread(v8::Isolate* isolate,
Task* task) {
main_thread_queue_[isolate].push(task);
if (event_loop_control_.count(isolate) != 0) {
event_loop_control_[isolate]->Signal();
}
}
void DefaultPlatform::CallOnForegroundThread(v8::Isolate* isolate, Task* task) {
base::LockGuard<base::Mutex> guard(&lock_);
ScheduleOnForegroundThread(isolate, task);
}
void DefaultPlatform::CallDelayedOnForegroundThread(Isolate* isolate,
Task* task,
double delay_in_seconds) {
base::LockGuard<base::Mutex> guard(&lock_);
double deadline = MonotonicallyIncreasingTime() + delay_in_seconds;
main_thread_delayed_queue_[isolate].push(std::make_pair(deadline, task));
}
void DefaultPlatform::CallIdleOnForegroundThread(Isolate* isolate,
IdleTask* task) {
base::LockGuard<base::Mutex> guard(&lock_);
main_thread_idle_queue_[isolate].push(task);
}
bool DefaultPlatform::IdleTasksEnabled(Isolate* isolate) {
return idle_task_support_ == IdleTaskSupport::kEnabled;
}
double DefaultPlatform::MonotonicallyIncreasingTime() {
return base::TimeTicks::HighResolutionNow().ToInternalValue() /
static_cast<double>(base::Time::kMicrosecondsPerSecond);
}
double DefaultPlatform::CurrentClockTimeMillis() {
return base::OS::TimeCurrentMillis();
}
TracingController* DefaultPlatform::GetTracingController() {
return tracing_controller_.get();
}
void DefaultPlatform::SetTracingController(
v8::TracingController* tracing_controller) {
DCHECK_NOT_NULL(tracing_controller);
tracing_controller_.reset(tracing_controller);
}
size_t DefaultPlatform::NumberOfAvailableBackgroundThreads() {
return static_cast<size_t>(thread_pool_size_);
}
Platform::StackTracePrinter DefaultPlatform::GetStackTracePrinter() {
return PrintStackTrace;
}
} // namespace platform
} // namespace v8