Commit d4cfef1e authored by Alexander Timin's avatar Alexander Timin Committed by Commit Bot

[scheduler] Simplify scheduling infrastructure for unit tests.

Do not create a full-blown blink scheduler with SequenceManager for
non-blink unittests (content_unittests and others).

Create a real scheduler with real message pump for blink's unittests.

Remove LazyThreadController class, which was, frankly, long overdue.

R=skyostil@chromium.org,haraken@chromium.org
TBR=gab@chromium.org
CC=​​hajimehoshi@chromium.org,alexclarke@chromium.org
BUG=495659

Change-Id: I93ce60f2badbc8c09675fd4e448dc7811dd5fb68
Reviewed-on: https://chromium-review.googlesource.com/c/1446651
Commit-Queue: Alexander Timin <altimin@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Reviewed-by: default avatarHajime Hoshi <hajimehoshi@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#629188}
parent 87780241
// Copyright 2017 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 "base/task/sequence_manager/test/lazy_thread_controller_for_test.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_current.h"
#include "base/time/default_tick_clock.h"
namespace base {
namespace sequence_manager {
LazyThreadControllerForTest::LazyThreadControllerForTest()
: ThreadControllerImpl(
MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated(),
nullptr,
DefaultTickClock::GetInstance()),
thread_ref_(PlatformThread::CurrentRef()) {
if (message_loop_base_)
task_runner_ = message_loop_base_->GetTaskRunner();
}
LazyThreadControllerForTest::~LazyThreadControllerForTest() = default;
void LazyThreadControllerForTest::EnsureMessageLoop() {
if (message_loop_base_)
return;
DCHECK(RunsTasksInCurrentSequence());
message_loop_base_ = MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated();
DCHECK(message_loop_base_);
task_runner_ = message_loop_base_->GetTaskRunner();
if (pending_observer_) {
RunLoop::AddNestingObserverOnCurrentThread(this);
pending_observer_ = false;
}
if (pending_default_task_runner_) {
ThreadControllerImpl::SetDefaultTaskRunner(pending_default_task_runner_);
pending_default_task_runner_ = nullptr;
}
}
bool LazyThreadControllerForTest::HasMessageLoop() {
return !!message_loop_base_;
}
void LazyThreadControllerForTest::AddNestingObserver(
RunLoop::NestingObserver* observer) {
// While |observer| _could_ be associated with the current thread regardless
// of the presence of a MessageLoop, the association is delayed until
// EnsureMessageLoop() is invoked. This works around a state issue where
// otherwise many tests fail because of the following sequence:
// 1) blink::scheduler::CreateRendererSchedulerForTests()
// -> SequenceManager::SequenceManager()
// -> LazySchedulerMessageLoopDelegateForTests::AddNestingObserver()
// 2) Any test framework with a MessageLoop member (and not caring
// about the blink scheduler) does:
// blink::scheduler::GetSingleThreadTaskRunnerForTesting()->PostTask(
// FROM_HERE, an_init_task_with_a_nested_loop);
// RunLoop.RunUntilIdle();
// 3) |a_task_with_a_nested_loop| triggers
// SequenceManager::OnBeginNestedLoop() which:
// a) flags any_thread().is_nested = true;
// b) posts a task to self, which triggers:
// LazySchedulerMessageLoopDelegateForTests::PostDelayedTask()
// 4) This self-task in turn triggers SequenceManager::DoWork()
// which expects to be the only one to trigger nested loops (doesn't
// support SequenceManager::OnBeginNestedLoop() being invoked before
// it kicks in), resulting in it hitting:
// DCHECK_EQ(any_thread().is_nested, delegate_->IsNested()); (1 vs 0).
// TODO(skyostil): fix this convolution as part of http://crbug.com/495659.
ThreadControllerImpl::nesting_observer_ = observer;
if (!HasMessageLoop()) {
DCHECK(!pending_observer_);
pending_observer_ = true;
return;
}
RunLoop::AddNestingObserverOnCurrentThread(this);
}
void LazyThreadControllerForTest::RemoveNestingObserver(
RunLoop::NestingObserver* observer) {
ThreadControllerImpl::nesting_observer_ = nullptr;
if (!HasMessageLoop()) {
DCHECK(pending_observer_);
pending_observer_ = false;
return;
}
// TODO(altimin): Refactor this to use STE::LifetimeObserver.
// We can't use message_loop_base_->IsBoundToCurrentThread as
// |message_loop_base_| might be deleted.
if (MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated() !=
message_loop_base_)
return;
RunLoop::RemoveNestingObserverOnCurrentThread(this);
}
bool LazyThreadControllerForTest::RunsTasksInCurrentSequence() {
return thread_ref_ == PlatformThread::CurrentRef();
}
void LazyThreadControllerForTest::ScheduleWork() {
EnsureMessageLoop();
ThreadControllerImpl::ScheduleWork();
}
void LazyThreadControllerForTest::SetNextDelayedDoWork(LazyNow* lazy_now,
TimeTicks run_time) {
EnsureMessageLoop();
ThreadControllerImpl::SetNextDelayedDoWork(lazy_now, run_time);
}
void LazyThreadControllerForTest::SetDefaultTaskRunner(
scoped_refptr<SingleThreadTaskRunner> task_runner) {
if (!HasMessageLoop()) {
pending_default_task_runner_ = task_runner;
return;
}
ThreadControllerImpl::SetDefaultTaskRunner(task_runner);
}
void LazyThreadControllerForTest::RestoreDefaultTaskRunner() {
pending_default_task_runner_ = nullptr;
// We can't use message_loop_base_->IsBoundToCurrentThread as
// |message_loop_base_| might be deleted.
if (HasMessageLoop() &&
MessageLoopCurrent::Get()->ToMessageLoopBaseDeprecated() ==
message_loop_base_) {
ThreadControllerImpl::RestoreDefaultTaskRunner();
}
}
} // namespace sequence_manager
} // namespace base
// Copyright 2017 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 BASE_TASK_SEQUENCE_MANAGER_TEST_LAZY_THREAD_CONTROLLER_FOR_TEST_H_
#define BASE_TASK_SEQUENCE_MANAGER_TEST_LAZY_THREAD_CONTROLLER_FOR_TEST_H_
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/task/sequence_manager/thread_controller_impl.h"
#include "base/threading/platform_thread.h"
namespace base {
namespace sequence_manager {
// This class connects the scheduler to a MessageLoop, but unlike
// ThreadControllerImpl it allows the message loop to be created lazily
// after the scheduler has been brought up. This is needed in testing scenarios
// where Blink is initialized before a MessageLoop has been created.
//
// TODO(skyostil): Fix the relevant test suites and remove this class
// (crbug.com/495659).
class LazyThreadControllerForTest : public internal::ThreadControllerImpl {
public:
LazyThreadControllerForTest();
~LazyThreadControllerForTest() override;
// internal::ThreadControllerImpl:
void AddNestingObserver(RunLoop::NestingObserver* observer) override;
void RemoveNestingObserver(RunLoop::NestingObserver* observer) override;
bool RunsTasksInCurrentSequence() override;
void ScheduleWork() override;
void SetNextDelayedDoWork(LazyNow* lazy_now, TimeTicks run_time) override;
void SetDefaultTaskRunner(
scoped_refptr<SingleThreadTaskRunner> task_runner) override;
void RestoreDefaultTaskRunner() override;
private:
bool HasMessageLoop();
void EnsureMessageLoop();
PlatformThreadRef thread_ref_;
bool pending_observer_ = false;
scoped_refptr<SingleThreadTaskRunner> pending_default_task_runner_;
DISALLOW_COPY_AND_ASSIGN(LazyThreadControllerForTest);
};
} // namespace sequence_manager
} // namespace base
#endif // BASE_TASK_SEQUENCE_MANAGER_TEST_LAZY_THREAD_CONTROLLER_FOR_TEST_H_
......@@ -30,8 +30,6 @@ static_library("test_support") {
"../memory/fake_memory_pressure_monitor.h",
"../task/sequence_manager/test/fake_task.cc",
"../task/sequence_manager/test/fake_task.h",
"../task/sequence_manager/test/lazy_thread_controller_for_test.cc",
"../task/sequence_manager/test/lazy_thread_controller_for_test.h",
"../task/sequence_manager/test/mock_time_domain.cc",
"../task/sequence_manager/test/mock_time_domain.h",
"../task/sequence_manager/test/sequence_manager_for_test.cc",
......
......@@ -9,8 +9,6 @@
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/strings/string_tokenizer.h"
#include "base/task/task_scheduler/task_scheduler.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_discardable_memory_allocator.h"
#include "build/build_config.h"
#include "content/common/content_switches_internal.h"
......@@ -38,12 +36,8 @@ namespace {
class TestEnvironment {
public:
TestEnvironment()
#if !defined(OS_ANDROID)
// On Android, Java pumps UI messages.
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI)
#endif
{
: blink_test_support_(
TestBlinkWebUnitTestSupport::SchedulerType::kRealScheduler) {
base::DiscardableMemoryAllocator::SetInstance(
&discardable_memory_allocator_);
}
......@@ -52,12 +46,9 @@ class TestEnvironment {
// This returns when both the main thread and the TaskSchedules queues are
// empty.
void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
// Must be instantiated after ScopedTaskEnvironment.
TestBlinkWebUnitTestSupport blink_test_support_;
TestContentClientInitializer content_initializer_;
base::TestDiscardableMemoryAllocator discardable_memory_allocator_;
......
......@@ -13,6 +13,8 @@
#include "base/path_service.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/task_scheduler/task_scheduler.h"
#include "base/test/null_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
......@@ -129,7 +131,8 @@ content::TestBlinkWebUnitTestSupport* g_test_platform = nullptr;
namespace content {
TestBlinkWebUnitTestSupport::TestBlinkWebUnitTestSupport()
TestBlinkWebUnitTestSupport::TestBlinkWebUnitTestSupport(
TestBlinkWebUnitTestSupport::SchedulerType scheduler_type)
: weak_factory_(this) {
#if defined(OS_MACOSX)
base::mac::ScopedNSAutoreleasePool autorelease_pool;
......@@ -147,7 +150,9 @@ TestBlinkWebUnitTestSupport::TestBlinkWebUnitTestSupport()
scoped_refptr<base::SingleThreadTaskRunner> dummy_task_runner;
std::unique_ptr<base::ThreadTaskRunnerHandle> dummy_task_runner_handle;
if (!base::ThreadTaskRunnerHandle::IsSet()) {
if (scheduler_type == SchedulerType::kMockScheduler) {
main_thread_scheduler_ =
blink::scheduler::CreateWebMainThreadSchedulerForTests();
// Dummy task runner is initialized here because the blink::Initialize
// creates IsolateHolder which needs the current task runner handle. There
// should be no task posted to this task runner. The message loop is not
......@@ -155,12 +160,17 @@ TestBlinkWebUnitTestSupport::TestBlinkWebUnitTestSupport()
// of message loops, and their types are not known upfront. Some tests also
// create their own thread bundles or message loops, and doing the same in
// TestBlinkWebUnitTestSupport would introduce a conflict.
dummy_task_runner = base::MakeRefCounted<DummyTaskRunner>();
dummy_task_runner = base::MakeRefCounted<base::NullTaskRunner>();
dummy_task_runner_handle.reset(
new base::ThreadTaskRunnerHandle(dummy_task_runner));
} else {
DCHECK_EQ(scheduler_type, SchedulerType::kRealScheduler);
main_thread_scheduler_ =
blink::scheduler::WebThreadScheduler::CreateMainThreadScheduler(
base::MessageLoop::CreateMessagePumpForType(
base::MessageLoop::TYPE_DEFAULT));
base::TaskScheduler::CreateAndStartWithDefaultParams("BlinkTestSupport");
}
main_thread_scheduler_ =
blink::scheduler::CreateWebMainThreadSchedulerForTests();
// Initialize mojo firstly to enable Blink initialization to use it.
InitializeMojo();
......
......@@ -30,7 +30,21 @@ class MockClipboardHost;
// An implementation of BlinkPlatformImpl for tests.
class TestBlinkWebUnitTestSupport : public BlinkPlatformImpl {
public:
TestBlinkWebUnitTestSupport();
enum class SchedulerType {
// Create a mock version of scheduling infrastructure, which just forwards
// all calls to the default task runner.
// All non-blink users (content_unittests etc) should call this method.
// Each test has to create base::test::ScopedTaskEnvironment manually.
kMockScheduler,
// Initialize blink platform with the real scheduler.
// Should be used only by webkit_unit_tests.
// Tests don't have to create base::test::ScopedTaskEnvironment, but should
// be careful not to leak any tasks to the other tests.
kRealScheduler,
};
explicit TestBlinkWebUnitTestSupport(
SchedulerType scheduler_type = SchedulerType::kMockScheduler);
~TestBlinkWebUnitTestSupport() override;
blink::WebBlobRegistry* GetBlobRegistry() override;
......
......@@ -18,6 +18,11 @@ namespace scheduler {
class WebThreadScheduler;
// Creates simple scheduling infrastructure for unit tests.
// It allows creation of FrameSchedulers and PageSchedulers, but doesn't provide
// any task running infrastructure, relying on the presence of
// ThreadTaskRunnerHandle::Get() instead, meaning that the users also have to
// create base::debug::ScopedTaskEnvironment.
std::unique_ptr<WebThreadScheduler> CreateWebMainThreadSchedulerForTests();
void RunIdleTasksForTesting(WebThreadScheduler* scheduler,
......
......@@ -7,21 +7,212 @@
#include <memory>
#include "base/single_thread_task_runner.h"
#include "base/task/sequence_manager/test/lazy_thread_controller_for_test.h"
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
#include "third_party/blink/renderer/platform/wtf/wtf.h"
namespace blink {
namespace scheduler {
namespace {
// TODO(altimin,yutak): Merge with SimpleThread in platform.cc.
class SimpleThread : public Thread {
public:
explicit SimpleThread(ThreadScheduler* scheduler) : scheduler_(scheduler) {}
~SimpleThread() override {}
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() const override {
return base::ThreadTaskRunnerHandle::Get();
}
ThreadScheduler* Scheduler() override { return scheduler_; }
void Init() override {}
bool IsCurrentThread() const { return WTF::IsMainThread(); }
private:
ThreadScheduler* scheduler_;
DISALLOW_COPY_AND_ASSIGN(SimpleThread);
};
class SimpleFrameScheduler : public FrameScheduler {
public:
explicit SimpleFrameScheduler(PageScheduler* page_scheduler)
: page_scheduler_(page_scheduler) {}
~SimpleFrameScheduler() override {}
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType) override {
DCHECK(WTF::IsMainThread());
return base::ThreadTaskRunnerHandle::Get();
}
PageScheduler* GetPageScheduler() const override { return page_scheduler_; }
void SetFrameVisible(bool) override {}
bool IsFrameVisible() const override { return true; }
bool IsPageVisible() const override { return true; }
void SetPaused(bool) override {}
void SetCrossOrigin(bool) override {}
bool IsCrossOrigin() const override { return false; }
void SetIsAdFrame() override {}
bool IsAdFrame() const override { return false; }
void TraceUrlChange(const String&) override {}
FrameType GetFrameType() const override { return FrameType::kMainFrame; }
WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
const String& name,
WebScopedVirtualTimePauser::VirtualTaskDuration) override {
return WebScopedVirtualTimePauser();
}
void DidStartProvisionalLoad(bool is_main_frame) override {}
void DidCommitProvisionalLoad(bool, bool, bool) override {}
void OnFirstMeaningfulPaint() override {}
bool IsExemptFromBudgetBasedThrottling() const override { return false; }
std::unique_ptr<blink::mojom::blink::PauseSubresourceLoadingHandle>
GetPauseSubresourceLoadingHandle() override {
return nullptr;
}
std::unique_ptr<ActiveConnectionHandle> OnActiveConnectionCreated() override {
return nullptr;
}
std::unique_ptr<WebResourceLoadingTaskRunnerHandle>
CreateResourceLoadingTaskRunnerHandle() override {
return WebResourceLoadingTaskRunnerHandle::CreateUnprioritized(
base::ThreadTaskRunnerHandle::Get());
}
ukm::SourceId GetUkmSourceId() override { return ukm::kInvalidSourceId; }
private:
PageScheduler* page_scheduler_;
DISALLOW_COPY_AND_ASSIGN(SimpleFrameScheduler);
};
class SimplePageScheduler : public PageScheduler {
public:
SimplePageScheduler() {}
~SimplePageScheduler() override {}
std::unique_ptr<FrameScheduler> CreateFrameScheduler(
FrameScheduler::Delegate* delegate,
BlameContext*,
FrameScheduler::FrameType) override {
return std::make_unique<SimpleFrameScheduler>(this);
}
void SetPageVisible(bool) override {}
void SetPageFrozen(bool) override {}
void SetKeepActive(bool) override {}
bool IsMainFrameLocal() const override { return true; }
void SetIsMainFrameLocal(bool) override {}
base::TimeTicks EnableVirtualTime() override { return base::TimeTicks(); }
void DisableVirtualTimeForTesting() override {}
bool VirtualTimeAllowedToAdvance() const override { return true; }
void SetInitialVirtualTime(base::Time) override {}
void SetInitialVirtualTimeOffset(base::TimeDelta) override {}
void SetVirtualTimePolicy(VirtualTimePolicy) override {}
void GrantVirtualTimeBudget(base::TimeDelta, base::OnceClosure) override {}
void SetMaxVirtualTimeTaskStarvationCount(int) override {}
void AudioStateChanged(bool is_audio_playing) override {}
bool IsAudioPlaying() const override { return false; }
bool IsExemptFromBudgetBasedThrottling() const override { return false; }
bool HasActiveConnectionForTest() const override { return false; }
bool RequestBeginMainFrameNotExpected(bool) override { return false; }
private:
DISALLOW_COPY_AND_ASSIGN(SimplePageScheduler);
};
class SimpleMainThreadScheduler : public WebThreadScheduler,
public ThreadScheduler {
public:
SimpleMainThreadScheduler() {}
~SimpleMainThreadScheduler() override {}
// WebThreadScheduler implementation:
void Shutdown() override {}
scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override {
DCHECK(WTF::IsMainThread());
return base::ThreadTaskRunnerHandle::Get();
}
scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
override {
DCHECK(WTF::IsMainThread());
return base::ThreadTaskRunnerHandle::Get();
}
scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override {
DCHECK(WTF::IsMainThread());
return base::ThreadTaskRunnerHandle::Get();
}
scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override {
DCHECK(WTF::IsMainThread());
return base::ThreadTaskRunnerHandle::Get();
}
scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override {
DCHECK(WTF::IsMainThread());
return base::ThreadTaskRunnerHandle::Get();
}
scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override {
DCHECK(WTF::IsMainThread());
return base::ThreadTaskRunnerHandle::Get();
}
scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override {
DCHECK(WTF::IsMainThread());
return base::ThreadTaskRunnerHandle::Get();
}
std::unique_ptr<Thread> CreateMainThread() override {
return std::make_unique<SimpleThread>(this);
}
std::unique_ptr<PageScheduler> CreatePageScheduler(
PageScheduler::Delegate*) override {
return std::make_unique<SimplePageScheduler>();
}
// ThreadScheduler implementation:
bool ShouldYieldForHighPriorityWork() override { return false; }
bool CanExceedIdleDeadlineIfRequired() const override { return false; }
void PostIdleTask(const base::Location&, Thread::IdleTask) override {
// NOTREACHED();
}
void PostNonNestableIdleTask(const base::Location&,
Thread::IdleTask) override {
// NOTREACHED();
}
void AddRAILModeObserver(WebRAILModeObserver*) override {}
std::unique_ptr<WebThreadScheduler::RendererPauseHandle> PauseScheduler()
override {
return nullptr;
}
base::TimeTicks MonotonicallyIncreasingVirtualTime() override {
return base::TimeTicks::Now();
}
void AddTaskObserver(base::MessageLoop::TaskObserver*) override {}
void RemoveTaskObserver(base::MessageLoop::TaskObserver*) override {}
NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() override {
return nullptr;
}
};
} // namespace
std::unique_ptr<WebThreadScheduler> CreateWebMainThreadSchedulerForTests() {
return std::make_unique<scheduler::MainThreadSchedulerImpl>(
base::sequence_manager::SequenceManagerForTest::Create(
std::make_unique<
base::sequence_manager::LazyThreadControllerForTest>()),
base::nullopt);
return std::make_unique<SimpleMainThreadScheduler>();
}
void RunIdleTasksForTesting(WebThreadScheduler* scheduler,
......
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