Skip to content

Commit

Permalink
Adding support to defer task queues on the browser UI thread.
Browse files Browse the repository at this point in the history
BrowserUIThreadScheduler now creates QueueEnabledVoters for
all it's task queues.
BrowserUIThreadScheduler now supports a Policy object which controls
different BrowserTaskQueue's priority and being enabled/disabled.
Initial policy disables all queues less than or equal to
kNormalPriority during scrolls/flings, and enables them back when
the scroll/fling ends.
This feature is controlled through the kBrowserDeferUIThreadTasks
finch flag.
To enable feature on local builds:
build/android/adb_chrome_public_command_line --enable-features=BrowserDeferUIThreadTasks:defer_normal_or_less_priority_tasks/true/defer_known_long_running_tasks/true

Bug: 1393353
Change-Id: I419d471dbfdf418d27c9e2465c984869212eb519
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4055527
Commit-Queue: Omar Elmekkawy <mekk@chromium.org>
Reviewed-by: Alexander Timin <altimin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1084703}
  • Loading branch information
Omar Elmekkawy authored and Chromium LUCI CQ committed Dec 17, 2022
1 parent 71f7a4c commit b081b58
Show file tree
Hide file tree
Showing 9 changed files with 364 additions and 62 deletions.
3 changes: 3 additions & 0 deletions base/tracing/protos/chrome_track_event.proto
Expand Up @@ -912,6 +912,9 @@ message SequenceManagerTask {
WORKER_THROTTLEABLE_TQ = 49;
WORKER_UNPAUSABLE_TQ = 50;
WORKER_WEB_SCHEDULING_TQ = 51;

UI_USER_BLOCKING_DEFERRABLE_TQ = 52;
IO_USER_BLOCKING_DEFERRABLE_TQ = 53;
}

optional Priority priority = 1;
Expand Down
Expand Up @@ -29,7 +29,7 @@ TEST(BrowserIOThreadDelegateTest, CanPostTasksToThread) {
thread.StartWithOptions(std::move(options));

auto runner =
handle->GetBrowserTaskRunner(BrowserTaskQueues::QueueType::kDefault);
handle->GetBrowserTaskRunner(BrowserTaskQueues::QueueType::kUserBlocking);

base::WaitableEvent event;
runner->PostTask(FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
Expand Down
Expand Up @@ -194,7 +194,7 @@ class BrowserTaskExecutorWithCustomSchedulerTest : public testing::Test {
BrowserUIThreadScheduler::CreateForTesting(sequence_manager());
DeferredInitFromSubclass(
browser_ui_thread_scheduler->GetHandle()->GetBrowserTaskRunner(
QueueType::kDefault));
QueueType::kUserBlocking));
BrowserTaskExecutor::CreateForTesting(
std::move(browser_ui_thread_scheduler),
BrowserIOThreadDelegate::CreateForTesting(sequence_manager()));
Expand Down
64 changes: 37 additions & 27 deletions content/browser/scheduler/browser_task_queues.cc
Expand Up @@ -4,6 +4,8 @@

#include "content/browser/scheduler/browser_task_queues.h"

#include <array>
#include <cstdint>
#include <iterator>

#include "base/bind.h"
Expand Down Expand Up @@ -63,6 +65,8 @@ QueueName GetUITaskQueueName(BrowserTaskQueues::QueueType queue_type) {
return QueueName::UI_BEST_EFFORT_TQ;
case BrowserTaskQueues::QueueType::kDefault:
return QueueName::UI_DEFAULT_TQ;
case BrowserTaskQueues::QueueType::kDeferrableUserBlocking:
return QueueName::UI_USER_BLOCKING_DEFERRABLE_TQ;
case BrowserTaskQueues::QueueType::kUserBlocking:
return QueueName::UI_USER_BLOCKING_TQ;
case BrowserTaskQueues::QueueType::kUserVisible:
Expand All @@ -82,6 +86,8 @@ QueueName GetIOTaskQueueName(BrowserTaskQueues::QueueType queue_type) {
return QueueName::IO_BEST_EFFORT_TQ;
case BrowserTaskQueues::QueueType::kDefault:
return QueueName::IO_DEFAULT_TQ;
case BrowserTaskQueues::QueueType::kDeferrableUserBlocking:
return QueueName::IO_USER_BLOCKING_DEFERRABLE_TQ;
case BrowserTaskQueues::QueueType::kUserBlocking:
return QueueName::IO_USER_BLOCKING_TQ;
case BrowserTaskQueues::QueueType::kUserVisible:
Expand Down Expand Up @@ -109,21 +115,14 @@ QueueName GetTaskQueueName(BrowserThread::ID thread_id,
return QueueName::UNKNOWN_TQ;
}

QueueName GetDefaultQueueName(BrowserThread::ID thread_id) {
switch (thread_id) {
case BrowserThread::UI:
return QueueName::UI_THREAD_TQ;
case BrowserThread::IO:
return QueueName::IO_THREAD_TQ;
case BrowserThread::ID_COUNT:
break;
}
NOTREACHED();
return QueueName::UNKNOWN_TQ;
}

} // namespace

BrowserTaskQueues::QueueData::QueueData() = default;
BrowserTaskQueues::QueueData::~QueueData() = default;
BrowserTaskQueues::QueueData::QueueData(BrowserTaskQueues::QueueData&& other) {
task_queue_ = std::move(other.task_queue_);
voter_ = std::move(other.voter_);
}
BrowserTaskQueues::Handle::~Handle() = default;

BrowserTaskQueues::Handle::Handle(BrowserTaskQueues* outer)
Expand Down Expand Up @@ -155,23 +154,23 @@ void BrowserTaskQueues::Handle::ScheduleRunAllPendingTasksForTesting(
base::ScopedClosureRunner(std::move(on_pending_task_ran))));
}

