Commit 4ff5b671 authored by Farah Charab's avatar Farah Charab Committed by Commit Bot

Scheduling: Increase starvation scores when priorities are starved.

Modify the task queue selection logic to only increment the
starvation scores when tasks of corresponding priorities are present.

Change-Id: Ia2a290691ba29b5f0077e0a007348152fcc4e66e
Reviewed-on: https://chromium-review.googlesource.com/1027835
Commit-Queue: Farah Charab <farahcharab@chromium.org>
Reviewed-by: default avatarAlex Clarke <alexclarke@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#555702}
parent 814d076a
......@@ -313,22 +313,34 @@ void TaskQueueSelector::DidSelectQueueWithPriority(
break;
case TaskQueue::kHighestPriority:
low_priority_starvation_score_ +=
kSmallScoreIncrementForLowPriorityStarvation;
HasTasksWithPriority(TaskQueue::kLowPriority)
? kSmallScoreIncrementForLowPriorityStarvation
: 0;
normal_priority_starvation_score_ +=
kSmallScoreIncrementForNormalPriorityStarvation;
HasTasksWithPriority(TaskQueue::kNormalPriority)
? kSmallScoreIncrementForNormalPriorityStarvation
: 0;
high_priority_starvation_score_ +=
kSmallScoreIncrementForHighPriorityStarvation;
HasTasksWithPriority(TaskQueue::kHighPriority)
? kSmallScoreIncrementForHighPriorityStarvation
: 0;
break;
case TaskQueue::kHighPriority:
low_priority_starvation_score_ +=
kLargeScoreIncrementForLowPriorityStarvation;
HasTasksWithPriority(TaskQueue::kLowPriority)
? kLargeScoreIncrementForLowPriorityStarvation
: 0;
normal_priority_starvation_score_ +=
kLargeScoreIncrementForNormalPriorityStarvation;
HasTasksWithPriority(TaskQueue::kNormalPriority)
? kLargeScoreIncrementForNormalPriorityStarvation
: 0;
high_priority_starvation_score_ = 0;
break;
case TaskQueue::kNormalPriority:
low_priority_starvation_score_ +=
kLargeScoreIncrementForLowPriorityStarvation;
HasTasksWithPriority(TaskQueue::kLowPriority)
? kLargeScoreIncrementForLowPriorityStarvation
: 0;
normal_priority_starvation_score_ = 0;
break;
case TaskQueue::kLowPriority:
......@@ -383,6 +395,14 @@ void TaskQueueSelector::SetImmediateStarvationCountForTest(
immediate_starvation_count_ = immediate_starvation_count;
}
bool TaskQueueSelector::HasTasksWithPriority(
TaskQueue::QueuePriority priority) {
return !prioritizing_selector_.delayed_work_queue_sets()->IsSetEmpty(
priority) ||
!prioritizing_selector_.immediate_work_queue_sets()->IsSetEmpty(
priority);
}
} // namespace internal
} // namespace scheduler
} // namespace blink
......@@ -147,18 +147,6 @@ class PLATFORM_EXPORT TaskQueueSelector {
return &prioritizing_selector_;
}
private:
// Returns the priority which is next after |priority|.
static TaskQueue::QueuePriority NextPriority(
TaskQueue::QueuePriority priority);
bool SelectWorkQueueToServiceInternal(WorkQueue** out_work_queue);
// Called whenever the selector chooses a task queue for execution with the
// priority |priority|.
void DidSelectQueueWithPriority(TaskQueue::QueuePriority priority,
bool chose_delayed_over_immediate);
// Maximum score to accumulate before high priority tasks are run even in
// the presence of highest priority tasks.
static const size_t kMaxHighPriorityStarvationScore = 3;
......@@ -206,6 +194,20 @@ class PLATFORM_EXPORT TaskQueueSelector {
static const size_t kMaxDelayedStarvationTasks = 3;
private:
// Returns the priority which is next after |priority|.
static TaskQueue::QueuePriority NextPriority(
TaskQueue::QueuePriority priority);
bool SelectWorkQueueToServiceInternal(WorkQueue** out_work_queue);
// Called whenever the selector chooses a task queue for execution with the
// priority |priority|.
void DidSelectQueueWithPriority(TaskQueue::QueuePriority priority,
bool chose_delayed_over_immediate);
// Returns true if there are pending tasks with priority |priority|.
bool HasTasksWithPriority(TaskQueue::QueuePriority priority);
base::ThreadChecker main_thread_checker_;
PrioritizingSelector prioritizing_selector_;
......
......@@ -44,6 +44,46 @@ class TaskQueueSelectorForTest : public TaskQueueSelector {
using TaskQueueSelector::prioritizing_selector_for_test;
using TaskQueueSelector::PrioritizingSelector;
using TaskQueueSelector::SetImmediateStarvationCountForTest;
// Returns the number of highest priority tasks needed to starve high priority
// task.
static constexpr size_t NumberOfHighestPriorityToStarveHighPriority() {
return (kMaxHighPriorityStarvationScore +
kSmallScoreIncrementForHighPriorityStarvation - 1) /
kSmallScoreIncrementForHighPriorityStarvation;
}
// Returns the number of highest priority tasks needed to starve normal
// priority tasks.
static constexpr size_t NumberOfHighestPriorityToStarveNormalPriority() {
return (kMaxNormalPriorityStarvationScore +
kSmallScoreIncrementForNormalPriorityStarvation - 1) /
kSmallScoreIncrementForNormalPriorityStarvation;
}
// Returns the number of high priority tasks needed to starve normal priority
// tasks.
static constexpr size_t NumberOfHighPriorityToStarveNormalPriority() {
return (kMaxNormalPriorityStarvationScore +
kLargeScoreIncrementForNormalPriorityStarvation - 1) /
kLargeScoreIncrementForNormalPriorityStarvation;
}
// Returns the number of highest priority tasks needed to starve low priority
// ones.
static constexpr size_t NumberOfHighestPriorityToStarveLowPriority() {
return (kMaxLowPriorityStarvationScore +
kSmallScoreIncrementForLowPriorityStarvation - 1) /
kSmallScoreIncrementForLowPriorityStarvation;
}
// Returns the number of high/normal priority tasks needed to starve low
// priority ones.
static constexpr size_t NumberOfHighAndNormalPriorityToStarveLowPriority() {
return (kMaxLowPriorityStarvationScore +
kLargeScoreIncrementForLowPriorityStarvation - 1) /
kLargeScoreIncrementForLowPriorityStarvation;
}
};
class TaskQueueSelectorTest : public testing::Test {
......@@ -530,6 +570,133 @@ TEST_F(TaskQueueSelectorTest, TestBestEffortGetsStarved) {
}
}
TEST_F(TaskQueueSelectorTest,
TestHighPriorityStarvationScoreIncreasedOnlyWhenTasksArePresent) {
size_t queue_order[] = {0, 1};
PushTasks(queue_order, 2);
selector_.SetQueuePriority(task_queues_[0].get(),
TaskQueue::kHighestPriority);
selector_.SetQueuePriority(task_queues_[1].get(),
TaskQueue::kHighestPriority);
// Run a number of highest priority tasks needed to starve high priority
// tasks (when present).
for (size_t num_tasks = 0;
num_tasks <=
TaskQueueSelectorForTest::NumberOfHighestPriorityToStarveHighPriority();
num_tasks++) {
WorkQueue* chosen_work_queue = nullptr;
ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
// Don't remove task from queue to simulate the queue is still full.
}
// Post a high priority task.
selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
WorkQueue* chosen_work_queue = nullptr;
ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
// Check that the high priority task is not considered starved, and thus isn't
// processed.
EXPECT_NE(
static_cast<int>(
queue_to_index_map_.find(chosen_work_queue->task_queue())->second),
1);
}
TEST_F(TaskQueueSelectorTest,
TestNormalPriorityStarvationScoreIncreasedOnllWhenTasksArePresent) {
size_t queue_order[] = {0, 1};
PushTasks(queue_order, 2);
selector_.SetQueuePriority(task_queues_[0].get(),
TaskQueue::kHighestPriority);
selector_.SetQueuePriority(task_queues_[1].get(),
TaskQueue::kHighestPriority);
// Run a number of highest priority tasks needed to starve normal priority
// tasks (when present).
for (size_t num_tasks = 0;
num_tasks <= TaskQueueSelectorForTest::
NumberOfHighestPriorityToStarveNormalPriority();
num_tasks++) {
WorkQueue* chosen_work_queue = nullptr;
ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
// Don't remove task from queue to simulate the queue is still full.
}
selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kHighPriority);
selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
// Run a number of high priority tasks needed to starve normal priority
// tasks (when present).
for (size_t num_tasks = 0;
num_tasks <=
TaskQueueSelectorForTest::NumberOfHighPriorityToStarveNormalPriority();
num_tasks++) {
WorkQueue* chosen_work_queue = nullptr;
ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
// Don't remove task from queue to simulate the queue is still full.
}
// Post a normal priority task.
selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kNormalPriority);
WorkQueue* chosen_work_queue = nullptr;
ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
// Check that the normal priority task is not considered starved, and thus
// isn't processed.
EXPECT_NE(
static_cast<int>(
queue_to_index_map_.find(chosen_work_queue->task_queue())->second),
1);
}
TEST_F(TaskQueueSelectorTest,
TestLowPriorityTaskStarvationOnlyIncreasedWhenTasksArePresent) {
size_t queue_order[] = {0, 1};
PushTasks(queue_order, 2);
selector_.SetQueuePriority(task_queues_[0].get(),
TaskQueue::kHighestPriority);
selector_.SetQueuePriority(task_queues_[1].get(),
TaskQueue::kHighestPriority);
// Run a number of highest priority tasks needed to starve low priority
// tasks (when present).
for (size_t num_tasks = 0;
num_tasks <=
TaskQueueSelectorForTest::NumberOfHighestPriorityToStarveLowPriority();
num_tasks++) {
WorkQueue* chosen_work_queue = nullptr;
ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
// Don't remove task from queue to simulate the queue is still full.
}
selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kHighPriority);
selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kNormalPriority);
// Run a number of high/normal priority tasks needed to starve low priority
// tasks (when present).
for (size_t num_tasks = 0;
num_tasks <= TaskQueueSelectorForTest::
NumberOfHighAndNormalPriorityToStarveLowPriority();
num_tasks++) {
WorkQueue* chosen_work_queue = nullptr;
ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
// Don't remove task from queue to simulate the queue is still full.
}
// Post a low priority task.
selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kLowPriority);
WorkQueue* chosen_work_queue = nullptr;
ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
// Check that the low priority task is not considered starved, and thus
// isn't processed.
EXPECT_NE(
static_cast<int>(
queue_to_index_map_.find(chosen_work_queue->task_queue())->second),
1);
}
TEST_F(TaskQueueSelectorTest, AllEnabledWorkQueuesAreEmpty) {
EXPECT_TRUE(selector_.AllEnabledWorkQueuesAreEmpty());
size_t queue_order[] = {0, 1};
......
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