[platform] Implement TaskRunners in the DefaultPlatform

This CL implements the TaskRunners in the DefaultPlatform which has been
added recently to the platform API. In addition I changed how task
posting works on the DefaultPlatform.

With this implementation the DefaultPlatform keeps one
DefaultForegroundTaskRunner per isolate, plus one
DefaultBackgroundTaskRunner. The DefaultPlatform owns these TaskRunners
with a shared_ptr, which is also shared with any caller of
GetForegroundTaskRunner or GetBackgroundTaskrunner.

This CL moves the task management from the DefaultPlatform to the
TaskRunners.  The DefaultForegroundTaskRunner owns and manages the the
task queue, the delayed task  queue, and the idle task queue. The
DefaultBackgroundTaskRunner owns the WorkerThread pool and the
background task queue.

In addition changed many Task* to std::unique_ptr<Task> to document task
ownership.

R=rmcilroy@chromium.org

Change-Id: Ib9a01f1f45e5b48844a37d801f884210ec3f6c27
Reviewed-on: https://chromium-review.googlesource.com/753583
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49354}
This commit is contained in:
Andreas Haas 2017-11-14 14:45:03 +01:00 committed by Commit Bot
parent d8c40807cb
commit 8de2e6db02
14 changed files with 610 additions and 183 deletions

View File

@ -2607,6 +2607,10 @@ v8_component("v8_libplatform") {
"include/libplatform/libplatform-export.h",
"include/libplatform/libplatform.h",
"include/libplatform/v8-tracing.h",
"src/libplatform/default-background-task-runner.cc",
"src/libplatform/default-background-task-runner.h",
"src/libplatform/default-foreground-task-runner.cc",
"src/libplatform/default-foreground-task-runner.h",
"src/libplatform/default-platform.cc",
"src/libplatform/default-platform.h",
"src/libplatform/task-queue.cc",

View File

@ -0,0 +1,56 @@
// Copyright 2017 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-background-task-runner.h"
#include "src/base/platform/mutex.h"
#include "src/libplatform/worker-thread.h"
namespace v8 {
namespace platform {
DefaultBackgroundTaskRunner::DefaultBackgroundTaskRunner(
uint32_t thread_pool_size) {
for (uint32_t i = 0; i < thread_pool_size; ++i) {
thread_pool_.push_back(base::make_unique<WorkerThread>(&queue_));
}
}
DefaultBackgroundTaskRunner::~DefaultBackgroundTaskRunner() {
base::LockGuard<base::Mutex> guard(&lock_);
queue_.Terminate();
}
void DefaultBackgroundTaskRunner::Terminate() {
base::LockGuard<base::Mutex> guard(&lock_);
terminated_ = true;
}
void DefaultBackgroundTaskRunner::PostTask(std::unique_ptr<Task> task) {
base::LockGuard<base::Mutex> guard(&lock_);
if (terminated_) return;
queue_.Append(std::move(task));
}
void DefaultBackgroundTaskRunner::PostDelayedTask(std::unique_ptr<Task> task,
double delay_in_seconds) {
base::LockGuard<base::Mutex> guard(&lock_);
if (terminated_) return;
// There is no use case for this function on a background thread at the
// moment, but it is still part of the interface.
UNIMPLEMENTED();
}
void DefaultBackgroundTaskRunner::PostIdleTask(std::unique_ptr<IdleTask> task) {
// There are no idle background tasks.
UNREACHABLE();
}
bool DefaultBackgroundTaskRunner::IdleTasksEnabled() {
// There are no idle background tasks.
return false;
}
} // namespace platform
} // namespace v8

View File

@ -0,0 +1,45 @@
// Copyright 2017 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 V8_LIBPLATFORM_DEFAULT_BACKGROUND_TASK_RUNNER_H_
#define V8_LIBPLATFORM_DEFAULT_BACKGROUND_TASK_RUNNER_H_
#include "include/v8-platform.h"
#include "src/libplatform/task-queue.h"
namespace v8 {
namespace platform {
class Thread;
class WorkerThread;
class V8_PLATFORM_EXPORT DefaultBackgroundTaskRunner
: public NON_EXPORTED_BASE(TaskRunner) {
public:
DefaultBackgroundTaskRunner(uint32_t thread_pool_size);
~DefaultBackgroundTaskRunner();
void Terminate();
// v8::TaskRunner implementation.
void PostTask(std::unique_ptr<Task> task) override;
void PostDelayedTask(std::unique_ptr<Task> task,
double delay_in_seconds) override;
void PostIdleTask(std::unique_ptr<IdleTask> task) override;
bool IdleTasksEnabled() override;
private:
bool terminated_ = false;
base::Mutex lock_;
TaskQueue queue_;
std::vector<std::unique_ptr<WorkerThread>> thread_pool_;
};
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_DEFAULT_BACKGROUND_TASK_RUNNER_H_

View File

@ -0,0 +1,115 @@
// Copyright 2017 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-foreground-task-runner.h"
#include "src/base/platform/mutex.h"
#include "src/libplatform/default-platform.h"
namespace v8 {
namespace platform {
DefaultForegroundTaskRunner::DefaultForegroundTaskRunner(
IdleTaskSupport idle_task_support, TimeFunction time_function)
: event_loop_control_(0),
idle_task_support_(idle_task_support),
time_function_(time_function) {}
void DefaultForegroundTaskRunner::Terminate() {
base::LockGuard<base::Mutex> guard(&lock_);
terminated_ = true;
// Drain the task queues.
while (!task_queue_.empty()) task_queue_.pop();
while (!delayed_task_queue_.empty()) delayed_task_queue_.pop();
while (!idle_task_queue_.empty()) idle_task_queue_.pop();
}
void DefaultForegroundTaskRunner::PostTaskLocked(
std::unique_ptr<Task> task, const base::LockGuard<base::Mutex>& guard) {
if (terminated_) return;
task_queue_.push(std::move(task));
event_loop_control_.Signal();
}
void DefaultForegroundTaskRunner::PostTask(std::unique_ptr<Task> task) {
base::LockGuard<base::Mutex> guard(&lock_);
PostTaskLocked(std::move(task), guard);
}
double DefaultForegroundTaskRunner::MonotonicallyIncreasingTime() {
return time_function_();
}
void DefaultForegroundTaskRunner::PostDelayedTask(std::unique_ptr<Task> task,
double delay_in_seconds) {
DCHECK_GE(delay_in_seconds, 0.0);
base::LockGuard<base::Mutex> guard(&lock_);
if (terminated_) return;
double deadline = MonotonicallyIncreasingTime() + delay_in_seconds;
delayed_task_queue_.push(std::make_pair(deadline, std::move(task)));
}
void DefaultForegroundTaskRunner::PostIdleTask(std::unique_ptr<IdleTask> task) {
CHECK_EQ(IdleTaskSupport::kEnabled, idle_task_support_);
base::LockGuard<base::Mutex> guard(&lock_);
if (terminated_) return;
idle_task_queue_.push(std::move(task));
}
bool DefaultForegroundTaskRunner::IdleTasksEnabled() {
return idle_task_support_ == IdleTaskSupport::kEnabled;
}
std::unique_ptr<Task> DefaultForegroundTaskRunner::PopTaskFromQueue() {
base::LockGuard<base::Mutex> guard(&lock_);
// Move delayed tasks that hit their deadline to the main queue.
std::unique_ptr<Task> task = PopTaskFromDelayedQueueLocked(guard);
while (task) {
PostTaskLocked(std::move(task), guard);
task = PopTaskFromDelayedQueueLocked(guard);
}
if (task_queue_.empty()) return {};
task = std::move(task_queue_.front());
task_queue_.pop();
return task;
}
std::unique_ptr<Task>
DefaultForegroundTaskRunner::PopTaskFromDelayedQueueLocked(
const base::LockGuard<base::Mutex>& guard) {
if (delayed_task_queue_.empty()) return {};
double now = MonotonicallyIncreasingTime();
const DelayedEntry& deadline_and_task = delayed_task_queue_.top();
if (deadline_and_task.first > now) return {};
// The const_cast here is necessary because there does not exist a clean way
// to get a unique_ptr out of the priority queue. We provide the priority
// queue with a custom comparison operator to make sure that the priority
// queue does not access the unique_ptr. Therefore it should be safe to reset
// the unique_ptr in the priority queue here. Note that the DelayedEntry is
// removed from the priority_queue immediately afterwards.
std::unique_ptr<Task> result =
std::move(const_cast<DelayedEntry&>(deadline_and_task).second);
delayed_task_queue_.pop();
return result;
}
std::unique_ptr<IdleTask> DefaultForegroundTaskRunner::PopTaskFromIdleQueue() {
base::LockGuard<base::Mutex> guard(&lock_);
if (idle_task_queue_.empty()) return {};
std::unique_ptr<IdleTask> task = std::move(idle_task_queue_.front());
idle_task_queue_.pop();
return task;
}
void DefaultForegroundTaskRunner::WaitForTask() { event_loop_control_.Wait(); }
} // namespace platform
} // namespace v8

View File

@ -0,0 +1,84 @@
// Copyright 2017 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 V8_LIBPLATFORM_DEFAULT_FOREGROUND_TASK_RUNNER_H_
#define V8_LIBPLATFORM_DEFAULT_FOREGROUND_TASK_RUNNER_H_
#include <queue>
#include "include/libplatform/libplatform.h"
#include "include/v8-platform.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/semaphore.h"
namespace v8 {
namespace platform {
class V8_PLATFORM_EXPORT DefaultForegroundTaskRunner
: public NON_EXPORTED_BASE(TaskRunner) {
public:
using TimeFunction = double (*)();
DefaultForegroundTaskRunner(IdleTaskSupport idle_task_support,
TimeFunction time_function);
void Terminate();
std::unique_ptr<Task> PopTaskFromQueue();
std::unique_ptr<IdleTask> PopTaskFromIdleQueue();
void WaitForTask();
double MonotonicallyIncreasingTime();
// v8::TaskRunner implementation.
void PostTask(std::unique_ptr<Task> task) override;
void PostDelayedTask(std::unique_ptr<Task> task,
double delay_in_seconds) override;
void PostIdleTask(std::unique_ptr<IdleTask> task) override;
bool IdleTasksEnabled() override;
private:
// The same as PostTask, but the lock is already held by the caller. The
// {guard} parameter should make sure that the caller is holding the lock.
void PostTaskLocked(std::unique_ptr<Task> task,
const base::LockGuard<base::Mutex>& guard);
// A caller of this function has to hold {lock_}. The {guard} parameter should
// make sure that the caller is holding the lock.
std::unique_ptr<Task> PopTaskFromDelayedQueueLocked(
const base::LockGuard<base::Mutex>& guard);
bool terminated_ = false;
base::Mutex lock_;
base::Semaphore event_loop_control_;
std::queue<std::unique_ptr<Task>> task_queue_;
IdleTaskSupport idle_task_support_;
std::queue<std::unique_ptr<IdleTask>> idle_task_queue_;
// Some helper constructs for the {delayed_task_queue_}.
using DelayedEntry = std::pair<double, std::unique_ptr<Task>>;
// Define a comparison operator for the delayed_task_queue_ to make sure
// that the unique_ptr in the DelayedEntry is not accessed in the priority
// queue. This is necessary because we have to reset the unique_ptr when we
// remove a DelayedEntry from the priority queue.
struct DelayedEntryCompare {
bool operator()(DelayedEntry& left, DelayedEntry& right) {
return left.first > right.first;
}
};
std::priority_queue<DelayedEntry, std::vector<DelayedEntry>,
DelayedEntryCompare>
delayed_task_queue_;
TimeFunction time_function_;
};
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_DEFAULT_FOREGROUND_TASK_RUNNER_H_

View File

@ -13,7 +13,8 @@
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "src/base/sys-info.h"
#include "src/libplatform/worker-thread.h"
#include "src/libplatform/default-background-task-runner.h"
#include "src/libplatform/default-foreground-task-runner.h"
namespace v8 {
namespace platform {
@ -39,7 +40,7 @@ std::unique_ptr<v8::Platform> NewDefaultPlatform(
std::unique_ptr<DefaultPlatform> platform(
new DefaultPlatform(idle_task_support, std::move(tracing_controller)));
platform->SetThreadPoolSize(thread_pool_size);
platform->EnsureInitialized();
platform->EnsureBackgroundTaskRunnerInitialized();
return std::move(platform);
}
@ -60,8 +61,6 @@ bool PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate,
}
void EnsureEventLoopInitialized(v8::Platform* platform, v8::Isolate* isolate) {
return static_cast<DefaultPlatform*>(platform)->EnsureEventLoopInitialized(
isolate);
}
void RunIdleTasks(v8::Platform* platform, v8::Isolate* isolate,
@ -82,10 +81,10 @@ const int DefaultPlatform::kMaxThreadPoolSize = 8;
DefaultPlatform::DefaultPlatform(
IdleTaskSupport idle_task_support,
std::unique_ptr<v8::TracingController> tracing_controller)
: initialized_(false),
thread_pool_size_(0),
: thread_pool_size_(0),
idle_task_support_(idle_task_support),
tracing_controller_(std::move(tracing_controller)) {
tracing_controller_(std::move(tracing_controller)),
time_function_for_testing_(nullptr) {
if (!tracing_controller_) {
tracing::TracingController* controller = new tracing::TracingController();
controller->Initialize(nullptr);
@ -94,36 +93,12 @@ DefaultPlatform::DefaultPlatform(
}
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();
}
if (background_task_runner_) background_task_runner_->Terminate();
for (auto it : foreground_task_runner_map_) {
it.second->Terminate();
}
}
void DefaultPlatform::SetThreadPoolSize(int thread_pool_size) {
base::LockGuard<base::Mutex> guard(&lock_);
DCHECK_GE(thread_pool_size, 0);
@ -134,149 +109,148 @@ void DefaultPlatform::SetThreadPoolSize(int thread_pool_size) {
std::max(std::min(thread_pool_size, kMaxThreadPoolSize), 1);
}
void DefaultPlatform::EnsureInitialized() {
void DefaultPlatform::EnsureBackgroundTaskRunnerInitialized() {
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_));
if (!background_task_runner_) {
background_task_runner_ =
std::make_shared<DefaultBackgroundTaskRunner>(thread_pool_size_);
}
}
namespace {
Task* DefaultPlatform::PopTaskInMainThreadQueue(v8::Isolate* isolate) {
auto it = main_thread_queue_.find(isolate);
if (it == main_thread_queue_.end() || it->second.empty()) {
return nullptr;
}
Task* task = it->second.front();
it->second.pop();
return task;
double DefaultTimeFunction() {
return base::TimeTicks::HighResolutionNow().ToInternalValue() /
static_cast<double>(base::Time::kMicrosecondsPerSecond);
}
} // namespace
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 nullptr;
}
double now = MonotonicallyIncreasingTime();
std::pair<double, Task*> deadline_and_task = it->second.top();
if (deadline_and_task.first > now) {
return nullptr;
}
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) {
void DefaultPlatform::EnsureForegroundTaskRunnerInitialized(
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))));
if (foreground_task_runner_map_.find(isolate) ==
foreground_task_runner_map_.end()) {
foreground_task_runner_map_.insert(std::make_pair(
isolate, std::make_shared<DefaultForegroundTaskRunner>(
idle_task_support_, time_function_for_testing_
? time_function_for_testing_
: DefaultTimeFunction)));
}
}
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();
void DefaultPlatform::SetTimeFunctionForTesting(
DefaultPlatform::TimeFunction time_function) {
time_function_for_testing_ = time_function;
// The time function has to be right after the construction of the platform.
DCHECK(foreground_task_runner_map_.empty());
}
bool DefaultPlatform::PumpMessageLoop(v8::Isolate* isolate,
MessageLoopBehavior behavior) {
bool failed_result = behavior == MessageLoopBehavior::kWaitForWork;
if (foreground_task_runner_map_.find(isolate) ==
foreground_task_runner_map_.end()) {
return failed_result;
}
if (behavior == MessageLoopBehavior::kWaitForWork) {
WaitForForegroundWork(isolate);
foreground_task_runner_map_[isolate]->WaitForTask();
}
Task* task = nullptr;
{
base::LockGuard<base::Mutex> guard(&lock_);
// Move delayed tasks that hit their deadline to the main queue.
task = PopTaskInMainThreadDelayedQueue(isolate);
while (task != nullptr) {
ScheduleOnForegroundThread(isolate, task);
task = PopTaskInMainThreadDelayedQueue(isolate);
}
auto it = foreground_task_runner_map_.find(isolate);
if (it == foreground_task_runner_map_.end()) return failed_result;
std::shared_ptr<DefaultForegroundTaskRunner>& task_runner = it->second;
task = PopTaskInMainThreadQueue(isolate);
std::unique_ptr<Task> task = task_runner->PopTaskFromQueue();
if (!task) return failed_result;
if (task == nullptr) {
return behavior == MessageLoopBehavior::kWaitForWork;
}
}
task->Run();
delete task;
return true;
}
void DefaultPlatform::RunIdleTasks(v8::Isolate* isolate,
double idle_time_in_seconds) {
if (foreground_task_runner_map_.find(isolate) ==
foreground_task_runner_map_.end())
return;
DCHECK_EQ(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;
}
std::unique_ptr<IdleTask> task =
foreground_task_runner_map_[isolate]->PopTaskFromIdleQueue();
if (!task) return;
task->Run(deadline_in_seconds);
}
}
namespace {
// A wrapper for the TaskRunner to comply with the platform API interface. The
// TaskRunner has to be returned as a unique_ptr but is available as a
// shared_ptr on the default platform.
class TaskRunnerWrapper : public NON_EXPORTED_BASE(TaskRunner) {
public:
TaskRunnerWrapper(std::shared_ptr<TaskRunner> task_runner)
: task_runner_(task_runner) {}
void PostTask(std::unique_ptr<Task> task) override {
task_runner_->PostTask(std::move(task));
}
void PostDelayedTask(std::unique_ptr<Task> task,
double delay_in_seconds) override {
task_runner_->PostDelayedTask(std::move(task), delay_in_seconds);
}
void PostIdleTask(std::unique_ptr<IdleTask> task) override {
task_runner_->PostIdleTask(std::move(task));
}
bool IdleTasksEnabled() override { return task_runner_->IdleTasksEnabled(); }
private:
std::shared_ptr<TaskRunner> task_runner_;
};
} // namespace
std::unique_ptr<TaskRunner> DefaultPlatform::GetForegroundTaskRunner(
v8::Isolate* isolate) {
EnsureForegroundTaskRunnerInitialized(isolate);
return base::make_unique<TaskRunnerWrapper>(
foreground_task_runner_map_[isolate]);
}
std::unique_ptr<TaskRunner> DefaultPlatform::GetBackgroundTaskRunner(
v8::Isolate*) {
EnsureBackgroundTaskRunnerInitialized();
return base::make_unique<TaskRunnerWrapper>(background_task_runner_);
}
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();
}
GetBackgroundTaskRunner(nullptr)->PostTask(std::unique_ptr<Task>(task));
}
void DefaultPlatform::CallOnForegroundThread(v8::Isolate* isolate, Task* task) {
base::LockGuard<base::Mutex> guard(&lock_);
ScheduleOnForegroundThread(isolate, task);
GetForegroundTaskRunner(isolate)->PostTask(std::unique_ptr<Task>(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));
GetForegroundTaskRunner(isolate)->PostDelayedTask(std::unique_ptr<Task>(task),
delay_in_seconds);
}
void DefaultPlatform::CallIdleOnForegroundThread(Isolate* isolate,
IdleTask* task) {
base::LockGuard<base::Mutex> guard(&lock_);
main_thread_idle_queue_[isolate].push(task);
GetForegroundTaskRunner(isolate)->PostIdleTask(
std::unique_ptr<IdleTask>(task));
}
bool DefaultPlatform::IdleTasksEnabled(Isolate* isolate) {
@ -284,8 +258,8 @@ bool DefaultPlatform::IdleTasksEnabled(Isolate* isolate) {
}
double DefaultPlatform::MonotonicallyIncreasingTime() {
return base::TimeTicks::HighResolutionNow().ToInternalValue() /
static_cast<double>(base::Time::kMicrosecondsPerSecond);
if (time_function_for_testing_) return time_function_for_testing_();
return DefaultTimeFunction();
}
double DefaultPlatform::CurrentClockTimeMillis() {

View File

@ -18,38 +18,49 @@
#include "src/base/compiler-specific.h"
#include "src/base/macros.h"
#include "src/base/platform/mutex.h"
#include "src/libplatform/task-queue.h"
#include "src/base/platform/time.h"
namespace v8 {
namespace platform {
class TaskQueue;
class Thread;
class WorkerThread;
class DefaultForegroundTaskRunner;
class DefaultBackgroundTaskRunner;
class V8_PLATFORM_EXPORT DefaultPlatform : public NON_EXPORTED_BASE(Platform) {
public:
explicit DefaultPlatform(
IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled,
std::unique_ptr<v8::TracingController> tracing_controller = {nullptr});
std::unique_ptr<v8::TracingController> tracing_controller = {});
virtual ~DefaultPlatform();
void SetThreadPoolSize(int thread_pool_size);
void EnsureInitialized();
void EnsureBackgroundTaskRunnerInitialized();
void EnsureForegroundTaskRunnerInitialized(v8::Isolate* isolate);
bool PumpMessageLoop(
v8::Isolate* isolate,
MessageLoopBehavior behavior = MessageLoopBehavior::kDoNotWait);
void EnsureEventLoopInitialized(v8::Isolate* isolate);
void RunIdleTasks(v8::Isolate* isolate, double idle_time_in_seconds);
void SetTracingController(
std::unique_ptr<v8::TracingController> tracing_controller);
using TimeFunction = double (*)();
void SetTimeFunctionForTesting(TimeFunction time_function);
// v8::Platform implementation.
size_t NumberOfAvailableBackgroundThreads() override;
std::unique_ptr<TaskRunner> GetForegroundTaskRunner(
v8::Isolate* isolate) override;
std::unique_ptr<TaskRunner> GetBackgroundTaskRunner(
v8::Isolate* isolate) override;
void CallOnBackgroundThread(Task* task,
ExpectedRuntime expected_runtime) override;
void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override;
@ -65,34 +76,19 @@ class V8_PLATFORM_EXPORT DefaultPlatform : public NON_EXPORTED_BASE(Platform) {
private:
static const int kMaxThreadPoolSize;
Task* PopTaskInMainThreadQueue(v8::Isolate* isolate);
Task* PopTaskInMainThreadDelayedQueue(v8::Isolate* isolate);
IdleTask* PopTaskInMainThreadIdleQueue(v8::Isolate* isolate);
void WaitForForegroundWork(v8::Isolate* isolate);
void ScheduleOnForegroundThread(v8::Isolate* isolate, Task* task);
base::Mutex lock_;
bool initialized_;
int thread_pool_size_;
IdleTaskSupport idle_task_support_;
std::vector<WorkerThread*> thread_pool_;
TaskQueue queue_;
std::map<v8::Isolate*, std::queue<Task*>> main_thread_queue_;
std::map<v8::Isolate*, std::queue<IdleTask*>> main_thread_idle_queue_;
std::map<v8::Isolate*, std::unique_ptr<base::Semaphore>> event_loop_control_;
std::shared_ptr<DefaultBackgroundTaskRunner> background_task_runner_;
std::map<v8::Isolate*, std::shared_ptr<DefaultForegroundTaskRunner>>
foreground_task_runner_map_;
typedef std::pair<double, Task*> DelayedEntry;
std::map<v8::Isolate*,
std::priority_queue<DelayedEntry, std::vector<DelayedEntry>,
std::greater<DelayedEntry> > >
main_thread_delayed_queue_;
std::unique_ptr<TracingController> tracing_controller_;
TimeFunction time_function_for_testing_;
DISALLOW_COPY_AND_ASSIGN(DefaultPlatform);
};
} // namespace platform
} // namespace v8

View File

@ -4,6 +4,7 @@
#include "src/libplatform/task-queue.h"
#include "include/v8-platform.h"
#include "src/base/logging.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
@ -20,21 +21,19 @@ TaskQueue::~TaskQueue() {
DCHECK(task_queue_.empty());
}
void TaskQueue::Append(Task* task) {
void TaskQueue::Append(std::unique_ptr<Task> task) {
base::LockGuard<base::Mutex> guard(&lock_);
DCHECK(!terminated_);
task_queue_.push(task);
task_queue_.push(std::move(task));
process_queue_semaphore_.Signal();
}
Task* TaskQueue::GetNext() {
std::unique_ptr<Task> TaskQueue::GetNext() {
for (;;) {
{
base::LockGuard<base::Mutex> guard(&lock_);
if (!task_queue_.empty()) {
Task* result = task_queue_.front();
std::unique_ptr<Task> result = std::move(task_queue_.front());
task_queue_.pop();
return result;
}

View File

@ -25,11 +25,11 @@ class V8_PLATFORM_EXPORT TaskQueue {
~TaskQueue();
// Appends a task to the queue. The queue takes ownership of |task|.
void Append(Task* task);
void Append(std::unique_ptr<Task> task);
// Returns the next task to process. Blocks if no task is available. Returns
// nullptr if the queue is terminated.
Task* GetNext();
std::unique_ptr<Task> GetNext();
// Terminate the queue.
void Terminate();
@ -41,7 +41,7 @@ class V8_PLATFORM_EXPORT TaskQueue {
base::Semaphore process_queue_semaphore_;
base::Mutex lock_;
std::queue<Task*> task_queue_;
std::queue<std::unique_ptr<Task>> task_queue_;
bool terminated_;
DISALLOW_COPY_AND_ASSIGN(TaskQueue);

View File

@ -22,9 +22,8 @@ WorkerThread::~WorkerThread() {
void WorkerThread::Run() {
while (Task* task = queue_->GetNext()) {
while (std::unique_ptr<Task> task = queue_->GetNext()) {
task->Run();
delete task;
}
}

View File

@ -2188,6 +2188,10 @@
'../include/libplatform/libplatform.h',
'../include/libplatform/libplatform-export.h',
'../include/libplatform/v8-tracing.h',
'libplatform/default-background-task-runner.cc',
'libplatform/default-background-task-runner.h',
'libplatform/default-foreground-task-runner.cc',
'libplatform/default-foreground-task-runner.h',
'libplatform/default-platform.cc',
'libplatform/default-platform.h',
'libplatform/task-queue.cc',

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/libplatform/default-platform.h"
#include "src/base/platform/semaphore.h"
#include "src/base/platform/time.h"
#include "testing/gmock/include/gmock/gmock.h"
@ -30,17 +31,18 @@ struct MockIdleTask : public IdleTask {
class DefaultPlatformWithMockTime : public DefaultPlatform {
public:
DefaultPlatformWithMockTime()
: DefaultPlatform(IdleTaskSupport::kEnabled), time_(0) {}
double MonotonicallyIncreasingTime() override { return time_; }
double CurrentClockTimeMillis() override {
return time_ * base::Time::kMillisecondsPerSecond;
: DefaultPlatform(IdleTaskSupport::kEnabled, nullptr) {
mock_time_ = 0.0;
SetTimeFunctionForTesting([]() { return mock_time_; });
}
void IncreaseTime(double seconds) { time_ += seconds; }
void IncreaseTime(double seconds) { mock_time_ += seconds; }
private:
double time_;
static double mock_time_;
};
double DefaultPlatformWithMockTime::mock_time_ = 0.0;
} // namespace
@ -61,6 +63,24 @@ TEST(DefaultPlatformTest, PumpMessageLoop) {
EXPECT_FALSE(platform.PumpMessageLoop(isolate));
}
TEST(DefaultPlatformTest, PumpMessageLoopWithTaskRunner) {
InSequence s;
int dummy;
Isolate* isolate = reinterpret_cast<Isolate*>(&dummy);
DefaultPlatform platform;
std::unique_ptr<TaskRunner> taskrunner =
platform.GetForegroundTaskRunner(isolate);
EXPECT_FALSE(platform.PumpMessageLoop(isolate));
StrictMock<MockTask>* task = new StrictMock<MockTask>;
taskrunner->PostTask(std::unique_ptr<Task>(task));
EXPECT_CALL(*task, Run());
EXPECT_CALL(*task, Die());
EXPECT_TRUE(platform.PumpMessageLoop(isolate));
EXPECT_FALSE(platform.PumpMessageLoop(isolate));
}
TEST(DefaultPlatformTest, PumpMessageLoopDelayed) {
InSequence s;
@ -91,6 +111,36 @@ TEST(DefaultPlatformTest, PumpMessageLoopDelayed) {
EXPECT_TRUE(platform.PumpMessageLoop(isolate));
}
TEST(DefaultPlatformTest, PumpMessageLoopDelayedWithTaskRunner) {
InSequence s;
int dummy;
Isolate* isolate = reinterpret_cast<Isolate*>(&dummy);
DefaultPlatformWithMockTime platform;
std::unique_ptr<TaskRunner> taskrunner =
platform.GetForegroundTaskRunner(isolate);
EXPECT_FALSE(platform.PumpMessageLoop(isolate));
StrictMock<MockTask>* task1 = new StrictMock<MockTask>;
StrictMock<MockTask>* task2 = new StrictMock<MockTask>;
taskrunner->PostDelayedTask(std::unique_ptr<Task>(task2), 100);
taskrunner->PostDelayedTask(std::unique_ptr<Task>(task1), 10);
EXPECT_FALSE(platform.PumpMessageLoop(isolate));
platform.IncreaseTime(11);
EXPECT_CALL(*task1, Run());
EXPECT_CALL(*task1, Die());
EXPECT_TRUE(platform.PumpMessageLoop(isolate));
EXPECT_FALSE(platform.PumpMessageLoop(isolate));
platform.IncreaseTime(90);
EXPECT_CALL(*task2, Run());
EXPECT_CALL(*task2, Die());
EXPECT_TRUE(platform.PumpMessageLoop(isolate));
}
TEST(DefaultPlatformTest, PumpMessageLoopNoStarvation) {
InSequence s;
@ -153,6 +203,24 @@ TEST(DefaultPlatformTest, RunIdleTasks) {
platform.RunIdleTasks(isolate, 42.0);
}
TEST(DefaultPlatformTest, RunIdleTasksWithTaskRunner) {
InSequence s;
int dummy;
Isolate* isolate = reinterpret_cast<Isolate*>(&dummy);
DefaultPlatformWithMockTime platform;
std::unique_ptr<TaskRunner> taskrunner =
platform.GetForegroundTaskRunner(isolate);
StrictMock<MockIdleTask>* task = new StrictMock<MockIdleTask>;
taskrunner->PostIdleTask(std::unique_ptr<IdleTask>(task));
EXPECT_CALL(*task, Run(42.0 + 23.0));
EXPECT_CALL(*task, Die());
platform.IncreaseTime(23.0);
platform.RunIdleTasks(isolate, 42.0);
}
TEST(DefaultPlatformTest, PendingIdleTasksAreDestroyedOnShutdown) {
InSequence s;
@ -167,6 +235,88 @@ TEST(DefaultPlatformTest, PendingIdleTasksAreDestroyedOnShutdown) {
}
}
namespace {
class TestBackgroundTask : public Task {
public:
explicit TestBackgroundTask(base::Semaphore* sem, bool* executed)
: sem_(sem), executed_(executed) {}
virtual ~TestBackgroundTask() { Die(); }
MOCK_METHOD0(Die, void());
void Run() {
*executed_ = true;
sem_->Signal();
}
private:
base::Semaphore* sem_;
bool* executed_;
};
} // namespace
TEST(DefaultPlatformTest, RunBackgroundTask) {
int dummy;
Isolate* isolate = reinterpret_cast<Isolate*>(&dummy);
DefaultPlatform platform;
platform.SetThreadPoolSize(1);
std::unique_ptr<TaskRunner> taskrunner =
platform.GetBackgroundTaskRunner(isolate);
base::Semaphore sem(0);
bool task_executed = false;
StrictMock<TestBackgroundTask>* task =
new StrictMock<TestBackgroundTask>(&sem, &task_executed);
EXPECT_CALL(*task, Die());
taskrunner->PostTask(std::unique_ptr<Task>(task));
EXPECT_TRUE(sem.WaitFor(base::TimeDelta::FromSeconds(1)));
EXPECT_TRUE(task_executed);
}
TEST(DefaultPlatformTest, NoIdleTasksInBackground) {
int dummy;
Isolate* isolate = reinterpret_cast<Isolate*>(&dummy);
DefaultPlatform platform;
platform.SetThreadPoolSize(1);
std::unique_ptr<TaskRunner> taskrunner =
platform.GetBackgroundTaskRunner(isolate);
EXPECT_FALSE(taskrunner->IdleTasksEnabled());
}
TEST(DefaultPlatformTest, PostTaskAfterPlatformTermination) {
std::unique_ptr<TaskRunner> foreground_taskrunner;
std::unique_ptr<TaskRunner> background_taskrunner;
{
int dummy;
Isolate* isolate = reinterpret_cast<Isolate*>(&dummy);
DefaultPlatformWithMockTime platform;
platform.SetThreadPoolSize(1);
foreground_taskrunner = platform.GetForegroundTaskRunner(isolate);
background_taskrunner = platform.GetBackgroundTaskRunner(isolate);
}
// It should still be possible to post tasks, even when the platform does not
// exist anymore.
StrictMock<MockTask>* task1 = new StrictMock<MockTask>;
EXPECT_CALL(*task1, Die());
foreground_taskrunner->PostTask(std::unique_ptr<Task>(task1));
StrictMock<MockTask>* task2 = new StrictMock<MockTask>;
EXPECT_CALL(*task2, Die());
foreground_taskrunner->PostDelayedTask(std::unique_ptr<Task>(task2), 10);
StrictMock<MockIdleTask>* task3 = new StrictMock<MockIdleTask>;
EXPECT_CALL(*task3, Die());
foreground_taskrunner->PostIdleTask(std::unique_ptr<IdleTask>(task3));
StrictMock<MockTask>* task4 = new StrictMock<MockTask>;
EXPECT_CALL(*task4, Die());
background_taskrunner->PostTask(std::unique_ptr<Task>(task4));
}
} // namespace default_platform_unittest
} // namespace platform
} // namespace v8

View File

@ -38,9 +38,10 @@ class TaskQueueThread final : public base::Thread {
TEST(TaskQueueTest, Basic) {
TaskQueue queue;
MockTask task;
queue.Append(&task);
EXPECT_EQ(&task, queue.GetNext());
std::unique_ptr<Task> task(new MockTask());
Task* ptr = task.get();
queue.Append(std::move(task));
EXPECT_EQ(ptr, queue.GetNext().get());
queue.Terminate();
EXPECT_THAT(queue.GetNext(), IsNull());
}

View File

@ -32,10 +32,10 @@ TEST(WorkerThreadTest, PostSingleTask) {
WorkerThread thread2(&queue);
InSequence s;
StrictMock<MockTask>* task = new StrictMock<MockTask>;
EXPECT_CALL(*task, Run());
EXPECT_CALL(*task, Die());
queue.Append(task);
std::unique_ptr<StrictMock<MockTask>> task(new StrictMock<MockTask>);
EXPECT_CALL(*task.get(), Run());
EXPECT_CALL(*task.get(), Die());
queue.Append(std::move(task));
// The next call should not time out.
queue.BlockUntilQueueEmptyForTesting();
@ -50,10 +50,10 @@ TEST(WorkerThreadTest, Basic) {
TaskQueue queue;
for (size_t i = 0; i < kNumTasks; ++i) {
InSequence s;
StrictMock<MockTask>* task = new StrictMock<MockTask>;
EXPECT_CALL(*task, Run());
EXPECT_CALL(*task, Die());
queue.Append(task);
std::unique_ptr<StrictMock<MockTask>> task(new StrictMock<MockTask>);
EXPECT_CALL(*task.get(), Run());
EXPECT_CALL(*task.get(), Die());
queue.Append(std::move(task));
}
WorkerThread thread1(&queue);