summaryrefslogtreecommitdiffstats
path: root/content/renderer/scheduler
diff options
context:
space:
mode:
authorskyostil <skyostil@chromium.org>2015-02-24 11:35:49 -0800
committerCommit bot <commit-bot@chromium.org>2015-02-24 19:36:41 +0000
commit0da8dc886fad453b0cf4f1c718da8d44e132b618 (patch)
treedddc676f02460e27663dea6d33a41341ee2f7512 /content/renderer/scheduler
parent8ec6aaa574230d326a602f1cd719cfff227e4279 (diff)
downloadchromium_src-0da8dc886fad453b0cf4f1c718da8d44e132b618.zip
chromium_src-0da8dc886fad453b0cf4f1c718da8d44e132b618.tar.gz
chromium_src-0da8dc886fad453b0cf4f1c718da8d44e132b618.tar.bz2
scheduler: Implement task observers
This patch adds a way for scheduler clients to get notified of task execution, much like with base::MessageLoop. This is needed to allow the execution of microtask checkpoints[1] between tasks by Blink while letting the scheduler use a >1 work batch size. This patch also refactors the various WebThreadImpls to reduce code duplication removes the deprecated postTask entrypoints that don't take the posting location as a parameter. BUG=450977,444764,458990 [1] https://html.spec.whatwg.org/multipage/webappapis.html#processing-model-9 Committed: https://crrev.com/ab211a0e344b431ca58c74f7815b15b28f7fb01a Cr-Commit-Position: refs/heads/master@{#316450} Review URL: https://codereview.chromium.org/922733002 Cr-Commit-Position: refs/heads/master@{#317852}
Diffstat (limited to 'content/renderer/scheduler')
-rw-r--r--content/renderer/scheduler/null_renderer_scheduler.cc10
-rw-r--r--content/renderer/scheduler/null_renderer_scheduler.h3
-rw-r--r--content/renderer/scheduler/renderer_scheduler.h8
-rw-r--r--content/renderer/scheduler/renderer_scheduler_impl.cc19
-rw-r--r--content/renderer/scheduler/renderer_scheduler_impl.h4
-rw-r--r--content/renderer/scheduler/task_queue_manager.cc34
-rw-r--r--content/renderer/scheduler/task_queue_manager.h17
-rw-r--r--content/renderer/scheduler/task_queue_manager_unittest.cc74
-rw-r--r--content/renderer/scheduler/webthread_impl_for_scheduler.cc45
-rw-r--r--content/renderer/scheduler/webthread_impl_for_scheduler.h41
-rw-r--r--content/renderer/scheduler/webthread_impl_for_scheduler_unittest.cc197
11 files changed, 447 insertions, 5 deletions
diff --git a/content/renderer/scheduler/null_renderer_scheduler.cc b/content/renderer/scheduler/null_renderer_scheduler.cc
index 7dd3d52..7e5d2db 100644
--- a/content/renderer/scheduler/null_renderer_scheduler.cc
+++ b/content/renderer/scheduler/null_renderer_scheduler.cc
@@ -89,6 +89,16 @@ bool NullRendererScheduler::ShouldYieldForHighPriorityWork() {
return false;
}
+void NullRendererScheduler::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ base::MessageLoop::current()->AddTaskObserver(task_observer);
+}
+
+void NullRendererScheduler::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ base::MessageLoop::current()->RemoveTaskObserver(task_observer);
+}
+
void NullRendererScheduler::Shutdown() {
}
diff --git a/content/renderer/scheduler/null_renderer_scheduler.h b/content/renderer/scheduler/null_renderer_scheduler.h
index eb2faec..d983e72 100644
--- a/content/renderer/scheduler/null_renderer_scheduler.h
+++ b/content/renderer/scheduler/null_renderer_scheduler.h
@@ -27,6 +27,9 @@ class NullRendererScheduler : public RendererScheduler {
void DidAnimateForInputOnCompositorThread() override;
bool IsHighPriorityWorkAnticipated() override;
bool ShouldYieldForHighPriorityWork() override;
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override;
+ void RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) override;
void Shutdown() override;
private:
diff --git a/content/renderer/scheduler/renderer_scheduler.h b/content/renderer/scheduler/renderer_scheduler.h
index 0a57339..7d2c39e 100644
--- a/content/renderer/scheduler/renderer_scheduler.h
+++ b/content/renderer/scheduler/renderer_scheduler.h
@@ -70,6 +70,14 @@ class CONTENT_EXPORT RendererScheduler {
// Must be called from the main thread.
virtual bool ShouldYieldForHighPriorityWork() = 0;
+ // Adds or removes a task observer from the scheduler. The observer will be
+ // notified before and after every executed task. These functions can only be
+ // called on the main thread.
+ virtual void AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) = 0;
+ virtual void RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) = 0;
+
// Shuts down the scheduler by dropping any remaining pending work in the work
// queues. After this call any work posted to the task runners will be
// silently dropped.
diff --git a/content/renderer/scheduler/renderer_scheduler_impl.cc b/content/renderer/scheduler/renderer_scheduler_impl.cc
index 67f3072..4c3d1a3 100644
--- a/content/renderer/scheduler/renderer_scheduler_impl.cc
+++ b/content/renderer/scheduler/renderer_scheduler_impl.cc
@@ -350,6 +350,11 @@ void RendererSchedulerImpl::SetTimeSourceForTesting(
task_queue_manager_->SetTimeSourceForTesting(time_source);
}
+void RendererSchedulerImpl::SetWorkBatchSizeForTesting(size_t work_batch_size) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ task_queue_manager_->SetWorkBatchSize(work_batch_size);
+}
+
base::TimeTicks RendererSchedulerImpl::Now() const {
return UNLIKELY(time_source_) ? time_source_->Now() : base::TimeTicks::Now();
}
@@ -481,4 +486,18 @@ RendererSchedulerImpl::ComputeNewInputStreamState(
return INPUT_ACTIVE;
}
+void RendererSchedulerImpl::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (task_queue_manager_)
+ task_queue_manager_->AddTaskObserver(task_observer);
+}
+
+void RendererSchedulerImpl::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ if (task_queue_manager_)
+ task_queue_manager_->RemoveTaskObserver(task_observer);
+}
+
} // namespace content
diff --git a/content/renderer/scheduler/renderer_scheduler_impl.h b/content/renderer/scheduler/renderer_scheduler_impl.h
index f188f8b..67f5505 100644
--- a/content/renderer/scheduler/renderer_scheduler_impl.h
+++ b/content/renderer/scheduler/renderer_scheduler_impl.h
@@ -43,9 +43,13 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler {
void DidAnimateForInputOnCompositorThread() override;
bool IsHighPriorityWorkAnticipated() override;
bool ShouldYieldForHighPriorityWork() override;
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override;
+ void RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) override;
void Shutdown() override;
void SetTimeSourceForTesting(scoped_refptr<cc::TestNowSource> time_source);
+ void SetWorkBatchSizeForTesting(size_t work_batch_size);
private:
friend class RendererSchedulerImplTest;
diff --git a/content/renderer/scheduler/task_queue_manager.cc b/content/renderer/scheduler/task_queue_manager.cc
index 38a7192..90f89fe 100644
--- a/content/renderer/scheduler/task_queue_manager.cc
+++ b/content/renderer/scheduler/task_queue_manager.cc
@@ -419,6 +419,8 @@ void TaskQueueManager::DoWork(bool posted_from_main_thread) {
if (!UpdateWorkQueues(&next_pending_delayed_task, BEFORE_WAKEUP_EVENT_TYPE))
return;
+ base::PendingTask previous_task((tracked_objects::Location()),
+ (base::Closure()));
for (int i = 0; i < work_batch_size_; i++) {
// Interrupt the work batch if we should run the next delayed task.
if (i > 0 && next_pending_delayed_task.ToInternalValue() != kMaxTimeTicks &&
@@ -431,7 +433,7 @@ void TaskQueueManager::DoWork(bool posted_from_main_thread) {
// Note that this function won't post another call to DoWork if one is
// already pending, so it is safe to call it in a loop.
MaybePostDoWorkOnMainRunner();
- ProcessTaskFromWorkQueue(queue_index);
+ ProcessTaskFromWorkQueue(queue_index, i > 0, &previous_task);
if (!UpdateWorkQueues(&next_pending_delayed_task, AFTER_WAKEUP_EVENT_TYPE))
return;
@@ -451,7 +453,10 @@ void TaskQueueManager::DidQueueTask(base::PendingTask* pending_task) {
task_annotator_.DidQueueTask("TaskQueueManager::PostTask", *pending_task);
}
-void TaskQueueManager::ProcessTaskFromWorkQueue(size_t queue_index) {
+void TaskQueueManager::ProcessTaskFromWorkQueue(
+ size_t queue_index,
+ bool has_previous_task,
+ base::PendingTask* previous_task) {
DCHECK(main_thread_checker_.CalledOnValidThread());
internal::TaskQueue* queue = Queue(queue_index);
base::PendingTask pending_task = queue->TakeTaskFromWorkQueue();
@@ -461,8 +466,19 @@ void TaskQueueManager::ProcessTaskFromWorkQueue(size_t queue_index) {
main_task_runner_->PostNonNestableTask(pending_task.posted_from,
pending_task.task);
} else {
+ // Suppress "will" task observer notifications for the first and "did"
+ // notifications for the last task in the batch to avoid duplicate
+ // notifications.
+ if (has_previous_task) {
+ FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, task_observers_,
+ DidProcessTask(*previous_task));
+ FOR_EACH_OBSERVER(base::MessageLoop::TaskObserver, task_observers_,
+ WillProcessTask(pending_task));
+ }
task_annotator_.RunTask("TaskQueueManager::PostTask",
"TaskQueueManager::RunTask", pending_task);
+ pending_task.task.Reset();
+ *previous_task = pending_task;
}
}
@@ -490,6 +506,20 @@ void TaskQueueManager::SetWorkBatchSize(int work_batch_size) {
work_batch_size_ = work_batch_size;
}
+void TaskQueueManager::AddTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ base::MessageLoop::current()->AddTaskObserver(task_observer);
+ task_observers_.AddObserver(task_observer);
+}
+
+void TaskQueueManager::RemoveTaskObserver(
+ base::MessageLoop::TaskObserver* task_observer) {
+ DCHECK(main_thread_checker_.CalledOnValidThread());
+ base::MessageLoop::current()->RemoveTaskObserver(task_observer);
+ task_observers_.RemoveObserver(task_observer);
+}
+
void TaskQueueManager::SetTimeSourceForTesting(
scoped_refptr<cc::TestNowSource> time_source) {
DCHECK(main_thread_checker_.CalledOnValidThread());
diff --git a/content/renderer/scheduler/task_queue_manager.h b/content/renderer/scheduler/task_queue_manager.h
index 0b1bcbb..9e475ff 100644
--- a/content/renderer/scheduler/task_queue_manager.h
+++ b/content/renderer/scheduler/task_queue_manager.h
@@ -9,6 +9,7 @@
#include "base/debug/task_annotator.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
#include "base/pending_task.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
@@ -105,6 +106,11 @@ class CONTENT_EXPORT TaskQueueManager {
// tasks posted to the main loop. The batch size is 1 by default.
void SetWorkBatchSize(int work_batch_size);
+ // These functions can only be called on the same thread that the task queue
+ // manager executes its tasks on.
+ void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer);
+ void RemoveTaskObserver(base::MessageLoop::TaskObserver* task_observer);
+
void SetTimeSourceForTesting(scoped_refptr<cc::TestNowSource> time_source);
private:
@@ -141,9 +147,12 @@ class CONTENT_EXPORT TaskQueueManager {
bool SelectWorkQueueToService(size_t* out_queue_index);
// Runs a single nestable task from the work queue designated by
- // |queue_index|. Non-nestable task are reposted on the run loop.
- // The queue must not be empty.
- void ProcessTaskFromWorkQueue(size_t queue_index);
+ // |queue_index|. If |has_previous_task| is true, |previous_task| should
+ // contain the previous task in this work batch. Non-nestable task are
+ // reposted on the run loop. The queue must not be empty.
+ void ProcessTaskFromWorkQueue(size_t queue_index,
+ bool has_previous_task,
+ base::PendingTask* previous_task);
bool RunsTasksOnCurrentThread() const;
bool PostDelayedTask(const tracked_objects::Location& from_here,
@@ -177,6 +186,8 @@ class CONTENT_EXPORT TaskQueueManager {
scoped_refptr<cc::TestNowSource> time_source_;
+ ObserverList<base::MessageLoop::TaskObserver> task_observers_;
+
base::WeakPtrFactory<TaskQueueManager> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(TaskQueueManager);
diff --git a/content/renderer/scheduler/task_queue_manager_unittest.cc b/content/renderer/scheduler/task_queue_manager_unittest.cc
index ef5bd53..21c7525 100644
--- a/content/renderer/scheduler/task_queue_manager_unittest.cc
+++ b/content/renderer/scheduler/task_queue_manager_unittest.cc
@@ -11,6 +11,7 @@
#include "testing/gmock/include/gmock/gmock.h"
using testing::ElementsAre;
+using testing::_;
namespace content {
namespace {
@@ -655,6 +656,79 @@ TEST_F(TaskQueueManagerTest, AutoPumpOnWakeupTriggeredByManuallyPumpedQueue) {
EXPECT_THAT(run_order, ElementsAre(2, 1));
}
+class MockTaskObserver : public base::MessageLoop::TaskObserver {
+ public:
+ MOCK_METHOD1(DidProcessTask, void(const base::PendingTask& task));
+ MOCK_METHOD1(WillProcessTask, void(const base::PendingTask& task));
+};
+
+TEST_F(TaskQueueManagerTest, TaskObserverAdding) {
+ InitializeWithRealMessageLoop(1u);
+ MockTaskObserver observer;
+
+ manager_->SetWorkBatchSize(2);
+ manager_->AddTaskObserver(&observer);
+
+ std::vector<int> run_order;
+ scoped_refptr<base::SingleThreadTaskRunner> runner =
+ manager_->TaskRunnerForQueue(0);
+
+ runner->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order));
+ runner->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order));
+
+ selector_->AppendQueueToService(0);
+ selector_->AppendQueueToService(0);
+
+ // Two pairs of callbacks for the tasks above plus another one for the
+ // DoWork() posted by the task queue manager.
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(3);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(3);
+ message_loop_->RunUntilIdle();
+}
+
+TEST_F(TaskQueueManagerTest, TaskObserverRemoving) {
+ InitializeWithRealMessageLoop(1u);
+ MockTaskObserver observer;
+ manager_->SetWorkBatchSize(2);
+ manager_->AddTaskObserver(&observer);
+ manager_->RemoveTaskObserver(&observer);
+
+ std::vector<int> run_order;
+ scoped_refptr<base::SingleThreadTaskRunner> runner =
+ manager_->TaskRunnerForQueue(0);
+
+ runner->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order));
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(0);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
+
+ selector_->AppendQueueToService(0);
+ message_loop_->RunUntilIdle();
+}
+
+void RemoveObserverTask(TaskQueueManager* manager,
+ base::MessageLoop::TaskObserver* observer) {
+ manager->RemoveTaskObserver(observer);
+}
+
+TEST_F(TaskQueueManagerTest, TaskObserverRemovingInsideTask) {
+ InitializeWithRealMessageLoop(1u);
+ MockTaskObserver observer;
+ manager_->SetWorkBatchSize(3);
+ manager_->AddTaskObserver(&observer);
+
+ scoped_refptr<base::SingleThreadTaskRunner> runner =
+ manager_->TaskRunnerForQueue(0);
+ runner->PostTask(FROM_HERE,
+ base::Bind(&RemoveObserverTask, manager_.get(), &observer));
+
+ selector_->AppendQueueToService(0);
+
+ EXPECT_CALL(observer, WillProcessTask(_)).Times(1);
+ EXPECT_CALL(observer, DidProcessTask(_)).Times(0);
+ message_loop_->RunUntilIdle();
+}
+
} // namespace
} // namespace content
diff --git a/content/renderer/scheduler/webthread_impl_for_scheduler.cc b/content/renderer/scheduler/webthread_impl_for_scheduler.cc
new file mode 100644
index 0000000..b328142
--- /dev/null
+++ b/content/renderer/scheduler/webthread_impl_for_scheduler.cc
@@ -0,0 +1,45 @@
+// Copyright 2015 The Chromium 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 "content/renderer/scheduler/webthread_impl_for_scheduler.h"
+
+#include "content/renderer/scheduler/renderer_scheduler.h"
+#include "third_party/WebKit/public/platform/WebTraceLocation.h"
+
+namespace content {
+
+WebThreadImplForScheduler::WebThreadImplForScheduler(
+ RendererScheduler* scheduler)
+ : task_runner_(scheduler->DefaultTaskRunner()),
+ scheduler_(scheduler),
+ thread_id_(base::PlatformThread::CurrentId()) {
+}
+
+WebThreadImplForScheduler::~WebThreadImplForScheduler() {
+}
+
+blink::PlatformThreadId WebThreadImplForScheduler::threadId() const {
+ return thread_id_;
+}
+
+base::MessageLoop* WebThreadImplForScheduler::MessageLoop() const {
+ DCHECK(isCurrentThread());
+ return base::MessageLoop::current();
+}
+
+base::SingleThreadTaskRunner* WebThreadImplForScheduler::TaskRunner() const {
+ return task_runner_.get();
+}
+
+void WebThreadImplForScheduler::AddTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) {
+ scheduler_->AddTaskObserver(observer);
+}
+
+void WebThreadImplForScheduler::RemoveTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) {
+ scheduler_->RemoveTaskObserver(observer);
+}
+
+} // namespace content
diff --git a/content/renderer/scheduler/webthread_impl_for_scheduler.h b/content/renderer/scheduler/webthread_impl_for_scheduler.h
new file mode 100644
index 0000000..36bd2e2
--- /dev/null
+++ b/content/renderer/scheduler/webthread_impl_for_scheduler.h
@@ -0,0 +1,41 @@
+// Copyright 2015 The Chromium 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 CONTENT_RENDERER_SCHEDULER_WEBTHREAD_IMPL_FOR_SCHEDULER_H_
+#define CONTENT_RENDERER_SCHEDULER_WEBTHREAD_IMPL_FOR_SCHEDULER_H_
+
+#include "content/child/webthread_impl.h"
+
+#include "base/containers/scoped_ptr_hash_map.h"
+
+namespace content {
+
+class RendererScheduler;
+
+class CONTENT_EXPORT WebThreadImplForScheduler : public WebThreadBase {
+ public:
+ explicit WebThreadImplForScheduler(RendererScheduler* scheduler);
+ virtual ~WebThreadImplForScheduler();
+
+ // blink::WebThread implementation.
+ blink::PlatformThreadId threadId() const override;
+
+ // WebThreadBase implementation.
+ base::SingleThreadTaskRunner* TaskRunner() const override;
+
+ private:
+ base::MessageLoop* MessageLoop() const override;
+ void AddTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) override;
+ void RemoveTaskObserverInternal(
+ base::MessageLoop::TaskObserver* observer) override;
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ RendererScheduler* scheduler_; // Not owned.
+ blink::PlatformThreadId thread_id_;
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_SCHEDULER_WEBTHREAD_IMPL_FOR_SCHEDULER_H_
diff --git a/content/renderer/scheduler/webthread_impl_for_scheduler_unittest.cc b/content/renderer/scheduler/webthread_impl_for_scheduler_unittest.cc
new file mode 100644
index 0000000..68d3197
--- /dev/null
+++ b/content/renderer/scheduler/webthread_impl_for_scheduler_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright 2015 The Chromium 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 "content/renderer/scheduler/webthread_impl_for_scheduler.h"
+
+#include "base/run_loop.h"
+#include "content/renderer/scheduler/renderer_scheduler_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebTraceLocation.h"
+
+namespace content {
+namespace {
+
+const int kWorkBatchSize = 2;
+
+class MockTask : public blink::WebThread::Task {
+ public:
+ MOCK_METHOD0(run, void());
+};
+
+class MockTaskObserver : public blink::WebThread::TaskObserver {
+ public:
+ MOCK_METHOD0(willProcessTask, void());
+ MOCK_METHOD0(didProcessTask, void());
+};
+} // namespace
+
+class WebThreadImplForSchedulerTest : public testing::Test {
+ public:
+ WebThreadImplForSchedulerTest()
+ : scheduler_(message_loop_.task_runner()),
+ default_task_runner_(scheduler_.DefaultTaskRunner()),
+ thread_(&scheduler_) {}
+
+ ~WebThreadImplForSchedulerTest() override {}
+
+ protected:
+ void EatDefaultTask(MockTaskObserver* observer) {
+ // The scheduler posts one extra DoWork() task automatically.
+ EXPECT_CALL(*observer, willProcessTask());
+ EXPECT_CALL(*observer, didProcessTask());
+ }
+
+ base::MessageLoop message_loop_;
+ RendererSchedulerImpl scheduler_;
+ scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
+ WebThreadImplForScheduler thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebThreadImplForSchedulerTest);
+};
+
+TEST_F(WebThreadImplForSchedulerTest, TestTaskObserver) {
+ MockTaskObserver observer;
+ thread_.addTaskObserver(&observer);
+ scoped_ptr<MockTask> task(new MockTask());
+
+ {
+ testing::InSequence sequence;
+ EXPECT_CALL(observer, willProcessTask());
+ EXPECT_CALL(*task, run());
+ EXPECT_CALL(observer, didProcessTask());
+
+ EatDefaultTask(&observer);
+ }
+
+ thread_.postTask(blink::WebTraceLocation(), task.release());
+ message_loop_.RunUntilIdle();
+ thread_.removeTaskObserver(&observer);
+}
+
+TEST_F(WebThreadImplForSchedulerTest, TestWorkBatchWithOneTask) {
+ MockTaskObserver observer;
+ thread_.addTaskObserver(&observer);
+ scoped_ptr<MockTask> task(new MockTask());
+
+ scheduler_.SetWorkBatchSizeForTesting(kWorkBatchSize);
+ {
+ testing::InSequence sequence;
+ EXPECT_CALL(observer, willProcessTask());
+ EXPECT_CALL(*task, run());
+ EXPECT_CALL(observer, didProcessTask());
+
+ EatDefaultTask(&observer);
+ }
+
+ thread_.postTask(blink::WebTraceLocation(), task.release());
+ message_loop_.RunUntilIdle();
+ thread_.removeTaskObserver(&observer);
+}
+
+TEST_F(WebThreadImplForSchedulerTest, TestWorkBatchWithTwoTasks) {
+ MockTaskObserver observer;
+ thread_.addTaskObserver(&observer);
+ scoped_ptr<MockTask> task1(new MockTask());
+ scoped_ptr<MockTask> task2(new MockTask());
+
+ scheduler_.SetWorkBatchSizeForTesting(kWorkBatchSize);
+ {
+ testing::InSequence sequence;
+ EXPECT_CALL(observer, willProcessTask());
+ EXPECT_CALL(*task1, run());
+ EXPECT_CALL(observer, didProcessTask());
+
+ EXPECT_CALL(observer, willProcessTask());
+ EXPECT_CALL(*task2, run());
+ EXPECT_CALL(observer, didProcessTask());
+
+ EatDefaultTask(&observer);
+ }
+
+ thread_.postTask(blink::WebTraceLocation(), task1.release());
+ thread_.postTask(blink::WebTraceLocation(), task2.release());
+ message_loop_.RunUntilIdle();
+ thread_.removeTaskObserver(&observer);
+}
+
+TEST_F(WebThreadImplForSchedulerTest, TestWorkBatchWithThreeTasks) {
+ MockTaskObserver observer;
+ thread_.addTaskObserver(&observer);
+ scoped_ptr<MockTask> task1(new MockTask());
+ scoped_ptr<MockTask> task2(new MockTask());
+ scoped_ptr<MockTask> task3(new MockTask());
+
+ scheduler_.SetWorkBatchSizeForTesting(kWorkBatchSize);
+ {
+ testing::InSequence sequence;
+ EXPECT_CALL(observer, willProcessTask());
+ EXPECT_CALL(*task1, run());
+ EXPECT_CALL(observer, didProcessTask());
+
+ EXPECT_CALL(observer, willProcessTask());
+ EXPECT_CALL(*task2, run());
+ EXPECT_CALL(observer, didProcessTask());
+
+ EXPECT_CALL(observer, willProcessTask());
+ EXPECT_CALL(*task3, run());
+ EXPECT_CALL(observer, didProcessTask());
+
+ EatDefaultTask(&observer);
+ }
+
+ thread_.postTask(blink::WebTraceLocation(), task1.release());
+ thread_.postTask(blink::WebTraceLocation(), task2.release());
+ thread_.postTask(blink::WebTraceLocation(), task3.release());
+ message_loop_.RunUntilIdle();
+ thread_.removeTaskObserver(&observer);
+}
+
+class ExitRunLoopTask : public blink::WebThread::Task {
+ public:
+ ExitRunLoopTask(base::RunLoop* run_loop) : run_loop_(run_loop) {}
+
+ virtual void run() { run_loop_->Quit(); }
+
+ private:
+ base::RunLoop* run_loop_;
+};
+
+void EnterRunLoop(base::MessageLoop* message_loop, blink::WebThread* thread) {
+ // Note: WebThreads do not support nested run loops, which is why we use a
+ // run loop directly.
+ base::RunLoop run_loop;
+ thread->postTask(blink::WebTraceLocation(), new ExitRunLoopTask(&run_loop));
+ message_loop->SetNestableTasksAllowed(true);
+ run_loop.Run();
+}
+
+TEST_F(WebThreadImplForSchedulerTest, TestNestedRunLoop) {
+ MockTaskObserver observer;
+ thread_.addTaskObserver(&observer);
+
+ {
+ testing::InSequence sequence;
+
+ // One callback for EnterRunLoop.
+ EXPECT_CALL(observer, willProcessTask());
+
+ // A pair for ExitRunLoopTask.
+ EXPECT_CALL(observer, willProcessTask());
+ EXPECT_CALL(observer, didProcessTask());
+
+ // A final callback for EnterRunLoop.
+ EXPECT_CALL(observer, didProcessTask());
+
+ EatDefaultTask(&observer);
+ }
+
+ message_loop_.PostTask(
+ FROM_HERE, base::Bind(&EnterRunLoop, base::Unretained(&message_loop_),
+ base::Unretained(&thread_)));
+ message_loop_.RunUntilIdle();
+ thread_.removeTaskObserver(&observer);
+}
+
+} // namespace content