BrowserTaskQueues::QueueData::QueueData() = default;
BrowserTaskQueues::QueueData::~QueueData() = default;

BrowserTaskQueues::BrowserTaskQueues(
BrowserThread::ID thread_id,
base::sequence_manager::SequenceManager* sequence_manager) {
for (size_t i = 0; i < queue_data_.size(); ++i) {
queue_data_[i].task_queue = sequence_manager->CreateTaskQueue(
queue_data_[i].task_queue_ = sequence_manager->CreateTaskQueue(
base::sequence_manager::TaskQueue::Spec(
GetTaskQueueName(thread_id, static_cast<QueueType>(i))));
queue_data_[i].voter = queue_data_[i].task_queue->CreateQueueEnabledVoter();
queue_data_[i].voter->SetVoteToEnable(false);
queue_data_[i].voter_ =
queue_data_[i].task_queue_->CreateQueueEnabledVoter();
if (static_cast<QueueType>(i) != QueueType::kDefault) {
queue_data_[i].voter_->SetVoteToEnable(false);
}
}

// Default task queue
default_task_queue_ = sequence_manager->CreateTaskQueue(
base::sequence_manager::TaskQueue::Spec(GetDefaultQueueName(thread_id)));
default_task_queue_ =
queue_data_[static_cast<uint32_t>(QueueType::kDefault)].task_queue_;

GetBrowserTaskQueue(QueueType::kUserVisible)
->SetQueuePriority(QueuePriority::kLowPriority);
Expand Down Expand Up @@ -208,7 +207,7 @@ BrowserTaskQueues::BrowserTaskQueues(

BrowserTaskQueues::~BrowserTaskQueues() {
for (auto& queue : queue_data_) {
queue.task_queue->ShutdownTaskQueue();
queue.task_queue_->ShutdownTaskQueue();
}
control_queue_->ShutdownTaskQueue();
default_task_queue_->ShutdownTaskQueue();
Expand All @@ -221,15 +220,26 @@ BrowserTaskQueues::CreateBrowserTaskRunners() const {
std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes>
task_runners;
for (size_t i = 0; i < queue_data_.size(); ++i) {
task_runners[i] = queue_data_[i].task_queue->task_runner();
task_runners[i] = queue_data_[i].task_queue_->task_runner();
}
return task_runners;
}

std::array<BrowserTaskQueues::QueueData, BrowserTaskQueues::kNumQueueTypes>
BrowserTaskQueues::GetQueueData() const {
std::array<BrowserTaskQueues::QueueData, BrowserTaskQueues::kNumQueueTypes>
queue_data;
for (size_t i = 0; i < queue_data.size(); ++i) {
queue_data[i].task_queue_ = queue_data_[i].task_queue_;
queue_data[i].voter_ = queue_data[i].task_queue_->CreateQueueEnabledVoter();
}
return queue_data;
}

void BrowserTaskQueues::OnStartupComplete() {
// Enable all queues
for (const auto& queue : queue_data_) {
queue.voter->SetVoteToEnable(true);
queue.voter_->SetVoteToEnable(true);
}

// Update ServiceWorker task queue priority.
Expand All @@ -247,7 +257,7 @@ void BrowserTaskQueues::OnStartupComplete() {
void BrowserTaskQueues::EnableAllExceptBestEffortQueues() {
for (size_t i = 0; i < queue_data_.size(); ++i) {
if (i != static_cast<size_t>(QueueType::kBestEffort))
queue_data_[i].voter->SetVoteToEnable(true);
queue_data_[i].voter_->SetVoteToEnable(true);
}
}

Expand All @@ -266,7 +276,7 @@ void BrowserTaskQueues::StartRunAllPendingTasksForTesting(
base::ScopedClosureRunner on_pending_task_ran) {
++run_all_pending_nesting_level_;
for (const auto& queue : queue_data_) {
queue.task_queue->InsertFence(InsertFencePosition::kNow);
queue.task_queue_->InsertFence(InsertFencePosition::kNow);
}
default_task_queue_->InsertFence(InsertFencePosition::kNow);
run_all_pending_tasks_queue_->task_runner()->PostTask(
Expand All @@ -280,7 +290,7 @@ void BrowserTaskQueues::EndRunAllPendingTasksForTesting(
--run_all_pending_nesting_level_;
if (run_all_pending_nesting_level_ == 0) {
for (const auto& queue : queue_data_) {
queue.task_queue->RemoveFence();
queue.task_queue_->RemoveFence();
}
default_task_queue_->RemoveFence();
}
Expand Down
37 changes: 29 additions & 8 deletions content/browser/scheduler/browser_task_queues.h
Expand Up @@ -48,6 +48,11 @@ class CONTENT_EXPORT BrowserTaskQueues {
// practice.
kBestEffort,

// Those are tasks that affect the UI, but not urgent enough to run
// immediately, those tasks are either deferred or run based on the
// scheduling policy.
kDeferrableUserBlocking,

// base::TaskPriority::kUserBlocking maps to this task queue. It's for tasks
// that affect the UI immediately after a user interaction. Has the same
// priority as kDefault.
Expand Down Expand Up @@ -78,6 +83,16 @@ class CONTENT_EXPORT BrowserTaskQueues {
static constexpr size_t kNumQueueTypes =
static_cast<size_t>(QueueType::kMaxValue) + 1;

class CONTENT_EXPORT QueueData {
public:
QueueData();
~QueueData();
QueueData(QueueData&& other);
scoped_refptr<base::sequence_manager::TaskQueue> task_queue_;
std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter>
voter_;
};

// Handle to a BrowserTaskQueues instance that can be used from any thread
// as all operations are thread safe.
//
Expand All @@ -99,9 +114,14 @@ class CONTENT_EXPORT BrowserTaskQueues {
return browser_task_runners_[static_cast<size_t>(queue_type)];
}

// Informs that startup is complete. Can be called multiple times.
// Called after startup is complete, enables all task queues and can
// be called multiple times.
void OnStartupComplete();

// Called quite early in startup after initialising the owning thread's
// scheduler, before we call RunLoop::Run on the thread.
// Note: default_task_queue_ doesn't need to be enabled as it is not
// disabled during startup.
// Enables all task queues except the effort ones. Can be called multiple
// times.
void EnableAllExceptBestEffortQueues();
Expand Down Expand Up @@ -144,6 +164,13 @@ class CONTENT_EXPORT BrowserTaskQueues {
browser_task_runners_;
};

// Creates queue voters for all task queues created within this
// BrowserTaskQueues object, then zips voters with the queues in
// a QueueData object..
// NOTE: You can only call this function from the thread that owns the
// task queues, and you can only use the voters on the same thread.
std::array<QueueData, kNumQueueTypes> GetQueueData() const;

// |sequence_manager| must outlive this instance.
explicit BrowserTaskQueues(
BrowserThread::ID thread_id,
Expand All @@ -166,18 +193,12 @@ class CONTENT_EXPORT BrowserTaskQueues {
void EnableAllExceptBestEffortQueues();

base::sequence_manager::TaskQueue* GetBrowserTaskQueue(QueueType type) const {
return queue_data_[static_cast<size_t>(type)].task_queue.get();
return queue_data_[static_cast<size_t>(type)].task_queue_.get();
}

std::array<scoped_refptr<base::SingleThreadTaskRunner>, kNumQueueTypes>
CreateBrowserTaskRunners() const;

struct QueueData {
QueueData();
~QueueData();
scoped_refptr<base::sequence_manager::TaskQueue> task_queue;
std::unique_ptr<base::sequence_manager::TaskQueue::QueueEnabledVoter> voter;
};
std::array<QueueData, kNumQueueTypes> queue_data_;

// Helper queue to make sure private methods run on the associated thread. the
Expand Down
32 changes: 21 additions & 11 deletions content/browser/scheduler/browser_task_queues_unittest.cc
Expand Up @@ -58,6 +58,8 @@ TEST_F(BrowserTaskQueuesTest, NoTaskRunsUntilQueuesAreEnabled) {
{
RunLoop run_loop;
handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
// Default queue isn't disabled and should run.
EXPECT_CALL(task, Run).Times(1);
run_loop.Run();
}

Expand All @@ -66,16 +68,20 @@ TEST_F(BrowserTaskQueuesTest, NoTaskRunsUntilQueuesAreEnabled) {
{
RunLoop run_loop;
handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes);
// All tasks should run except default queue which already run as
// it's not disabled during startup.
EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes - 1);
run_loop.Run();
}
}

TEST_F(BrowserTaskQueuesTest, OnlyDefaultQueueRunsTasksOnCreation) {
StrictMockTask task;
for (size_t i = 0; i < BrowserTaskQueues::kNumQueueTypes; ++i) {
handle_->GetBrowserTaskRunner(static_cast<QueueType>(i))
->PostTask(FROM_HERE, task.Get());
if (static_cast<QueueType>(i) != QueueType::kDefault) {
handle_->GetBrowserTaskRunner(static_cast<QueueType>(i))
->PostTask(FROM_HERE, task.Get());
}
}

StrictMockTask default_task;
Expand All @@ -99,23 +105,27 @@ TEST_F(BrowserTaskQueuesTest, TasksRunWhenQueuesAreEnabled) {
{
RunLoop run_loop;
handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
// Default queue isn't disabled.
EXPECT_CALL(task, Run).Times(1);
run_loop.Run();
}

handle_->OnStartupComplete();

{
RunLoop run_loop;
// All tasks should run, except default queue which is already run
// as default queue isn't disabled.
handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes);
EXPECT_CALL(task, Run).Times(BrowserTaskQueues::kNumQueueTypes - 1);
run_loop.Run();
}
}

TEST_F(BrowserTaskQueuesTest, SimplePosting) {
handle_->OnStartupComplete();
scoped_refptr<base::SingleThreadTaskRunner> tq =
handle_->GetBrowserTaskRunner(QueueType::kDefault);
handle_->GetBrowserTaskRunner(QueueType::kUserBlocking);

StrictMockTask task_1;
StrictMockTask task_2;
Expand Down Expand Up @@ -147,7 +157,7 @@ TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTesting) {
}
}));

handle_->GetBrowserTaskRunner(QueueType::kDefault)
handle_->GetBrowserTaskRunner(QueueType::kUserBlocking)
->PostTask(FROM_HERE, task.Get());

{
Expand Down Expand Up @@ -190,19 +200,19 @@ TEST_F(BrowserTaskQueuesTest, RunAllPendingTasksForTestingIsReentrant) {
StrictMockTask task_3;

EXPECT_CALL(task_1, Run).WillOnce(Invoke([&]() {
handle_->GetBrowserTaskRunner(QueueType::kDefault)
handle_->GetBrowserTaskRunner(QueueType::kUserBlocking)
->PostTask(FROM_HERE, task_2.Get());
RunLoop run_loop(RunLoop::Type::kNestableTasksAllowed);
handle_->ScheduleRunAllPendingTasksForTesting(run_loop.QuitClosure());
run_loop.Run();
}));

EXPECT_CALL(task_2, Run).WillOnce(Invoke([&]() {
handle_->GetBrowserTaskRunner(QueueType::kDefault)
handle_->GetBrowserTaskRunner(QueueType::kUserBlocking)
->PostTask(FROM_HERE, task_3.Get());
}));

handle_->GetBrowserTaskRunner(QueueType::kDefault)
handle_->GetBrowserTaskRunner(QueueType::kUserBlocking)
->PostTask(FROM_HERE, task_1.Get());

RunLoop run_loop;
Expand All @@ -218,7 +228,7 @@ TEST_F(BrowserTaskQueuesTest,

handle_->GetBrowserTaskRunner(QueueType::kBestEffort)
->PostTask(FROM_HERE, best_effort_task.Get());
handle_->GetBrowserTaskRunner(QueueType::kDefault)
handle_->GetBrowserTaskRunner(QueueType::kUserBlocking)
->PostTask(FROM_HERE, default_task.Get());

EXPECT_CALL(default_task, Run);
Expand All @@ -244,7 +254,7 @@ TEST_F(BrowserTaskQueuesTest,
}));
EXPECT_CALL(task_2, Run);

handle_->GetBrowserTaskRunner(QueueType::kDefault)
handle_->GetBrowserTaskRunner(QueueType::kUserBlocking)
->PostTask(FROM_HERE, task_1.Get());
handle_->GetBrowserTaskRunner(QueueType::kBestEffort)
->PostTask(FROM_HERE, task_2.Get());
Expand Down

0 comments on commit b081b58

Please sign in to comment.