blob: 10b2da31554133b5dcbb7064043bc64df1fba9b2 [file] [log] [blame]
// Copyright 2018 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 "third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.h"
#include <memory>
#include <tuple>
#include <utility>
#include "base/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/task_queue.h"
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
using base::sequence_manager::TaskQueue;
using QueueType = blink::scheduler::MainThreadTaskQueue::QueueType;
using QueueTraits = blink::scheduler::MainThreadTaskQueue::QueueTraits;
namespace blink {
namespace scheduler {
class FrameTaskQueueControllerTest : public testing::Test,
public FrameTaskQueueController::Delegate {
public:
FrameTaskQueueControllerTest()
: task_environment_(
base::test::TaskEnvironment::TimeSource::MOCK_TIME,
base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED),
task_queue_created_count_(0) {}
~FrameTaskQueueControllerTest() override = default;
void SetUp() override {
scheduler_ = std::make_unique<MainThreadSchedulerImpl>(
base::sequence_manager::SequenceManagerForTest::Create(
nullptr, task_environment_.GetMainThreadTaskRunner(),
task_environment_.GetMockTickClock()),
base::nullopt);
agent_group_scheduler_ = scheduler_->CreateAgentGroupScheduler();
page_scheduler_ =
agent_group_scheduler_->AsAgentGroupScheduler().CreatePageScheduler(
nullptr);
frame_scheduler_ = page_scheduler_->CreateFrameScheduler(
nullptr, nullptr, FrameScheduler::FrameType::kSubframe);
frame_task_queue_controller_ = std::make_unique<FrameTaskQueueController>(
scheduler_.get(),
static_cast<FrameSchedulerImpl*>(frame_scheduler_.get()), this);
}
void TearDown() override {
frame_task_queue_controller_.reset();
frame_scheduler_.reset();
page_scheduler_.reset();
agent_group_scheduler_.reset();
scheduler_->Shutdown();
scheduler_.reset();
}
// FrameTaskQueueController::Delegate implementation.
void OnTaskQueueCreated(MainThreadTaskQueue* task_queue,
TaskQueue::QueueEnabledVoter* voter) override {
++task_queue_created_count_;
}
protected:
scoped_refptr<MainThreadTaskQueue> LoadingTaskQueue() const {
return frame_task_queue_controller_->GetTaskQueue(QueueTraits()
.SetCanBePaused(true)
.SetCanBeFrozen(true)
.SetCanBeDeferred(true)
.SetPrioritisationType(
QueueTraits::PrioritisationType::kLoading));
}
scoped_refptr<MainThreadTaskQueue> LoadingControlTaskQueue() const {
return frame_task_queue_controller_->GetTaskQueue(QueueTraits()
.SetCanBePaused(true)
.SetCanBeFrozen(true)
.SetCanBeDeferred(true)
.SetPrioritisationType(
QueueTraits::PrioritisationType::kLoadingControl));
}
scoped_refptr<MainThreadTaskQueue> ThrottleableTaskQueue() const {
return frame_task_queue_controller_->GetTaskQueue(
QueueTraits()
.SetCanBeThrottled(true)
.SetCanBeFrozen(true)
.SetCanBeDeferred(true)
.SetCanBePaused(true)
.SetCanRunWhenVirtualTimePaused(false));
}
scoped_refptr<MainThreadTaskQueue> GetTaskQueue(
QueueTraits queue_traits) const {
return frame_task_queue_controller_->GetTaskQueue(queue_traits);
}
scoped_refptr<MainThreadTaskQueue> NewResourceLoadingTaskQueue() const {
return frame_task_queue_controller_->NewResourceLoadingTaskQueue();
}
size_t task_queue_created_count() const { return task_queue_created_count_; }
protected:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<MainThreadSchedulerImpl> scheduler_;
std::unique_ptr<WebAgentGroupScheduler> agent_group_scheduler_;
std::unique_ptr<PageScheduler> page_scheduler_;
std::unique_ptr<FrameScheduler> frame_scheduler_;
std::unique_ptr<FrameTaskQueueController> frame_task_queue_controller_;
private:
size_t task_queue_created_count_;
DISALLOW_COPY_AND_ASSIGN(FrameTaskQueueControllerTest);
};
TEST_F(FrameTaskQueueControllerTest, CreateAllTaskQueues) {
enum class QueueCheckResult { kDidNotSeeQueue, kDidSeeQueue };
WTF::HashMap<scoped_refptr<MainThreadTaskQueue>, QueueCheckResult>
all_task_queues;
scoped_refptr<MainThreadTaskQueue> task_queue = LoadingTaskQueue();
EXPECT_FALSE(all_task_queues.Contains(task_queue));
all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
task_queue = LoadingControlTaskQueue();
EXPECT_FALSE(all_task_queues.Contains(task_queue));
all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
// Create the 4 default task queues used by FrameSchedulerImpl.
task_queue = GetTaskQueue(QueueTraits()
.SetCanBeThrottled(true)
.SetCanBeDeferred(true)
.SetCanBeFrozen(true)
.SetCanBePaused(true)
.SetCanRunWhenVirtualTimePaused(false));
EXPECT_FALSE(all_task_queues.Contains(task_queue));
all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
task_queue = GetTaskQueue(QueueTraits()
.SetCanBeDeferred(true)
.SetCanBePaused(true)
.SetCanRunWhenVirtualTimePaused(false));
EXPECT_FALSE(all_task_queues.Contains(task_queue));
all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
task_queue = GetTaskQueue(
QueueTraits().SetCanBePaused(true).SetCanRunWhenVirtualTimePaused(false));
EXPECT_FALSE(all_task_queues.Contains(task_queue));
all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
task_queue =
GetTaskQueue(QueueTraits().SetCanRunWhenVirtualTimePaused(false));
EXPECT_FALSE(all_task_queues.Contains(task_queue));
all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
// Add a couple resource loading task queues.
task_queue = NewResourceLoadingTaskQueue();
EXPECT_FALSE(all_task_queues.Contains(task_queue));
all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
task_queue = NewResourceLoadingTaskQueue();
EXPECT_FALSE(all_task_queues.Contains(task_queue));
all_task_queues.insert(task_queue.get(), QueueCheckResult::kDidNotSeeQueue);
EXPECT_EQ(all_task_queues.size(), task_queue_created_count());
// Verify that we get all of the queues that we added, and only those queues.
EXPECT_EQ(all_task_queues.size(),
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
for (const auto& task_queue_and_voter :
frame_task_queue_controller_->GetAllTaskQueuesAndVoters()) {
MainThreadTaskQueue* task_queue_ptr;
TaskQueue::QueueEnabledVoter* voter;
std::tie(task_queue_ptr, voter) = task_queue_and_voter;
EXPECT_NE(task_queue_ptr, nullptr);
EXPECT_TRUE(all_task_queues.find(task_queue_ptr) != all_task_queues.end());
// Make sure we don't get the same queue twice.
auto it = all_task_queues.find(task_queue_ptr);
EXPECT_FALSE(it == all_task_queues.end());
EXPECT_EQ(it->value, QueueCheckResult::kDidNotSeeQueue);
all_task_queues.Set(task_queue_ptr, QueueCheckResult::kDidSeeQueue);
EXPECT_NE(voter, nullptr);
}
}
TEST_F(FrameTaskQueueControllerTest, RemoveResourceLoadingTaskQueues) {
scoped_refptr<MainThreadTaskQueue> resource_loading_queue1 =
NewResourceLoadingTaskQueue();
EXPECT_EQ(1u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
scoped_refptr<MainThreadTaskQueue> resource_loading_queue2 =
NewResourceLoadingTaskQueue();
EXPECT_EQ(2u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
// Check that we can remove the resource loading queues.
bool was_removed =
frame_task_queue_controller_->RemoveResourceLoadingTaskQueue(
resource_loading_queue1);
EXPECT_TRUE(was_removed);
EXPECT_EQ(1u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
// Can't delete twice.
was_removed = frame_task_queue_controller_->RemoveResourceLoadingTaskQueue(
resource_loading_queue1);
EXPECT_FALSE(was_removed);
EXPECT_EQ(1u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
was_removed = frame_task_queue_controller_->RemoveResourceLoadingTaskQueue(
resource_loading_queue2);
EXPECT_TRUE(was_removed);
EXPECT_EQ(0u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
// Can't delete twice.
was_removed = frame_task_queue_controller_->RemoveResourceLoadingTaskQueue(
resource_loading_queue2);
EXPECT_FALSE(was_removed);
EXPECT_EQ(0u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
}
TEST_F(FrameTaskQueueControllerTest, CannotRemoveNonResourceLoadingTaskQueues) {
scoped_refptr<MainThreadTaskQueue> task_queue = LoadingTaskQueue();
EXPECT_EQ(1u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
bool was_removed =
frame_task_queue_controller_->RemoveResourceLoadingTaskQueue(task_queue);
EXPECT_FALSE(was_removed);
EXPECT_EQ(1u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
}
TEST_F(FrameTaskQueueControllerTest,
NonWebSchedulingTaskQueueWebSchedulingPriorityNullopt) {
scoped_refptr<MainThreadTaskQueue> task_queue =
frame_task_queue_controller_->GetTaskQueue(
MainThreadTaskQueue::QueueTraits());
EXPECT_EQ(base::nullopt, task_queue->web_scheduling_priority());
}
TEST_F(FrameTaskQueueControllerTest, AddWebSchedulingTaskQueues) {
scoped_refptr<MainThreadTaskQueue> task_queue =
frame_task_queue_controller_->NewWebSchedulingTaskQueue(
QueueTraits(), WebSchedulingPriority::kUserBlockingPriority);
EXPECT_EQ(1u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
EXPECT_EQ(WebSchedulingPriority::kUserBlockingPriority,
task_queue->web_scheduling_priority().value());
task_queue = frame_task_queue_controller_->NewWebSchedulingTaskQueue(
QueueTraits(), WebSchedulingPriority::kUserVisiblePriority);
EXPECT_EQ(2u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
EXPECT_EQ(WebSchedulingPriority::kUserVisiblePriority,
task_queue->web_scheduling_priority().value());
task_queue = frame_task_queue_controller_->NewWebSchedulingTaskQueue(
QueueTraits(), WebSchedulingPriority::kBackgroundPriority);
EXPECT_EQ(3u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
EXPECT_EQ(WebSchedulingPriority::kBackgroundPriority,
task_queue->web_scheduling_priority().value());
}
TEST_F(FrameTaskQueueControllerTest,
AddMultipleSamePriorityWebSchedulingTaskQueues) {
scoped_refptr<MainThreadTaskQueue> task_queue1 =
frame_task_queue_controller_->NewWebSchedulingTaskQueue(
QueueTraits(), WebSchedulingPriority::kUserBlockingPriority);
EXPECT_EQ(1u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
EXPECT_EQ(WebSchedulingPriority::kUserBlockingPriority,
task_queue1->web_scheduling_priority().value());
scoped_refptr<MainThreadTaskQueue> task_queue2 =
frame_task_queue_controller_->NewWebSchedulingTaskQueue(
QueueTraits(), WebSchedulingPriority::kUserBlockingPriority);
EXPECT_EQ(2u,
frame_task_queue_controller_->GetAllTaskQueuesAndVoters().size());
EXPECT_EQ(WebSchedulingPriority::kUserBlockingPriority,
task_queue2->web_scheduling_priority().value());
EXPECT_NE(task_queue1.get(), task_queue2.get());
}
TEST_F(FrameTaskQueueControllerTest, QueueTypeFromQueueTraits) {
scoped_refptr<MainThreadTaskQueue> task_queue = LoadingTaskQueue();
EXPECT_EQ(task_queue->queue_type(),
MainThreadTaskQueue::QueueType::kFrameLoading);
task_queue = LoadingControlTaskQueue();
EXPECT_EQ(task_queue->queue_type(),
MainThreadTaskQueue::QueueType::kFrameLoadingControl);
task_queue = ThrottleableTaskQueue();
EXPECT_EQ(task_queue->queue_type(),
MainThreadTaskQueue::QueueType::kFrameThrottleable);
}
class TaskQueueCreationFromQueueTraitsTest :
public FrameTaskQueueControllerTest,
public testing::WithParamInterface<QueueTraits::PrioritisationType> {};
INSTANTIATE_TEST_SUITE_P(
All,
TaskQueueCreationFromQueueTraitsTest,
::testing::Values(
QueueTraits::PrioritisationType::kInternalScriptContinuation,
QueueTraits::PrioritisationType::kBestEffort,
QueueTraits::PrioritisationType::kRegular,
QueueTraits::PrioritisationType::kLoading,
QueueTraits::PrioritisationType::kLoadingControl,
QueueTraits::PrioritisationType::kFindInPage,
QueueTraits::PrioritisationType::kExperimentalDatabase,
QueueTraits::PrioritisationType::kJavaScriptTimer,
QueueTraits::PrioritisationType::kHighPriorityLocalFrame,
QueueTraits::PrioritisationType::kInput));
TEST_P(TaskQueueCreationFromQueueTraitsTest,
AddAndRetrieveAllTaskQueues) {
// Create queues for all combination of queue traits for all combinations of
// the 6 QueueTraits bits with different PrioritisationTypes.
WTF::HashSet<scoped_refptr<MainThreadTaskQueue>> all_task_queues;
constexpr size_t kTotalUniqueQueueTraits = 1 << 6;
for (size_t i = 0; i < kTotalUniqueQueueTraits; i++) {
QueueTraits::PrioritisationType prioritisation_type = GetParam();
MainThreadTaskQueue::QueueTraits queue_traits =
QueueTraits()
.SetCanBeThrottled(!!(i & 1 << 0))
.SetCanBeDeferred(!!(i & 1 << 1))
.SetCanBeFrozen(!!(i & 1 << 2))
.SetCanBePaused(!!(i & 1 << 3))
.SetCanRunInBackground(!!(i & 1 << 4))
.SetCanRunWhenVirtualTimePaused(!!(i & 1 << 5))
.SetPrioritisationType(prioritisation_type);
scoped_refptr<MainThreadTaskQueue> task_queue =
frame_task_queue_controller_->GetTaskQueue(queue_traits);
EXPECT_FALSE(all_task_queues.Contains(task_queue));
all_task_queues.insert(task_queue);
EXPECT_EQ(task_queue->GetQueueTraits(), queue_traits);
EXPECT_EQ(task_queue->GetQueueTraits().prioritisation_type,
prioritisation_type);
}
// Make sure we get the same queues back, with matching QueueTraits.
EXPECT_EQ(all_task_queues.size(), kTotalUniqueQueueTraits);
for (const auto& task_queue : all_task_queues) {
scoped_refptr<MainThreadTaskQueue> returned_task_queue =
frame_task_queue_controller_->GetTaskQueue(
task_queue->GetQueueTraits());
EXPECT_EQ(task_queue->GetQueueTraits(),
returned_task_queue->GetQueueTraits());
EXPECT_TRUE(task_queue == returned_task_queue);
}
}
} // namespace scheduler
} // namespace blink