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

Reland "[platform] Implement TaskRunners in the DefaultPlatform"

There was a data race in the access of the foreground_task_runner_map_.
I protect each access to foreground_task_runner_map_ with a lock now.

Original change's description:
> [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}

Change-Id: Iddccdb07bde1a799815ec6ed6af37082df4987c7
Reviewed-on: https://chromium-review.googlesource.com/770970
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49379}
parent 271ffdb0
...@@ -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,47 @@ ...@@ -18,38 +18,47 @@
#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();
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::shared_ptr<TaskRunner> GetForegroundTaskRunner(
v8::Isolate* isolate) override;
std::shared_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 +74,19 @@ class V8_PLATFORM_EXPORT DefaultPlatform : public NON_EXPORTED_BASE(Platform) { ...@@ -65,34 +74,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::shared_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::shared_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::shared_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::shared_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::shared_ptr<TaskRunner> taskrunner =
platform.GetBackgroundTaskRunner(isolate);
EXPECT_FALSE(taskrunner->IdleTasksEnabled());
}
TEST(DefaultPlatformTest, PostTaskAfterPlatformTermination) {
std::shared_ptr<TaskRunner> foreground_taskrunner;
std::shared_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