Commit 8de2e6db authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[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: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49354}
parent d8c40807
...@@ -2607,6 +2607,10 @@ v8_component("v8_libplatform") { ...@@ -2607,6 +2607,10 @@ v8_component("v8_libplatform") {
"include/libplatform/libplatform-export.h", "include/libplatform/libplatform-export.h",
"include/libplatform/libplatform.h", "include/libplatform/libplatform.h",
"include/libplatform/v8-tracing.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.cc",
"src/libplatform/default-platform.h", "src/libplatform/default-platform.h",
"src/libplatform/task-queue.cc", "src/libplatform/task-queue.cc",
......
// 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
// 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_
// 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
// 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_
This diff is collapsed.
...@@ -18,38 +18,49 @@ ...@@ -18,38 +18,49 @@
#include "src/base/compiler-specific.h" #include "src/base/compiler-specific.h"
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/base/platform/mutex.h" #include "src/base/platform/mutex.h"
#include "src/libplatform/task-queue.h" #include "src/base/platform/time.h"
namespace v8 { namespace v8 {
namespace platform { namespace platform {
class TaskQueue;
class Thread; class Thread;
class WorkerThread; class WorkerThread;
class DefaultForegroundTaskRunner;
class DefaultBackgroundTaskRunner;
class V8_PLATFORM_EXPORT DefaultPlatform : public NON_EXPORTED_BASE(Platform) { class V8_PLATFORM_EXPORT DefaultPlatform : public NON_EXPORTED_BASE(Platform) {
public: public:
explicit DefaultPlatform( explicit DefaultPlatform(
IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled, IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled,
std::unique_ptr<v8::TracingController> tracing_controller = {nullptr}); std::unique_ptr<v8::TracingController> tracing_controller = {});
virtual ~DefaultPlatform(); virtual ~DefaultPlatform();
void SetThreadPoolSize(int thread_pool_size); void SetThreadPoolSize(int thread_pool_size);
void EnsureInitialized(); void EnsureBackgroundTaskRunnerInitialized();
void EnsureForegroundTaskRunnerInitialized(v8::Isolate* isolate);
bool PumpMessageLoop( bool PumpMessageLoop(
v8::Isolate* isolate, v8::Isolate* isolate,
MessageLoopBehavior behavior = MessageLoopBehavior::kDoNotWait); MessageLoopBehavior behavior = MessageLoopBehavior::kDoNotWait);
void EnsureEventLoopInitialized(v8::Isolate* isolate);
void RunIdleTasks(v8::Isolate* isolate, double idle_time_in_seconds); void RunIdleTasks(v8::Isolate* isolate, double idle_time_in_seconds);
void SetTracingController( void SetTracingController(
std::unique_ptr<v8::TracingController> tracing_controller); std::unique_ptr<v8::TracingController> tracing_controller);
using TimeFunction = double (*)();
void SetTimeFunctionForTesting(TimeFunction time_function);
// v8::Platform implementation. // v8::Platform implementation.
size_t NumberOfAvailableBackgroundThreads() override; 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, void CallOnBackgroundThread(Task* task,
ExpectedRuntime expected_runtime) override; ExpectedRuntime expected_runtime) override;
void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override; void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override;
...@@ -65,34 +76,19 @@ class V8_PLATFORM_EXPORT DefaultPlatform : public NON_EXPORTED_BASE(Platform) { ...@@ -65,34 +76,19 @@ class V8_PLATFORM_EXPORT DefaultPlatform : public NON_EXPORTED_BASE(Platform) {
private: private:
static const int kMaxThreadPoolSize; 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_; base::Mutex lock_;
bool initialized_;
int thread_pool_size_; int thread_pool_size_;
IdleTaskSupport idle_task_support_; IdleTaskSupport idle_task_support_;
std::vector<WorkerThread*> thread_pool_; std::shared_ptr<DefaultBackgroundTaskRunner> background_task_runner_;
TaskQueue queue_; std::map<v8::Isolate*, std::shared_ptr<DefaultForegroundTaskRunner>>
std::map<v8::Isolate*, std::queue<Task*>> main_thread_queue_; foreground_task_runner_map_;
std::map<v8::Isolate*, std::queue<IdleTask*>> main_thread_idle_queue_;
std::map<v8::Isolate*, std::unique_ptr<base::Semaphore>> event_loop_control_;
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_; std::unique_ptr<TracingController> tracing_controller_;
TimeFunction time_function_for_testing_;
DISALLOW_COPY_AND_ASSIGN(DefaultPlatform); DISALLOW_COPY_AND_ASSIGN(DefaultPlatform);
}; };
} // namespace platform } // namespace platform
} // namespace v8 } // namespace v8
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/libplatform/task-queue.h" #include "src/libplatform/task-queue.h"
#include "include/v8-platform.h"
#include "src/base/logging.h" #include "src/base/logging.h"
#include "src/base/platform/platform.h" #include "src/base/platform/platform.h"
#include "src/base/platform/time.h" #include "src/base/platform/time.h"
...@@ -20,21 +21,19 @@ TaskQueue::~TaskQueue() { ...@@ -20,21 +21,19 @@ TaskQueue::~TaskQueue() {
DCHECK(task_queue_.empty()); DCHECK(task_queue_.empty());
} }
void TaskQueue::Append(std::unique_ptr<Task> task) {
void TaskQueue::Append(Task* task) {
base::LockGuard<base::Mutex> guard(&lock_); base::LockGuard<base::Mutex> guard(&lock_);
DCHECK(!terminated_); DCHECK(!terminated_);
task_queue_.push(task); task_queue_.push(std::move(task));
process_queue_semaphore_.Signal(); process_queue_semaphore_.Signal();
} }
std::unique_ptr<Task> TaskQueue::GetNext() {
Task* TaskQueue::GetNext() {
for (;;) { for (;;) {
{ {
base::LockGuard<base::Mutex> guard(&lock_); base::LockGuard<base::Mutex> guard(&lock_);
if (!task_queue_.empty()) { if (!task_queue_.empty()) {
Task* result = task_queue_.front(); std::unique_ptr<Task> result = std::move(task_queue_.front());
task_queue_.pop(); task_queue_.pop();
return result; return result;
} }
......
...@@ -25,11 +25,11 @@ class V8_PLATFORM_EXPORT TaskQueue { ...@@ -25,11 +25,11 @@ class V8_PLATFORM_EXPORT TaskQueue {
~TaskQueue(); ~TaskQueue();
// Appends a task to the queue. The queue takes ownership of |task|. // 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 // Returns the next task to process. Blocks if no task is available. Returns
// nullptr if the queue is terminated. // nullptr if the queue is terminated.
Task* GetNext(); std::unique_ptr<Task> GetNext();
// Terminate the queue. // Terminate the queue.
void Terminate(); void Terminate();
...@@ -41,7 +41,7 @@ class V8_PLATFORM_EXPORT TaskQueue { ...@@ -41,7 +41,7 @@ class V8_PLATFORM_EXPORT TaskQueue {
base::Semaphore process_queue_semaphore_; base::Semaphore process_queue_semaphore_;
base::Mutex lock_; base::Mutex lock_;
std::queue<Task*> task_queue_; std::queue<std::unique_ptr<Task>> task_queue_;
bool terminated_; bool terminated_;
DISALLOW_COPY_AND_ASSIGN(TaskQueue); DISALLOW_COPY_AND_ASSIGN(TaskQueue);
......
...@@ -22,9 +22,8 @@ WorkerThread::~WorkerThread() { ...@@ -22,9 +22,8 @@ WorkerThread::~WorkerThread() {
void WorkerThread::Run() { void WorkerThread::Run() {
while (Task* task = queue_->GetNext()) { while (std::unique_ptr<Task> task = queue_->GetNext()) {
task->Run(); task->Run();
delete task;
} }
} }
......
...@@ -2188,6 +2188,10 @@ ...@@ -2188,6 +2188,10 @@
'../include/libplatform/libplatform.h', '../include/libplatform/libplatform.h',
'../include/libplatform/libplatform-export.h', '../include/libplatform/libplatform-export.h',
'../include/libplatform/v8-tracing.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.cc',
'libplatform/default-platform.h', 'libplatform/default-platform.h',
'libplatform/task-queue.cc', 'libplatform/task-queue.cc',
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "src/libplatform/default-platform.h" #include "src/libplatform/default-platform.h"
#include "src/base/platform/semaphore.h"
#include "src/base/platform/time.h" #include "src/base/platform/time.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
...@@ -30,17 +31,18 @@ struct MockIdleTask : public IdleTask { ...@@ -30,17 +31,18 @@ struct MockIdleTask : public IdleTask {
class DefaultPlatformWithMockTime : public DefaultPlatform { class DefaultPlatformWithMockTime : public DefaultPlatform {
public: public:
DefaultPlatformWithMockTime() DefaultPlatformWithMockTime()
: DefaultPlatform(IdleTaskSupport::kEnabled), time_(0) {} : DefaultPlatform(IdleTaskSupport::kEnabled, nullptr) {
double MonotonicallyIncreasingTime() override { return time_; } mock_time_ = 0.0;
double CurrentClockTimeMillis() override { SetTimeFunctionForTesting([]() { return mock_time_; });
return time_ * base::Time::kMillisecondsPerSecond;
} }
void IncreaseTime(double seconds) { time_ += seconds; } void IncreaseTime(double seconds) { mock_time_ += seconds; }
private: private:
double time_; static double mock_time_;
}; };
double DefaultPlatformWithMockTime::mock_time_ = 0.0;
} // namespace } // namespace
...@@ -61,6 +63,24 @@ TEST(DefaultPlatformTest, PumpMessageLoop) { ...@@ -61,6 +63,24 @@ TEST(DefaultPlatformTest, PumpMessageLoop) {
EXPECT_FALSE(platform.PumpMessageLoop(isolate)); 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) { TEST(DefaultPlatformTest, PumpMessageLoopDelayed) {
InSequence s; InSequence s;
...@@ -91,6 +111,36 @@ TEST(DefaultPlatformTest, PumpMessageLoopDelayed) { ...@@ -91,6 +111,36 @@ TEST(DefaultPlatformTest, PumpMessageLoopDelayed) {
EXPECT_TRUE(platform.PumpMessageLoop(isolate)); 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) { TEST(DefaultPlatformTest, PumpMessageLoopNoStarvation) {
InSequence s; InSequence s;
...@@ -153,6 +203,24 @@ TEST(DefaultPlatformTest, RunIdleTasks) { ...@@ -153,6 +203,24 @@ TEST(DefaultPlatformTest, RunIdleTasks) {
platform.RunIdleTasks(isolate, 42.0); 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) { TEST(DefaultPlatformTest, PendingIdleTasksAreDestroyedOnShutdown) {
InSequence s; InSequence s;
...@@ -167,6 +235,88 @@ TEST(DefaultPlatformTest, PendingIdleTasksAreDestroyedOnShutdown) { ...@@ -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 default_platform_unittest
} // namespace platform } // namespace platform
} // namespace v8 } // namespace v8
...@@ -38,9 +38,10 @@ class TaskQueueThread final : public base::Thread { ...@@ -38,9 +38,10 @@ class TaskQueueThread final : public base::Thread {
TEST(TaskQueueTest, Basic) { TEST(TaskQueueTest, Basic) {
TaskQueue queue; TaskQueue queue;
MockTask task; std::unique_ptr<Task> task(new MockTask());
queue.Append(&task); Task* ptr = task.get();
EXPECT_EQ(&task, queue.GetNext()); queue.Append(std::move(task));
EXPECT_EQ(ptr, queue.GetNext().get());
queue.Terminate(); queue.Terminate();
EXPECT_THAT(queue.GetNext(), IsNull()); EXPECT_THAT(queue.GetNext(), IsNull());
} }
......
...@@ -32,10 +32,10 @@ TEST(WorkerThreadTest, PostSingleTask) { ...@@ -32,10 +32,10 @@ TEST(WorkerThreadTest, PostSingleTask) {
WorkerThread thread2(&queue); WorkerThread thread2(&queue);
InSequence s; InSequence s;
StrictMock<MockTask>* task = new StrictMock<MockTask>; std::unique_ptr<StrictMock<MockTask>> task(new StrictMock<MockTask>);
EXPECT_CALL(*task, Run()); EXPECT_CALL(*task.get(), Run());
EXPECT_CALL(*task, Die()); EXPECT_CALL(*task.get(), Die());
queue.Append(task); queue.Append(std::move(task));
// The next call should not time out. // The next call should not time out.
queue.BlockUntilQueueEmptyForTesting(); queue.BlockUntilQueueEmptyForTesting();
...@@ -50,10 +50,10 @@ TEST(WorkerThreadTest, Basic) { ...@@ -50,10 +50,10 @@ TEST(WorkerThreadTest, Basic) {
TaskQueue queue; TaskQueue queue;
for (size_t i = 0; i < kNumTasks; ++i) { for (size_t i = 0; i < kNumTasks; ++i) {
InSequence s; InSequence s;
StrictMock<MockTask>* task = new StrictMock<MockTask>; std::unique_ptr<StrictMock<MockTask>> task(new StrictMock<MockTask>);
EXPECT_CALL(*task, Run()); EXPECT_CALL(*task.get(), Run());
EXPECT_CALL(*task, Die()); EXPECT_CALL(*task.get(), Die());
queue.Append(task); queue.Append(std::move(task));
} }
WorkerThread thread1(&queue); WorkerThread thread1(&queue);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment