Commit 1dc3ac96 authored by Shu-yu Guo's avatar Shu-yu Guo Committed by Commit Bot

[platform] Support nestable and non-nestable tasks in DefaultForegroundTaskRunner

Bug: v8:8179
Change-Id: I3a41243b971d499d50e35c4782bff5b8b012f434
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2013695
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65965}
parent dc3893e5
...@@ -47,9 +47,11 @@ V8_PLATFORM_EXPORT std::unique_ptr<v8::Platform> NewDefaultPlatform( ...@@ -47,9 +47,11 @@ V8_PLATFORM_EXPORT std::unique_ptr<v8::Platform> NewDefaultPlatform(
* Pumps the message loop for the given isolate. * Pumps the message loop for the given isolate.
* *
* The caller has to make sure that this is called from the right thread. * The caller has to make sure that this is called from the right thread.
* Returns true if a task was executed, and false otherwise. Unless requested * Returns true if a task was executed, and false otherwise. If the call to
* through the |behavior| parameter, this call does not block if no task is * PumpMessageLoop is nested within another call to PumpMessageLoop, only
* pending. The |platform| has to be created using |NewDefaultPlatform|. * nestable tasks may run. Otherwise, any task may run. Unless requested through
* the |behavior| parameter, this call does not block if no task is pending. The
* |platform| has to be created using |NewDefaultPlatform|.
*/ */
V8_PLATFORM_EXPORT bool PumpMessageLoop( V8_PLATFORM_EXPORT bool PumpMessageLoop(
v8::Platform* platform, v8::Isolate* isolate, v8::Platform* platform, v8::Isolate* isolate,
......
...@@ -10,6 +10,18 @@ ...@@ -10,6 +10,18 @@
namespace v8 { namespace v8 {
namespace platform { namespace platform {
DefaultForegroundTaskRunner::RunTaskScope::RunTaskScope(
std::shared_ptr<DefaultForegroundTaskRunner> task_runner)
: task_runner_(task_runner) {
DCHECK_GE(task_runner->nesting_depth_, 0);
task_runner->nesting_depth_++;
}
DefaultForegroundTaskRunner::RunTaskScope::~RunTaskScope() {
DCHECK_GT(task_runner_->nesting_depth_, 0);
task_runner_->nesting_depth_--;
}
DefaultForegroundTaskRunner::DefaultForegroundTaskRunner( DefaultForegroundTaskRunner::DefaultForegroundTaskRunner(
IdleTaskSupport idle_task_support, TimeFunction time_function) IdleTaskSupport idle_task_support, TimeFunction time_function)
: idle_task_support_(idle_task_support), time_function_(time_function) {} : idle_task_support_(idle_task_support), time_function_(time_function) {}
...@@ -19,21 +31,22 @@ void DefaultForegroundTaskRunner::Terminate() { ...@@ -19,21 +31,22 @@ void DefaultForegroundTaskRunner::Terminate() {
terminated_ = true; terminated_ = true;
// Drain the task queues. // Drain the task queues.
while (!task_queue_.empty()) task_queue_.pop(); while (!task_queue_.empty()) task_queue_.pop_front();
while (!delayed_task_queue_.empty()) delayed_task_queue_.pop(); while (!delayed_task_queue_.empty()) delayed_task_queue_.pop();
while (!idle_task_queue_.empty()) idle_task_queue_.pop(); while (!idle_task_queue_.empty()) idle_task_queue_.pop();
} }
void DefaultForegroundTaskRunner::PostTaskLocked(std::unique_ptr<Task> task, void DefaultForegroundTaskRunner::PostTaskLocked(std::unique_ptr<Task> task,
Nestability nestability,
const base::MutexGuard&) { const base::MutexGuard&) {
if (terminated_) return; if (terminated_) return;
task_queue_.push(std::move(task)); task_queue_.push_back(std::make_pair(nestability, std::move(task)));
event_loop_control_.NotifyOne(); event_loop_control_.NotifyOne();
} }
void DefaultForegroundTaskRunner::PostTask(std::unique_ptr<Task> task) { void DefaultForegroundTaskRunner::PostTask(std::unique_ptr<Task> task) {
base::MutexGuard guard(&lock_); base::MutexGuard guard(&lock_);
PostTaskLocked(std::move(task), guard); PostTaskLocked(std::move(task), kNestable, guard);
} }
double DefaultForegroundTaskRunner::MonotonicallyIncreasingTime() { double DefaultForegroundTaskRunner::MonotonicallyIncreasingTime() {
...@@ -62,31 +75,46 @@ bool DefaultForegroundTaskRunner::IdleTasksEnabled() { ...@@ -62,31 +75,46 @@ bool DefaultForegroundTaskRunner::IdleTasksEnabled() {
void DefaultForegroundTaskRunner::PostNonNestableTask( void DefaultForegroundTaskRunner::PostNonNestableTask(
std::unique_ptr<Task> task) { std::unique_ptr<Task> task) {
// Default platform does not nest tasks. base::MutexGuard guard(&lock_);
PostTask(std::move(task)); PostTaskLocked(std::move(task), kNonNestable, guard);
} }
bool DefaultForegroundTaskRunner::NonNestableTasksEnabled() const { bool DefaultForegroundTaskRunner::NonNestableTasksEnabled() const {
return true; return true;
} }
bool DefaultForegroundTaskRunner::HasPoppableTaskInQueue() const {
if (nesting_depth_ == 0) return !task_queue_.empty();
for (auto it = task_queue_.cbegin(); it != task_queue_.cend(); it++) {
if (it->first == kNestable) return true;
}
return false;
}
std::unique_ptr<Task> DefaultForegroundTaskRunner::PopTaskFromQueue( std::unique_ptr<Task> DefaultForegroundTaskRunner::PopTaskFromQueue(
MessageLoopBehavior wait_for_work) { MessageLoopBehavior wait_for_work) {
base::MutexGuard guard(&lock_); base::MutexGuard guard(&lock_);
// Move delayed tasks that hit their deadline to the main queue. // Move delayed tasks that hit their deadline to the main queue.
std::unique_ptr<Task> task = PopTaskFromDelayedQueueLocked(guard); std::unique_ptr<Task> task = PopTaskFromDelayedQueueLocked(guard);
while (task) { while (task) {
PostTaskLocked(std::move(task), guard); PostTaskLocked(std::move(task), kNestable, guard);
task = PopTaskFromDelayedQueueLocked(guard); task = PopTaskFromDelayedQueueLocked(guard);
} }
while (task_queue_.empty()) { while (!HasPoppableTaskInQueue()) {
if (wait_for_work == MessageLoopBehavior::kDoNotWait) return {}; if (wait_for_work == MessageLoopBehavior::kDoNotWait) return {};
WaitForTaskLocked(guard); WaitForTaskLocked(guard);
} }
task = std::move(task_queue_.front()); auto it = task_queue_.begin();
task_queue_.pop(); for (; it != task_queue_.end(); it++) {
// When the task queue is nested (i.e. popping a task from the queue from
// within a task), only nestable tasks may run. Otherwise, any task may run.
if (nesting_depth_ == 0 || it->first == kNestable) break;
}
DCHECK(it != task_queue_.end());
task = std::move(it->second);
task_queue_.erase(it);
return task; return task;
} }
......
...@@ -20,6 +20,18 @@ class V8_PLATFORM_EXPORT DefaultForegroundTaskRunner ...@@ -20,6 +20,18 @@ class V8_PLATFORM_EXPORT DefaultForegroundTaskRunner
: public NON_EXPORTED_BASE(TaskRunner) { : public NON_EXPORTED_BASE(TaskRunner) {
public: public:
using TimeFunction = double (*)(); using TimeFunction = double (*)();
class RunTaskScope {
public:
explicit RunTaskScope(
std::shared_ptr<DefaultForegroundTaskRunner> task_runner);
~RunTaskScope();
private:
RunTaskScope(const RunTaskScope&) = delete;
RunTaskScope& operator=(const RunTaskScope&) = delete;
std::shared_ptr<DefaultForegroundTaskRunner> task_runner_;
};
DefaultForegroundTaskRunner(IdleTaskSupport idle_task_support, DefaultForegroundTaskRunner(IdleTaskSupport idle_task_support,
TimeFunction time_function); TimeFunction time_function);
...@@ -46,18 +58,31 @@ class V8_PLATFORM_EXPORT DefaultForegroundTaskRunner ...@@ -46,18 +58,31 @@ class V8_PLATFORM_EXPORT DefaultForegroundTaskRunner
bool NonNestableTasksEnabled() const override; bool NonNestableTasksEnabled() const override;
private: private:
// The same as PostTask, but the lock is already held by the caller. The enum Nestability { kNestable, kNonNestable };
// {guard} parameter should make sure that the caller is holding the lock.
void PostTaskLocked(std::unique_ptr<Task> task, const base::MutexGuard&); // The same as PostTask or PostNonNestableTask, 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, Nestability nestability,
const base::MutexGuard&);
// A caller of this function has to hold {lock_}. The {guard} parameter should // A caller of this function has to hold {lock_}. The {guard} parameter should
// make sure that the caller is holding the lock. // make sure that the caller is holding the lock.
std::unique_ptr<Task> PopTaskFromDelayedQueueLocked(const base::MutexGuard&); std::unique_ptr<Task> PopTaskFromDelayedQueueLocked(const base::MutexGuard&);
// A non-nestable task is poppable only if the task runner is not nested,
// i.e. if a task is not being run from within a task. A nestable task is
// always poppable.
bool HasPoppableTaskInQueue() const;
bool terminated_ = false; bool terminated_ = false;
base::Mutex lock_; base::Mutex lock_;
base::ConditionVariable event_loop_control_; base::ConditionVariable event_loop_control_;
std::queue<std::unique_ptr<Task>> task_queue_; int nesting_depth_ = 0;
using TaskQueueEntry = std::pair<Nestability, std::unique_ptr<Task>>;
std::deque<TaskQueueEntry> task_queue_;
IdleTaskSupport idle_task_support_; IdleTaskSupport idle_task_support_;
std::queue<std::unique_ptr<IdleTask>> idle_task_queue_; std::queue<std::unique_ptr<IdleTask>> idle_task_queue_;
......
...@@ -141,6 +141,7 @@ bool DefaultPlatform::PumpMessageLoop(v8::Isolate* isolate, ...@@ -141,6 +141,7 @@ bool DefaultPlatform::PumpMessageLoop(v8::Isolate* isolate,
std::unique_ptr<Task> task = task_runner->PopTaskFromQueue(wait_for_work); std::unique_ptr<Task> task = task_runner->PopTaskFromQueue(wait_for_work);
if (!task) return failed_result; if (!task) return failed_result;
DefaultForegroundTaskRunner::RunTaskScope scope(task_runner);
task->Run(); task->Run();
return true; return true;
} }
...@@ -163,6 +164,7 @@ void DefaultPlatform::RunIdleTasks(v8::Isolate* isolate, ...@@ -163,6 +164,7 @@ void DefaultPlatform::RunIdleTasks(v8::Isolate* isolate,
while (deadline_in_seconds > MonotonicallyIncreasingTime()) { while (deadline_in_seconds > MonotonicallyIncreasingTime()) {
std::unique_ptr<IdleTask> task = task_runner->PopTaskFromIdleQueue(); std::unique_ptr<IdleTask> task = task_runner->PopTaskFromIdleQueue();
if (!task) return; if (!task) return;
DefaultForegroundTaskRunner::RunTaskScope scope(task_runner);
task->Run(deadline_in_seconds); task->Run(deadline_in_seconds);
} }
} }
......
...@@ -65,6 +65,9 @@ class PlatformTest : public ::testing::Test { ...@@ -65,6 +65,9 @@ class PlatformTest : public ::testing::Test {
void CallOnForegroundThread(Task* task) { void CallOnForegroundThread(Task* task) {
task_runner()->PostTask(std::unique_ptr<Task>(task)); task_runner()->PostTask(std::unique_ptr<Task>(task));
} }
void CallNonNestableOnForegroundThread(Task* task) {
task_runner()->PostNonNestableTask(std::unique_ptr<Task>(task));
}
void CallDelayedOnForegroundThread(Task* task, double delay_in_seconds) { void CallDelayedOnForegroundThread(Task* task, double delay_in_seconds) {
task_runner()->PostDelayedTask(std::unique_ptr<Task>(task), task_runner()->PostDelayedTask(std::unique_ptr<Task>(task),
delay_in_seconds); delay_in_seconds);
...@@ -113,6 +116,37 @@ TEST_F(DefaultPlatformTest, PumpMessageLoopWithTaskRunner) { ...@@ -113,6 +116,37 @@ TEST_F(DefaultPlatformTest, PumpMessageLoopWithTaskRunner) {
EXPECT_FALSE(PumpMessageLoop()); EXPECT_FALSE(PumpMessageLoop());
} }
TEST_F(DefaultPlatformTest, PumpMessageLoopNested) {
EXPECT_FALSE(PumpMessageLoop());
StrictMock<MockTask>* nestable_task1 = new StrictMock<MockTask>;
StrictMock<MockTask>* non_nestable_task2 = new StrictMock<MockTask>;
StrictMock<MockTask>* nestable_task3 = new StrictMock<MockTask>;
StrictMock<MockTask>* non_nestable_task4 = new StrictMock<MockTask>;
CallOnForegroundThread(nestable_task1);
CallNonNestableOnForegroundThread(non_nestable_task2);
CallOnForegroundThread(nestable_task3);
CallNonNestableOnForegroundThread(non_nestable_task4);
// Nestable tasks are FIFO; non-nestable tasks are FIFO. A task being
// non-nestable may cause it to be executed later, but not earlier.
EXPECT_CALL(*nestable_task1, Run).WillOnce([this]() {
EXPECT_TRUE(PumpMessageLoop());
});
EXPECT_CALL(*nestable_task3, Run());
EXPECT_CALL(*nestable_task3, Die());
EXPECT_CALL(*nestable_task1, Die());
EXPECT_TRUE(PumpMessageLoop());
EXPECT_CALL(*non_nestable_task2, Run());
EXPECT_CALL(*non_nestable_task2, Die());
EXPECT_TRUE(PumpMessageLoop());
EXPECT_CALL(*non_nestable_task4, Run());
EXPECT_CALL(*non_nestable_task4, Die());
EXPECT_TRUE(PumpMessageLoop());
EXPECT_FALSE(PumpMessageLoop());
}
TEST_F(DefaultPlatformTestWithMockTime, PumpMessageLoopDelayed) { TEST_F(DefaultPlatformTestWithMockTime, PumpMessageLoopDelayed) {
EXPECT_FALSE(PumpMessageLoop()); EXPECT_FALSE(PumpMessageLoop());
......
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