Commit 3816a275 authored by Scott Haseley's avatar Scott Haseley Committed by Commit Bot

[scheduler] Remove priority-based anti-starvation logic

This CL removes the priority-based anti-starvation logic from
TaskQueueSelector. Anti-starvation has been disabled both in the
browser and for the main thread in Blink, so this CL should not
result in any additional behavior changes.

This CL also replaces the backing data structure (SmallPriorityQueue)
with a simplified ActivePriorityTracker, which is a wrapper around an
integer representing which priorities are active.

This also cleans up various unit tests:
- Remove parameterized tests (with/without anti-starvation)
- Fix PrioritizeCompositingAndLoadingInUseCaseLoadingTest in main thread
  scheduler unit tests. This was not running because it was missing an
  INSTANTIATE_TEST_SUITE_P, and the test was missing a comma causing it
  to fail.
- Replace the TaskQueueSelector starvation unit tests and test for
  different orders of posted tasks. These were modified to be a bit more
  future proof/general and to test all priorities.
- Remove the unit tests that test the starvation score logic.


Bug: 1031336
Change-Id: I1ff8d83617d33c7510c33c08b78b1abc1a148683
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1954760
Commit-Queue: Scott Haseley <shaseley@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Reviewed-by: default avatarAlex Clarke <alexclarke@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Cr-Commit-Position: refs/heads/master@{#723517}
parent 9052759a
...@@ -50,14 +50,6 @@ SequenceManager::Settings::Builder::SetAddQueueTimeToTasks( ...@@ -50,14 +50,6 @@ SequenceManager::Settings::Builder::SetAddQueueTimeToTasks(
return *this; return *this;
} }
SequenceManager::Settings::Builder&
SequenceManager::Settings::Builder::SetAntiStarvationLogicForPrioritiesDisabled(
bool anti_starvation_logic_for_priorities_disabled_val) {
settings_.anti_starvation_logic_for_priorities_disabled =
anti_starvation_logic_for_priorities_disabled_val;
return *this;
}
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
SequenceManager::Settings::Builder& SequenceManager::Settings::Builder&
......
...@@ -92,12 +92,6 @@ class BASE_EXPORT SequenceManager { ...@@ -92,12 +92,6 @@ class BASE_EXPORT SequenceManager {
// If true, add the timestamp the task got queued to the task. // If true, add the timestamp the task got queued to the task.
bool add_queue_time_to_tasks = false; bool add_queue_time_to_tasks = false;
// If true, the scheduler will bypass the priority-based anti-starvation
// logic that prevents indefinite starvation of lower priority tasks in the
// presence of higher priority tasks by occasionally selecting lower
// priority task queues over higher priority task queues.
bool anti_starvation_logic_for_priorities_disabled = false;
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
// TODO(alexclarke): Consider adding command line flags to control these. // TODO(alexclarke): Consider adding command line flags to control these.
enum class TaskLogging { enum class TaskLogging {
...@@ -279,18 +273,6 @@ class BASE_EXPORT SequenceManager::Settings::Builder { ...@@ -279,18 +273,6 @@ class BASE_EXPORT SequenceManager::Settings::Builder {
// Whether or not queueing timestamp will be added to tasks. // Whether or not queueing timestamp will be added to tasks.
Builder& SetAddQueueTimeToTasks(bool add_queue_time_to_tasks); Builder& SetAddQueueTimeToTasks(bool add_queue_time_to_tasks);
// Sets whether priority-based anti-starvation logic is disabled. By default,
// the scheduler uses priority-based anti-starvation logic that prevents
// indefinite starvation of lower priority tasks in the presence of higher
// priority tasks by occasionally selecting lower priority task queues over
// higher priority task queues.
//
// Note: this does not affect the anti-starvation logic that is in place for
// preventing delayed tasks from starving immediate tasks, which is always
// enabled.
Builder& SetAntiStarvationLogicForPrioritiesDisabled(
bool anti_starvation_logic_for_priorities_disabled);
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
// Controls task execution logging. // Controls task execution logging.
Builder& SetTaskLogging(TaskLogging task_execution_logging); Builder& SetTaskLogging(TaskLogging task_execution_logging);
......
...@@ -80,11 +80,6 @@ enum class TestType { ...@@ -80,11 +80,6 @@ enum class TestType {
kMessagePump, kMessagePump,
}; };
enum class AntiStarvationLogic {
kEnabled,
kDisabled,
};
std::string ToString(TestType type) { std::string ToString(TestType type) {
switch (type) { switch (type) {
case TestType::kMockTaskRunner: case TestType::kMockTaskRunner:
...@@ -96,21 +91,8 @@ std::string ToString(TestType type) { ...@@ -96,21 +91,8 @@ std::string ToString(TestType type) {
} }
} }
std::string ToString(AntiStarvationLogic type) { std::string GetTestNameSuffix(const testing::TestParamInfo<TestType>& info) {
switch (type) { return StrCat({"With", ToString(info.param).substr(1)});
case AntiStarvationLogic::kEnabled:
return "AntiStarvationLogicEnabled";
case AntiStarvationLogic::kDisabled:
return "AntiStarvationLogicDisabled";
}
}
using SequenceManagerTestParams = std::pair<TestType, AntiStarvationLogic>;
std::string GetTestNameSuffix(
const testing::TestParamInfo<SequenceManagerTestParams>& info) {
return StrCat({"With", ToString(info.param.first).substr(1), "And",
ToString(info.param.second)});
} }
void PrintTo(const TestType type, std::ostream* os) { void PrintTo(const TestType type, std::ostream* os) {
...@@ -167,9 +149,6 @@ class CallCountingTickClock : public TickClock { ...@@ -167,9 +149,6 @@ class CallCountingTickClock : public TickClock {
class FixtureWithMockTaskRunner final : public Fixture { class FixtureWithMockTaskRunner final : public Fixture {
public: public:
FixtureWithMockTaskRunner() FixtureWithMockTaskRunner()
: FixtureWithMockTaskRunner(AntiStarvationLogic::kEnabled) {}
explicit FixtureWithMockTaskRunner(AntiStarvationLogic anti_starvation_logic)
: test_task_runner_(MakeRefCounted<TestMockTimeTaskRunner>( : test_task_runner_(MakeRefCounted<TestMockTimeTaskRunner>(
TestMockTimeTaskRunner::Type::kBoundToThread)), TestMockTimeTaskRunner::Type::kBoundToThread)),
call_counting_clock_(BindRepeating(&TestMockTimeTaskRunner::NowTicks, call_counting_clock_(BindRepeating(&TestMockTimeTaskRunner::NowTicks,
...@@ -182,8 +161,6 @@ class FixtureWithMockTaskRunner final : public Fixture { ...@@ -182,8 +161,6 @@ class FixtureWithMockTaskRunner final : public Fixture {
.SetMessagePumpType(MessagePumpType::DEFAULT) .SetMessagePumpType(MessagePumpType::DEFAULT)
.SetRandomisedSamplingEnabled(false) .SetRandomisedSamplingEnabled(false)
.SetTickClock(mock_tick_clock()) .SetTickClock(mock_tick_clock())
.SetAntiStarvationLogicForPrioritiesDisabled(
anti_starvation_logic == AntiStarvationLogic::kDisabled)
.Build())) { .Build())) {
// A null clock triggers some assertions. // A null clock triggers some assertions.
AdvanceMockTickClock(TimeDelta::FromMilliseconds(1)); AdvanceMockTickClock(TimeDelta::FromMilliseconds(1));
...@@ -245,8 +222,7 @@ class FixtureWithMockTaskRunner final : public Fixture { ...@@ -245,8 +222,7 @@ class FixtureWithMockTaskRunner final : public Fixture {
class FixtureWithMockMessagePump : public Fixture { class FixtureWithMockMessagePump : public Fixture {
public: public:
explicit FixtureWithMockMessagePump(AntiStarvationLogic anti_starvation_logic) explicit FixtureWithMockMessagePump() : call_counting_clock_(&mock_clock_) {
: call_counting_clock_(&mock_clock_) {
// A null clock triggers some assertions. // A null clock triggers some assertions.
mock_clock_.Advance(TimeDelta::FromMilliseconds(1)); mock_clock_.Advance(TimeDelta::FromMilliseconds(1));
...@@ -257,8 +233,6 @@ class FixtureWithMockMessagePump : public Fixture { ...@@ -257,8 +233,6 @@ class FixtureWithMockMessagePump : public Fixture {
.SetMessagePumpType(MessagePumpType::DEFAULT) .SetMessagePumpType(MessagePumpType::DEFAULT)
.SetRandomisedSamplingEnabled(false) .SetRandomisedSamplingEnabled(false)
.SetTickClock(mock_tick_clock()) .SetTickClock(mock_tick_clock())
.SetAntiStarvationLogicForPrioritiesDisabled(
anti_starvation_logic == AntiStarvationLogic::kDisabled)
.Build(); .Build();
sequence_manager_ = SequenceManagerForTest::Create( sequence_manager_ = SequenceManagerForTest::Create(
std::make_unique<ThreadControllerWithMessagePumpImpl>(std::move(pump), std::make_unique<ThreadControllerWithMessagePumpImpl>(std::move(pump),
...@@ -328,7 +302,7 @@ class FixtureWithMockMessagePump : public Fixture { ...@@ -328,7 +302,7 @@ class FixtureWithMockMessagePump : public Fixture {
class FixtureWithMessageLoop : public Fixture { class FixtureWithMessageLoop : public Fixture {
public: public:
explicit FixtureWithMessageLoop(AntiStarvationLogic anti_starvation_logic) explicit FixtureWithMessageLoop()
: call_counting_clock_(&mock_clock_), : call_counting_clock_(&mock_clock_),
auto_reset_global_clock_(&global_clock_, &call_counting_clock_) { auto_reset_global_clock_(&global_clock_, &call_counting_clock_) {
// A null clock triggers some assertions. // A null clock triggers some assertions.
...@@ -346,8 +320,6 @@ class FixtureWithMessageLoop : public Fixture { ...@@ -346,8 +320,6 @@ class FixtureWithMessageLoop : public Fixture {
.SetMessagePumpType(MessagePumpType::DEFAULT) .SetMessagePumpType(MessagePumpType::DEFAULT)
.SetRandomisedSamplingEnabled(false) .SetRandomisedSamplingEnabled(false)
.SetTickClock(mock_tick_clock()) .SetTickClock(mock_tick_clock())
.SetAntiStarvationLogicForPrioritiesDisabled(
anti_starvation_logic == AntiStarvationLogic::kDisabled)
.Build()); .Build());
// The SequenceManager constructor calls Now() once for setting up // The SequenceManager constructor calls Now() once for setting up
...@@ -422,24 +394,19 @@ TickClock* FixtureWithMessageLoop::global_clock_; ...@@ -422,24 +394,19 @@ TickClock* FixtureWithMessageLoop::global_clock_;
// Convenience wrapper around the fixtures so that we can use parametrized tests // Convenience wrapper around the fixtures so that we can use parametrized tests
// instead of templated ones. The latter would be more verbose as all method // instead of templated ones. The latter would be more verbose as all method
// calls to the fixture would need to be like this->method() // calls to the fixture would need to be like this->method()
class SequenceManagerTest class SequenceManagerTest : public testing::TestWithParam<TestType>,
: public testing::TestWithParam<SequenceManagerTestParams>, public Fixture {
public Fixture {
public: public:
SequenceManagerTest() { SequenceManagerTest() {
AntiStarvationLogic anti_starvation_logic = GetAntiStarvationLogicType();
switch (GetUnderlyingRunnerType()) { switch (GetUnderlyingRunnerType()) {
case TestType::kMockTaskRunner: case TestType::kMockTaskRunner:
fixture_ = fixture_ = std::make_unique<FixtureWithMockTaskRunner>();
std::make_unique<FixtureWithMockTaskRunner>(anti_starvation_logic);
break; break;
case TestType::kMessagePump: case TestType::kMessagePump:
fixture_ = fixture_ = std::make_unique<FixtureWithMockMessagePump>();
std::make_unique<FixtureWithMockMessagePump>(anti_starvation_logic);
break; break;
case TestType::kMessageLoop: case TestType::kMessageLoop:
fixture_ = fixture_ = std::make_unique<FixtureWithMessageLoop>();
std::make_unique<FixtureWithMessageLoop>(anti_starvation_logic);
break; break;
default: default:
NOTREACHED(); NOTREACHED();
...@@ -510,27 +477,18 @@ class SequenceManagerTest ...@@ -510,27 +477,18 @@ class SequenceManagerTest
return fixture_->GetNowTicksCallCount(); return fixture_->GetNowTicksCallCount();
} }
TestType GetUnderlyingRunnerType() { return GetParam().first; } TestType GetUnderlyingRunnerType() { return GetParam(); }
AntiStarvationLogic GetAntiStarvationLogicType() { return GetParam().second; }
private: private:
std::unique_ptr<Fixture> fixture_; std::unique_ptr<Fixture> fixture_;
}; };
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(All,
All, SequenceManagerTest,
SequenceManagerTest, testing::Values(TestType::kMockTaskRunner,
testing::Values( TestType::kMessageLoop,
std::make_pair(TestType::kMockTaskRunner, TestType::kMessagePump),
AntiStarvationLogic::kEnabled), GetTestNameSuffix);
std::make_pair(TestType::kMockTaskRunner,
AntiStarvationLogic::kDisabled),
std::make_pair(TestType::kMessageLoop, AntiStarvationLogic::kEnabled),
std::make_pair(TestType::kMessageLoop, AntiStarvationLogic::kDisabled),
std::make_pair(TestType::kMessagePump, AntiStarvationLogic::kEnabled),
std::make_pair(TestType::kMessagePump, AntiStarvationLogic::kDisabled)),
GetTestNameSuffix);
void PostFromNestedRunloop(scoped_refptr<TestTaskQueue> runner, void PostFromNestedRunloop(scoped_refptr<TestTaskQueue> runner,
std::vector<std::pair<OnceClosure, bool>>* tasks) { std::vector<std::pair<OnceClosure, bool>>* tasks) {
...@@ -4509,28 +4467,14 @@ TEST_P(SequenceManagerTest, TaskPriortyInterleaving) { ...@@ -4509,28 +4467,14 @@ TEST_P(SequenceManagerTest, TaskPriortyInterleaving) {
RunLoop().RunUntilIdle(); RunLoop().RunUntilIdle();
switch (GetAntiStarvationLogicType()) { EXPECT_EQ(order,
case AntiStarvationLogic::kDisabled: "000000000000000000000000000000000000000000000000000000000000"
EXPECT_EQ(order, "111111111111111111111111111111111111111111111111111111111111"
"000000000000000000000000000000000000000000000000000000000000" "222222222222222222222222222222222222222222222222222222222222"
"111111111111111111111111111111111111111111111111111111111111" "333333333333333333333333333333333333333333333333333333333333"
"222222222222222222222222222222222222222222222222222222222222" "444444444444444444444444444444444444444444444444444444444444"
"333333333333333333333333333333333333333333333333333333333333" "555555555555555555555555555555555555555555555555555555555555"
"444444444444444444444444444444444444444444444444444444444444" "666666666666666666666666666666666666666666666666666666666666");
"555555555555555555555555555555555555555555555555555555555555"
"666666666666666666666666666666666666666666666666666666666666");
break;
case AntiStarvationLogic::kEnabled:
EXPECT_EQ(order,
"000000000000000000000000000000000000000000000000000000000000"
"111121311214131215112314121131211151234112113121114123511211"
"312411123115121341211131211145123111211314211352232423222322"
"452322232423222352423222322423252322423222322452322232433353"
"343333334353333433333345333334333354444445444444544444454444"
"445444444544444454445555555555555555555555555555555555555555"
"666666666666666666666666666666666666666666666666666666666666");
break;
}
} }
class CancelableTaskWithDestructionObserver { class CancelableTaskWithDestructionObserver {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <utility> #include <utility>
#include "base/bits.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/task/sequence_manager/associated_thread_id.h" #include "base/task/sequence_manager/associated_thread_id.h"
#include "base/task/sequence_manager/task_queue_impl.h" #include "base/task/sequence_manager/task_queue_impl.h"
...@@ -17,8 +18,6 @@ namespace base { ...@@ -17,8 +18,6 @@ namespace base {
namespace sequence_manager { namespace sequence_manager {
namespace internal { namespace internal {
constexpr const int64_t TaskQueueSelector::per_priority_starvation_tolerance_[];
TaskQueueSelector::TaskQueueSelector( TaskQueueSelector::TaskQueueSelector(
scoped_refptr<AssociatedThreadId> associated_thread, scoped_refptr<AssociatedThreadId> associated_thread,
const SequenceManager::Settings& settings) const SequenceManager::Settings& settings)
...@@ -26,8 +25,6 @@ TaskQueueSelector::TaskQueueSelector( ...@@ -26,8 +25,6 @@ TaskQueueSelector::TaskQueueSelector(
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
random_task_selection_(settings.random_task_selection_seed != 0), random_task_selection_(settings.random_task_selection_seed != 0),
#endif #endif
anti_starvation_logic_for_priorities_disabled_(
settings.anti_starvation_logic_for_priorities_disabled),
delayed_work_queue_sets_("delayed", this, settings), delayed_work_queue_sets_("delayed", this, settings),
immediate_work_queue_sets_("immediate", this, settings) { immediate_work_queue_sets_("immediate", this, settings) {
} }
...@@ -120,30 +117,16 @@ void TaskQueueSelector::RemoveQueueImpl(internal::TaskQueueImpl* queue) { ...@@ -120,30 +117,16 @@ void TaskQueueSelector::RemoveQueueImpl(internal::TaskQueueImpl* queue) {
#endif #endif
} }
int64_t TaskQueueSelector::GetSortKeyForPriority(
TaskQueue::QueuePriority priority) const {
switch (priority) {
case TaskQueue::kControlPriority:
return std::numeric_limits<int64_t>::min();
case TaskQueue::kBestEffortPriority:
return std::numeric_limits<int64_t>::max();
default:
if (anti_starvation_logic_for_priorities_disabled_)
return per_priority_starvation_tolerance_[priority];
return selection_count_ + per_priority_starvation_tolerance_[priority];
}
}
void TaskQueueSelector::WorkQueueSetBecameEmpty(size_t set_index) { void TaskQueueSelector::WorkQueueSetBecameEmpty(size_t set_index) {
non_empty_set_counts_[set_index]--; non_empty_set_counts_[set_index]--;
DCHECK_GE(non_empty_set_counts_[set_index], 0); DCHECK_GE(non_empty_set_counts_[set_index], 0);
// There are no delayed or immediate tasks for |set_index| so remove from // There are no delayed or immediate tasks for |set_index| so remove from
// |active_priorities_|. // |active_priority_tracker_|.
if (non_empty_set_counts_[set_index] == 0) if (non_empty_set_counts_[set_index] == 0) {
active_priorities_.erase(static_cast<TaskQueue::QueuePriority>(set_index)); active_priority_tracker_.SetActive(
static_cast<TaskQueue::QueuePriority>(set_index), false);
}
} }
void TaskQueueSelector::WorkQueueSetBecameNonEmpty(size_t set_index) { void TaskQueueSelector::WorkQueueSetBecameNonEmpty(size_t set_index) {
...@@ -151,11 +134,11 @@ void TaskQueueSelector::WorkQueueSetBecameNonEmpty(size_t set_index) { ...@@ -151,11 +134,11 @@ void TaskQueueSelector::WorkQueueSetBecameNonEmpty(size_t set_index) {
DCHECK_LE(non_empty_set_counts_[set_index], kMaxNonEmptySetCount); DCHECK_LE(non_empty_set_counts_[set_index], kMaxNonEmptySetCount);
// There is now a delayed or an immediate task for |set_index|, so add to // There is now a delayed or an immediate task for |set_index|, so add to
// |active_priorities_|. // |active_priority_tracker_|.
if (non_empty_set_counts_[set_index] == 1) { if (non_empty_set_counts_[set_index] == 1) {
TaskQueue::QueuePriority priority = TaskQueue::QueuePriority priority =
static_cast<TaskQueue::QueuePriority>(set_index); static_cast<TaskQueue::QueuePriority>(set_index);
active_priorities_.insert(GetSortKeyForPriority(priority), priority); active_priority_tracker_.SetActive(priority, true);
} }
} }
...@@ -187,20 +170,16 @@ bool TaskQueueSelector::CheckContainsQueueForTest( ...@@ -187,20 +170,16 @@ bool TaskQueueSelector::CheckContainsQueueForTest(
WorkQueue* TaskQueueSelector::SelectWorkQueueToService() { WorkQueue* TaskQueueSelector::SelectWorkQueueToService() {
DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
if (active_priorities_.empty()) if (!active_priority_tracker_.HasActivePriority())
return nullptr; return nullptr;
// Select the priority from which we will select a task. Usually this is // Select the priority from which we will select a task. Usually this is
// the highest priority for which we have work, unless we are starving a lower // the highest priority for which we have work, unless we are starving a lower
// priority. // priority.
TaskQueue::QueuePriority priority = active_priorities_.min_id(); TaskQueue::QueuePriority priority =
active_priority_tracker_.HighestActivePriority();
bool chose_delayed_over_immediate; bool chose_delayed_over_immediate;
// Control tasks are allowed to indefinitely stave out other work and any
// control tasks we run should not be counted for task starvation purposes.
if (priority != TaskQueue::kControlPriority)
selection_count_++;
WorkQueue* queue = WorkQueue* queue =
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
random_task_selection_ ? ChooseWithPriority<SetOperationRandom>( random_task_selection_ ? ChooseWithPriority<SetOperationRandom>(
...@@ -209,12 +188,6 @@ WorkQueue* TaskQueueSelector::SelectWorkQueueToService() { ...@@ -209,12 +188,6 @@ WorkQueue* TaskQueueSelector::SelectWorkQueueToService() {
#endif #endif
ChooseWithPriority<SetOperationOldest>( ChooseWithPriority<SetOperationOldest>(
priority, &chose_delayed_over_immediate); priority, &chose_delayed_over_immediate);
// If we still have any tasks remaining for |priority| then adjust its sort
// key.
if (active_priorities_.IsInQueue(priority))
active_priorities_.ChangeMinKey(GetSortKeyForPriority(priority));
if (chose_delayed_over_immediate) { if (chose_delayed_over_immediate) {
immediate_starvation_count_++; immediate_starvation_count_++;
} else { } else {
...@@ -235,9 +208,9 @@ void TaskQueueSelector::SetTaskQueueSelectorObserver(Observer* observer) { ...@@ -235,9 +208,9 @@ void TaskQueueSelector::SetTaskQueueSelectorObserver(Observer* observer) {
Optional<TaskQueue::QueuePriority> Optional<TaskQueue::QueuePriority>
TaskQueueSelector::GetHighestPendingPriority() const { TaskQueueSelector::GetHighestPendingPriority() const {
DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker); DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
if (active_priorities_.empty()) if (!active_priority_tracker_.HasActivePriority())
return nullopt; return nullopt;
return active_priorities_.min_id(); return active_priority_tracker_.HighestActivePriority();
} }
void TaskQueueSelector::SetImmediateStarvationCountForTest( void TaskQueueSelector::SetImmediateStarvationCountForTest(
...@@ -251,62 +224,26 @@ bool TaskQueueSelector::HasTasksWithPriority( ...@@ -251,62 +224,26 @@ bool TaskQueueSelector::HasTasksWithPriority(
!immediate_work_queue_sets_.IsSetEmpty(priority); !immediate_work_queue_sets_.IsSetEmpty(priority);
} }
TaskQueueSelector::SmallPriorityQueue::SmallPriorityQueue() { TaskQueueSelector::ActivePriorityTracker::ActivePriorityTracker() = default;
for (size_t i = 0; i < TaskQueue::kQueuePriorityCount; i++) {
id_to_index_[i] = kInvalidIndex;
}
}
void TaskQueueSelector::SmallPriorityQueue::insert(
int64_t key,
TaskQueue::QueuePriority id) {
DCHECK_LE(size_, TaskQueue::kQueuePriorityCount);
DCHECK_LT(id, TaskQueue::kQueuePriorityCount);
DCHECK(!IsInQueue(id));
// Insert while keeping |keys_| sorted.
size_t i = size_;
while (i > 0 && key < keys_[i - 1]) {
keys_[i] = keys_[i - 1];
TaskQueue::QueuePriority moved_id = index_to_id_[i - 1];
index_to_id_[i] = moved_id;
id_to_index_[moved_id] = i;
i--;
}
keys_[i] = key;
index_to_id_[i] = id;
id_to_index_[id] = i;
size_++;
}
void TaskQueueSelector::SmallPriorityQueue::erase(TaskQueue::QueuePriority id) { void TaskQueueSelector::ActivePriorityTracker::SetActive(
DCHECK_NE(size_, 0u); TaskQueue::QueuePriority priority,
DCHECK_LT(id, TaskQueue::kQueuePriorityCount); bool is_active) {
DCHECK(IsInQueue(id)); DCHECK_LT(priority, TaskQueue::QueuePriority::kQueuePriorityCount);
// Erase while keeping |keys_| sorted. DCHECK_NE(IsActive(priority), is_active);
size_--; if (is_active) {
for (size_t i = id_to_index_[id]; i < size_; i++) { active_priorities_ |= (1u << static_cast<size_t>(priority));
keys_[i] = keys_[i + 1]; } else {
TaskQueue::QueuePriority moved_id = index_to_id_[i + 1]; active_priorities_ &= ~(1u << static_cast<size_t>(priority));
index_to_id_[i] = moved_id;
id_to_index_[moved_id] = i;
} }
id_to_index_[id] = kInvalidIndex;
} }
void TaskQueueSelector::SmallPriorityQueue::ChangeMinKey(int64_t new_key) { TaskQueue::QueuePriority
DCHECK_NE(size_, 0u); TaskQueueSelector::ActivePriorityTracker::HighestActivePriority() const {
TaskQueue::QueuePriority id = index_to_id_[0]; DCHECK_NE(active_priorities_, 0u)
size_t i = 0; << "CountTrailingZeroBits(0) has undefined behavior";
while ((i + 1) < size_ && keys_[i + 1] < new_key) { return static_cast<TaskQueue::QueuePriority>(
keys_[i] = keys_[i + 1]; bits::CountTrailingZeroBits(active_priorities_));
TaskQueue::QueuePriority moved_id = index_to_id_[i + 1];
index_to_id_[i] = moved_id;
id_to_index_[moved_id] = i;
i++;
}
keys_[i] = new_key;
index_to_id_[i] = id;
id_to_index_[id] = i;
} }
} // namespace internal } // namespace internal
......
...@@ -97,56 +97,35 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer { ...@@ -97,56 +97,35 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer {
// starved by delayed tasks. // starved by delayed tasks.
void SetImmediateStarvationCountForTest(size_t immediate_starvation_count); void SetImmediateStarvationCountForTest(size_t immediate_starvation_count);
// Maximum score to accumulate before very high priority tasks are run even in
// the presence of highest priority tasks.
static const size_t kMaxVeryHighPriorityStarvationScore = 3;
// Maximum score to accumulate before high priority tasks are run even in the
// presence of very high priority tasks.
static const size_t kMaxHighPriorityStarvationScore = 5;
// Maximum score to accumulate before normal priority tasks are run even in
// the presence of higher priority tasks i.e. highest and high priority tasks.
static const size_t kMaxNormalPriorityStarvationScore = 10;
// Maximum score to accumulate before low priority tasks are run even in the
// presence of highest, high, or normal priority tasks.
static const size_t kMaxLowPriorityStarvationScore = 15;
// Maximum number of delayed tasks tasks which can be run while there's a // Maximum number of delayed tasks tasks which can be run while there's a
// waiting non-delayed task. // waiting non-delayed task.
static const size_t kMaxDelayedStarvationTasks = 3; static const size_t kMaxDelayedStarvationTasks = 3;
// Because there are only a handful of priorities, we can get away with using // Tracks which priorities are currently active, meaning there are pending
// a very simple priority queue. This queue has a stable sorting order. // runnable tasks with that priority. Because there are only a handful of
// Note IDs must be in the range [0..TaskQueue::kQueuePriorityCount) // priorities, and because we always run tasks in order from highest to lowest
class BASE_EXPORT SmallPriorityQueue { // priority, we can use a single integer to represent enabled priorities,
// using a bit per priority.
class BASE_EXPORT ActivePriorityTracker {
public: public:
SmallPriorityQueue(); ActivePriorityTracker();
bool empty() const { return size_ == 0; }
TaskQueue::QueuePriority min_id() const { return index_to_id_[0]; } bool HasActivePriority() const { return active_priorities_ != 0; }
void insert(int64_t key, TaskQueue::QueuePriority id); bool IsActive(TaskQueue::QueuePriority priority) const {
return active_priorities_ & (1u << static_cast<size_t>(priority));
void erase(TaskQueue::QueuePriority id);
void ChangeMinKey(int64_t new_key);
bool IsInQueue(TaskQueue::QueuePriority id) const {
return id_to_index_[id] != kInvalidIndex;
} }
private: void SetActive(TaskQueue::QueuePriority priority, bool is_active);
static constexpr uint8_t kInvalidIndex = 255;
size_t size_ = 0; TaskQueue::QueuePriority HighestActivePriority() const;
// These are sorted in ascending order. private:
int64_t keys_[TaskQueue::kQueuePriorityCount]; static_assert(TaskQueue::QueuePriority::kQueuePriorityCount <
uint8_t id_to_index_[TaskQueue::kQueuePriorityCount]; sizeof(size_t) * 8,
TaskQueue::QueuePriority index_to_id_[TaskQueue::kQueuePriorityCount]; "The number of priorities must be strictly less than the "
"number of bits of |active_priorities_|!");
size_t active_priorities_ = 0;
}; };
/* /*
...@@ -257,64 +236,15 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer { ...@@ -257,64 +236,15 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer {
const bool random_task_selection_ = false; const bool random_task_selection_ = false;
#endif #endif
// If true, the scheduler will bypass the priority-based anti-starvation logic
// that prevents indefinite starvation of lower priority tasks in the presence
// of higher priority tasks by occasionally selecting lower priority task
// queues over higher priority task queues.
//
// Note: this does not affect the anti-starvation logic that is in place for
// preventing delayed tasks from starving immediate tasks, which is always
// enabled.
const bool anti_starvation_logic_for_priorities_disabled_;
// Count of the number of sets (delayed or immediate) for each priority. // Count of the number of sets (delayed or immediate) for each priority.
// Should only contain 0, 1 or 2. // Should only contain 0, 1 or 2.
std::array<int, TaskQueue::kQueuePriorityCount> non_empty_set_counts_ = {{0}}; std::array<int, TaskQueue::kQueuePriorityCount> non_empty_set_counts_ = {{0}};
static constexpr const int kMaxNonEmptySetCount = 2; static constexpr const int kMaxNonEmptySetCount = 2;
// The Priority sort key is adjusted based on these values. The idea being the // List of active priorities, which is used to work out which priority to run
// larger the adjustment, the more the queue can be starved before being // next.
// selected. The kControlPriority queues should run immediately so it always ActivePriorityTracker active_priority_tracker_;
// has the lowest possible value. Conversely kBestEffortPriority queues should
// only run if there's nothing else to do so they always have the highest
// possible value.
static constexpr const int64_t
per_priority_starvation_tolerance_[TaskQueue::kQueuePriorityCount] = {
// kControlPriority (unused)
std::numeric_limits<int64_t>::min(),
// kHighestPriority
0,
// kVeryHighPriority
kMaxVeryHighPriorityStarvationScore,
// kHighPriority
kMaxHighPriorityStarvationScore,
// kNormalPriority
kMaxNormalPriorityStarvationScore,
// kLowPriority
kMaxLowPriorityStarvationScore,
// kBestEffortPriority (unused)
std::numeric_limits<int64_t>::max()};
int64_t GetSortKeyForPriority(TaskQueue::QueuePriority priority) const;
// Min priority queue of priorities, which is used to work out which priority
// to run next.
SmallPriorityQueue active_priorities_;
// Each time we select a queue this is incremented. This forms the basis of
// the |active_priorities_| sort key. I.e. when a priority becomes selectable
// it's inserted into |active_priorities_| with a sort key of
// |selection_count_| plus an adjustment from
// |per_priority_starvation_tolerance_|. In theory this could wrap around and
// start misbehaving but in typical usage that would take a great many years.
int64_t selection_count_ = 0;
WorkQueueSets delayed_work_queue_sets_; WorkQueueSets delayed_work_queue_sets_;
WorkQueueSets immediate_work_queue_sets_; WorkQueueSets immediate_work_queue_sets_;
......
...@@ -74,7 +74,6 @@ CreateSequenceManagerForMainThreadType( ...@@ -74,7 +74,6 @@ CreateSequenceManagerForMainThreadType(
MessagePump::Create(type), MessagePump::Create(type),
base::sequence_manager::SequenceManager::Settings::Builder() base::sequence_manager::SequenceManager::Settings::Builder()
.SetMessagePumpType(type) .SetMessagePumpType(type)
.SetAntiStarvationLogicForPrioritiesDisabled(true)
.Build()); .Build());
} }
......
...@@ -55,7 +55,6 @@ BrowserIOThreadDelegate::BrowserIOThreadDelegate() ...@@ -55,7 +55,6 @@ BrowserIOThreadDelegate::BrowserIOThreadDelegate()
: owned_sequence_manager_(CreateUnboundSequenceManager( : owned_sequence_manager_(CreateUnboundSequenceManager(
SequenceManager::Settings::Builder() SequenceManager::Settings::Builder()
.SetMessagePumpType(base::MessagePumpType::IO) .SetMessagePumpType(base::MessagePumpType::IO)
.SetAntiStarvationLogicForPrioritiesDisabled(true)
.Build())), .Build())),
sequence_manager_(owned_sequence_manager_.get()) { sequence_manager_(owned_sequence_manager_.get()) {
Init(); Init();
......
...@@ -40,7 +40,6 @@ BrowserUIThreadScheduler::BrowserUIThreadScheduler() ...@@ -40,7 +40,6 @@ BrowserUIThreadScheduler::BrowserUIThreadScheduler()
base::sequence_manager::CreateUnboundSequenceManager( base::sequence_manager::CreateUnboundSequenceManager(
base::sequence_manager::SequenceManager::Settings::Builder() base::sequence_manager::SequenceManager::Settings::Builder()
.SetMessagePumpType(base::MessagePumpType::UI) .SetMessagePumpType(base::MessagePumpType::UI)
.SetAntiStarvationLogicForPrioritiesDisabled(true)
.Build())), .Build())),
task_queues_(BrowserThread::UI, task_queues_(BrowserThread::UI,
owned_sequence_manager_.get(), owned_sequence_manager_.get(),
......
...@@ -211,18 +211,6 @@ const base::Feature kPrioritizeCompositingAndLoadingDuringEarlyLoading{ ...@@ -211,18 +211,6 @@ const base::Feature kPrioritizeCompositingAndLoadingDuringEarlyLoading{
extern const char PLATFORM_EXPORT kThrottleableTaskTypesListParam[]; extern const char PLATFORM_EXPORT kThrottleableTaskTypesListParam[];
extern const char PLATFORM_EXPORT kFreezableTaskTypesListParam[]; extern const char PLATFORM_EXPORT kFreezableTaskTypesListParam[];
// If enabled, the scheduler will bypass the priority-based anti-starvation
// logic that prevents indefinite starvation of lower priority tasks in the
// presence of higher priority tasks by occasionally selecting lower
// priority task queues over higher priority task queues.
//
// Note: this does not affect the anti-starvation logic that is in place for
// preventing delayed tasks from starving immediate tasks, which is always
// enabled.
const base::Feature kBlinkSchedulerDisableAntiStarvationForPriorities{
"BlinkSchedulerDisableAntiStarvationForPriorities",
base::FEATURE_ENABLED_BY_DEFAULT};
// Enable setting high priority database task type from field trial parameters. // Enable setting high priority database task type from field trial parameters.
const base::Feature kHighPriorityDatabaseTaskType{ const base::Feature kHighPriorityDatabaseTaskType{
"HighPriorityDatabaseTaskType", base::FEATURE_DISABLED_BY_DEFAULT}; "HighPriorityDatabaseTaskType", base::FEATURE_DISABLED_BY_DEFAULT};
......
...@@ -29,9 +29,6 @@ WebThreadScheduler::CreateMainThreadScheduler( ...@@ -29,9 +29,6 @@ WebThreadScheduler::CreateMainThreadScheduler(
.SetMessagePumpType(base::MessagePumpType::DEFAULT) .SetMessagePumpType(base::MessagePumpType::DEFAULT)
.SetRandomisedSamplingEnabled(true) .SetRandomisedSamplingEnabled(true)
.SetAddQueueTimeToTasks(true) .SetAddQueueTimeToTasks(true)
.SetAntiStarvationLogicForPrioritiesDisabled(
base::FeatureList::IsEnabled(
kBlinkSchedulerDisableAntiStarvationForPriorities))
.Build(); .Build();
auto sequence_manager = auto sequence_manager =
message_pump message_pump
......
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