diff options
Diffstat (limited to 'content/renderer')
13 files changed, 458 insertions, 5 deletions
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc index 3ac2bd0..cf1f817 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc @@ -52,6 +52,7 @@ #include "content/renderer/renderer_clipboard_delegate.h" #include "content/renderer/scheduler/renderer_scheduler.h" #include "content/renderer/scheduler/web_scheduler_impl.h" +#include "content/renderer/scheduler/webthread_impl_for_scheduler.h" #include "content/renderer/screen_orientation/screen_orientation_observer.h" #include "content/renderer/webclipboard_impl.h" #include "content/renderer/webgraphicscontext3d_provider_impl.h" @@ -228,6 +229,7 @@ RendererBlinkPlatformImpl::RendererBlinkPlatformImpl( RendererScheduler* renderer_scheduler) : BlinkPlatformImpl(renderer_scheduler->DefaultTaskRunner()), web_scheduler_(new WebSchedulerImpl(renderer_scheduler)), + main_thread_(new WebThreadImplForScheduler(renderer_scheduler)), clipboard_delegate_(new RendererClipboardDelegate), clipboard_(new WebClipboardImpl(clipboard_delegate_.get())), mime_registry_(new RendererBlinkPlatformImpl::MimeRegistry), @@ -263,6 +265,12 @@ blink::WebScheduler* RendererBlinkPlatformImpl::scheduler() { return web_scheduler_.get(); } +blink::WebThread* RendererBlinkPlatformImpl::currentThread() { + if (main_thread_->isCurrentThread()) + return main_thread_.get(); + return BlinkPlatformImpl::currentThread(); +} + blink::WebClipboard* RendererBlinkPlatformImpl::clipboard() { blink::WebClipboard* clipboard = GetContentClient()->renderer()->OverrideWebClipboard(); diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h index 97ab4ee..ae8cb46 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h @@ -50,6 +50,7 @@ class ThreadSafeSender; class WebClipboardImpl; class WebDatabaseObserverImpl; class WebFileSystemImpl; +class WebThreadImplForScheduler; class WebSchedulerImpl; class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { @@ -152,6 +153,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { virtual void vibrate(unsigned int milliseconds); virtual void cancelVibration(); virtual blink::WebScheduler* scheduler(); + virtual blink::WebThread* currentThread(); // Set the PlatformEventObserverBase in |platform_event_observers_| associated // with |type| to |observer|. If there was already an observer associated to @@ -202,6 +204,7 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { device::VibrationManagerPtr& GetConnectedVibrationManagerService(); scoped_ptr<WebSchedulerImpl> web_scheduler_; + scoped_ptr<WebThreadImplForScheduler> main_thread_; scoped_ptr<RendererClipboardDelegate> clipboard_delegate_; scoped_ptr<WebClipboardImpl> clipboard_; 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 |