Commit 37ce9b49 authored by Alexander Timin's avatar Alexander Timin Committed by Commit Bot

[scheduler] Introduce ThreadController.

ThreadController is a designed replacement for TaskQueueManagerDelegate
in a SequenceManager world.

Current implementation of a ThreadController allows for drop-in
replacement of TaskQueueManagerDelegate. Further refactorings are
planned after complete deletion of TaskQueueManagerDelegate.

Full replacement code can be found at
https://chromium-review.googlesource.com/c/chromium/src/+/763511

R=skyostil@chromium.org,alexclarke@chromium.org,robliao@chromium.org,fdoray@chromium.org,gab@chromium.org

Bug: 783309

Change-Id: Id424da3cf43ac20b40c93ab7f20d79294d854df9
Reviewed-on: https://chromium-review.googlesource.com/765267
Commit-Queue: Alexander Timin <altimin@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Reviewed-by: default avatarRobert Liao <robliao@chromium.org>
Reviewed-by: default avatarAlex Clarke <alexclarke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517093}
parent e1317096
......@@ -16,6 +16,7 @@ jumbo_source_set("scheduler") {
"base/moveable_auto_lock.h",
"base/real_time_domain.cc",
"base/real_time_domain.h",
"base/sequence.h",
"base/task_queue.cc",
"base/task_queue.h",
"base/task_queue_impl.cc",
......@@ -26,6 +27,9 @@ jumbo_source_set("scheduler") {
"base/task_queue_selector.cc",
"base/task_queue_selector.h",
"base/task_time_observer.h",
"base/thread_controller.h",
"base/thread_controller_impl.cc",
"base/thread_controller_impl.h",
"base/time_domain.cc",
"base/time_domain.h",
"base/virtual_time_domain.cc",
......@@ -154,6 +158,8 @@ jumbo_source_set("test_support") {
"test/fake_web_view_scheduler.h",
"test/lazy_scheduler_message_loop_delegate_for_tests.cc",
"test/lazy_scheduler_message_loop_delegate_for_tests.h",
"test/lazy_thread_controller_for_test.cc",
"test/lazy_thread_controller_for_test.h",
"test/renderer_scheduler_test_support.cc",
"test/test_task_queue.cc",
"test/test_task_queue.h",
......
// 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 THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_SEQUENCE_H_
#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_SEQUENCE_H_
namespace blink {
namespace scheduler {
namespace internal {
// This is temporary interface for ThreadController to be able to run tasks
// from TaskQueueManager.
class Sequence {
public:
enum class WorkType { kImmediate, kDelayed };
// Take a next task to run from a sequence.
// TODO(altimin): Do not pass |work_type| here.
virtual base::PendingTask TakeTask(WorkType work_type) = 0;
// Notify a sequence that a taken task has been completed.
// Returns true if sequence has more work to do.
virtual bool DidRunTask() = 0;
};
} // namespace internal
} // namespace scheduler
} // namespace blink
#endif // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_SEQUENCE_H_
// 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 THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_WORKER_CONTROLLER_H_
#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_WORKER_CONTROLLER_H_
#include "base/location.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "platform/PlatformExport.h"
namespace base {
class TickClock;
};
namespace blink {
namespace scheduler {
namespace internal {
class Sequence;
// Interface for TaskQueueManager to schedule work to be run.
class PLATFORM_EXPORT ThreadController {
public:
virtual ~ThreadController() {}
// Notify the controller that its associated sequence has immediate work
// to run. Shortly after this is called, the thread associated with this
// controller will run a task returned by sequence->TakeTask().
//
// TODO(altimin): Change this to "the thread associated with this
// controller will run tasks returned by sequence->TakeTask() until it
// returns null or sequence->DidRunTask() returns false" once the
// code is changed to work that way.
virtual void ScheduleWork() = 0;
// Notify the controller that its associated sequence will have
// delayed work to run when |delay| expires. When |delay| expires,
// the thread associated with this controller will run a task
// returned by sequence->TakeTask(). This call cancels any previously
// scheduled delayed work.
//
// TODO(altimin): Change this to "the thread associated with this
// controller will run tasks returned by sequence->TakeTask() until
// it returns null or sequence->DidRunTask() returns false" once the
// code is changed to work that way.
virtual void ScheduleDelayedWork(base::TimeDelta delay) = 0;
// Notify thread controller that sequence does not have delayed work
// and previously scheduled callbacks should be cancelled.
virtual void CancelDelayedWork() = 0;
// Sets the sequence from which to take tasks after a Schedule*Work() call is
// made. Must be called before the first call to Schedule*Work().
virtual void SetSequence(Sequence*) = 0;
// TODO(altimin): Get rid of the methods below.
// These methods exist due to current integration of TaskQueueManager
// with MessageLoop.
// TaskQueueManager should schedule non-nestable work itself when
// appropriate.
virtual void PostNonNestableTask(const base::Location& from_here,
base::OnceClosure task) = 0;
virtual bool RunsTasksInCurrentSequence() = 0;
virtual base::TickClock* GetClock() = 0;
virtual void SetDefaultTaskRunner(
scoped_refptr<base::SingleThreadTaskRunner>) = 0;
virtual void RestoreDefaultTaskRunner() = 0;
virtual bool IsNested() = 0;
virtual void AddNestingObserver(base::RunLoop::NestingObserver* observer) = 0;
virtual void RemoveNestingObserver(
base::RunLoop::NestingObserver* observer) = 0;
};
} // namespace internal
} // namespace scheduler
} // namespace blink
#endif // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_WORKER_CONTROLLER_H_
// 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 "platform/scheduler/base/thread_controller_impl.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/time/tick_clock.h"
#include "platform/scheduler/base/sequence.h"
namespace blink {
namespace scheduler {
namespace internal {
ThreadControllerImpl::ThreadControllerImpl(
base::MessageLoop* message_loop,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
std::unique_ptr<base::TickClock> time_source)
: message_loop_(message_loop),
task_runner_(task_runner),
message_loop_task_runner_(message_loop ? message_loop->task_runner()
: nullptr),
time_source_(std::move(time_source)),
weak_factory_(this) {
immediate_do_work_closure_ = base::BindRepeating(
&ThreadControllerImpl::DoWork, weak_factory_.GetWeakPtr(),
Sequence::WorkType::kImmediate);
delayed_do_work_closure_ = base::BindRepeating(&ThreadControllerImpl::DoWork,
weak_factory_.GetWeakPtr(),
Sequence::WorkType::kDelayed);
}
ThreadControllerImpl::~ThreadControllerImpl() {}
std::unique_ptr<ThreadControllerImpl> ThreadControllerImpl::Create(
base::MessageLoop* message_loop,
std::unique_ptr<base::TickClock> time_source) {
return base::WrapUnique(new ThreadControllerImpl(
message_loop, message_loop->task_runner(), std::move(time_source)));
}
void ThreadControllerImpl::SetSequence(Sequence* sequence) {
DCHECK(sequence);
DCHECK(!sequence_);
sequence_ = sequence;
}
void ThreadControllerImpl::ScheduleWork() {
DCHECK(sequence_);
task_runner_->PostTask(FROM_HERE, immediate_do_work_closure_);
}
void ThreadControllerImpl::ScheduleDelayedWork(base::TimeDelta delay) {
DCHECK(sequence_);
cancelable_delayed_do_work_closure_.Reset(delayed_do_work_closure_);
task_runner_->PostDelayedTask(
FROM_HERE, cancelable_delayed_do_work_closure_.callback(), delay);
}
void ThreadControllerImpl::CancelDelayedWork() {
DCHECK(sequence_);
cancelable_delayed_do_work_closure_.Cancel();
}
void ThreadControllerImpl::PostNonNestableTask(const base::Location& from_here,
base::OnceClosure task) {
task_runner_->PostNonNestableTask(from_here, std::move(task));
}
bool ThreadControllerImpl::RunsTasksInCurrentSequence() {
return task_runner_->RunsTasksInCurrentSequence();
}
base::TickClock* ThreadControllerImpl::GetClock() {
return time_source_.get();
}
void ThreadControllerImpl::SetDefaultTaskRunner(
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
if (!message_loop_)
return;
message_loop_->SetTaskRunner(task_runner);
}
void ThreadControllerImpl::RestoreDefaultTaskRunner() {
if (!message_loop_)
return;
message_loop_->SetTaskRunner(message_loop_task_runner_);
}
bool ThreadControllerImpl::IsNested() {
DCHECK(RunsTasksInCurrentSequence());
return base::RunLoop::IsNestedOnCurrentThread();
}
void ThreadControllerImpl::DoWork(Sequence::WorkType work_type) {
DCHECK(sequence_);
base::PendingTask task = sequence_->TakeTask(work_type);
base::WeakPtr<ThreadControllerImpl> weak_ptr = weak_factory_.GetWeakPtr();
task_annotator_.RunTask("ThreadControllerImpl::DoWork", &task);
if (weak_ptr)
sequence_->DidRunTask();
}
void ThreadControllerImpl::AddNestingObserver(
base::RunLoop::NestingObserver* observer) {
base::RunLoop::AddNestingObserverOnCurrentThread(observer);
}
void ThreadControllerImpl::RemoveNestingObserver(
base::RunLoop::NestingObserver* observer) {
base::RunLoop::RemoveNestingObserverOnCurrentThread(observer);
}
} // namespace internal
} // namespace scheduler
} // namespace blink
// 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 THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_WORKER_CONTROLLER_IMPL_H_
#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_WORKER_CONTROLLER_IMPL_H_
#include "platform/scheduler/base/thread_controller.h"
#include "base/cancelable_callback.h"
#include "base/debug/task_annotator.h"
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
#include "platform/PlatformExport.h"
#include "platform/scheduler/base/sequence.h"
namespace base {
class MessageLoop;
class TickClock;
} // namespace base
namespace blink {
namespace scheduler {
namespace internal {
class PLATFORM_EXPORT ThreadControllerImpl : public ThreadController {
public:
~ThreadControllerImpl() override;
static std::unique_ptr<ThreadControllerImpl> Create(
base::MessageLoop* message_loop,
std::unique_ptr<base::TickClock> time_source);
// ThreadController:
void ScheduleWork() override;
void ScheduleDelayedWork(base::TimeDelta delay) override;
void CancelDelayedWork() override;
void SetSequence(Sequence* sequence) override;
void PostNonNestableTask(const base::Location& from_here,
base::OnceClosure task) override;
bool RunsTasksInCurrentSequence() override;
base::TickClock* GetClock() override;
void SetDefaultTaskRunner(
scoped_refptr<base::SingleThreadTaskRunner>) override;
void RestoreDefaultTaskRunner() override;
bool IsNested() override;
void AddNestingObserver(base::RunLoop::NestingObserver* observer) override;
void RemoveNestingObserver(base::RunLoop::NestingObserver* observer) override;
protected:
ThreadControllerImpl(base::MessageLoop* message_loop,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
std::unique_ptr<base::TickClock> time_source);
// TODO(altimin): Make these const. Blocked on removing
// lazy initialisation support.
base::MessageLoop* message_loop_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
private:
void DoWork(Sequence::WorkType work_type);
scoped_refptr<base::SingleThreadTaskRunner> message_loop_task_runner_;
std::unique_ptr<base::TickClock> time_source_;
base::RepeatingClosure immediate_do_work_closure_;
base::RepeatingClosure delayed_do_work_closure_;
base::CancelableClosure cancelable_delayed_do_work_closure_;
Sequence* sequence_ = nullptr; // NOT OWNED
base::debug::TaskAnnotator task_annotator_;
base::WeakPtrFactory<ThreadControllerImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ThreadControllerImpl);
};
} // namespace internal
} // namespace scheduler
} // namespace blink
#endif // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_BASE_WORKER_CONTROLLER_IMPL_H_
// 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 "platform/scheduler/test/lazy_thread_controller_for_test.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/time/default_tick_clock.h"
namespace blink {
namespace scheduler {
LazyThreadControllerForTest::LazyThreadControllerForTest()
: ThreadControllerImpl(base::MessageLoop::current(),
nullptr,
std::make_unique<base::DefaultTickClock>()),
thread_ref_(base::PlatformThread::CurrentRef()) {
if (message_loop_)
task_runner_ = message_loop_->task_runner();
}
LazyThreadControllerForTest::~LazyThreadControllerForTest() {}
void LazyThreadControllerForTest::EnsureMessageLoop() {
if (message_loop_)
return;
DCHECK(RunsTasksInCurrentSequence());
message_loop_ = base::MessageLoop::current();
DCHECK(message_loop_);
task_runner_ = message_loop_->task_runner();
if (pending_observer_) {
base::RunLoop::AddNestingObserverOnCurrentThread(pending_observer_);
pending_observer_ = nullptr;
}
}
bool LazyThreadControllerForTest::HasMessageLoop() {
return !!message_loop_;
}
void LazyThreadControllerForTest::AddNestingObserver(
base::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()
// -> TaskQueueManager::TaskQueueManager()
// -> LazySchedulerMessageLoopDelegateForTests::AddNestingObserver()
// 2) Any test framework with a base::MessageLoop member (and not caring
// about the blink scheduler) does:
// ThreadTaskRunnerHandle::Get()->PostTask(
// FROM_HERE, an_init_task_with_a_nested_loop);
// RunLoop.RunUntilIdle();
// 3) |a_task_with_a_nested_loop| triggers
// TaskQueueManager::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 TaskQueueManager::DoWork()
// which expects to be the only one to trigger nested loops (doesn't
// support TaskQueueManager::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.
if (!HasMessageLoop()) {
DCHECK(!pending_observer_);
pending_observer_ = observer;
return;
}
base::RunLoop::AddNestingObserverOnCurrentThread(observer);
}
void LazyThreadControllerForTest::RemoveNestingObserver(
base::RunLoop::NestingObserver* observer) {
if (!HasMessageLoop()) {
DCHECK_EQ(pending_observer_, observer);
pending_observer_ = nullptr;
return;
}
base::RunLoop::RemoveNestingObserverOnCurrentThread(observer);
}
bool LazyThreadControllerForTest::IsNested() {
EnsureMessageLoop();
return ThreadControllerImpl::IsNested();
}
bool LazyThreadControllerForTest::RunsTasksInCurrentSequence() {
return thread_ref_ == base::PlatformThread::CurrentRef();
}
void LazyThreadControllerForTest::ScheduleWork() {
EnsureMessageLoop();
ThreadControllerImpl::ScheduleWork();
}
void LazyThreadControllerForTest::ScheduleDelayedWork(base::TimeDelta delay) {
EnsureMessageLoop();
ThreadControllerImpl::ScheduleDelayedWork(delay);
}
void LazyThreadControllerForTest::CancelDelayedWork() {
EnsureMessageLoop();
ThreadControllerImpl::CancelDelayedWork();
}
void LazyThreadControllerForTest::PostNonNestableTask(
const base::Location& from_here,
base::OnceClosure task) {
EnsureMessageLoop();
ThreadControllerImpl::PostNonNestableTask(from_here, std::move(task));
}
} // namespace scheduler
} // namespace blink
// 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 THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_TEST_LAZY_THREAD_CONTROLLER_FOR_TEST_H_
#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_TEST_LAZY_THREAD_CONTROLLER_FOR_TEST_H_
#include "base/threading/platform_thread.h"
#include "platform/scheduler/base/thread_controller_impl.h"
namespace blink {
namespace scheduler {
// 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();
// internal::ThreadControllerImpl:
bool IsNested() override;
void AddNestingObserver(base::RunLoop::NestingObserver* observer) override;
void RemoveNestingObserver(base::RunLoop::NestingObserver* observer) override;
bool RunsTasksInCurrentSequence() override;
void ScheduleWork() override;
void ScheduleDelayedWork(base::TimeDelta delta) override;
void CancelDelayedWork() override;
void PostNonNestableTask(const base::Location& from_here,
base::OnceClosure task);
private:
bool HasMessageLoop();
void EnsureMessageLoop();
base::PlatformThreadRef thread_ref_;
base::RunLoop::NestingObserver* pending_observer_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(LazyThreadControllerForTest);
};
} // namespace scheduler
} // namespace blink
#endif // THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_TEST_LAZY_THREAD_CONTROLLER_FOR_TEST_H_
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