1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/scheduler/renderer_task_queue_selector.h"
7 #include "base/logging.h"
8 #include "base/pending_task.h"
9 #include "base/trace_event/trace_event_argument.h"
13 RendererTaskQueueSelector::RendererTaskQueueSelector() : starvation_count_(0) {
16 RendererTaskQueueSelector::~RendererTaskQueueSelector() {
19 void RendererTaskQueueSelector::RegisterWorkQueues(
20 const std::vector
<const base::TaskQueue
*>& work_queues
) {
21 DCHECK(main_thread_checker_
.CalledOnValidThread());
22 work_queues_
= work_queues
;
23 for (auto& queue_priority
: queue_priorities_
) {
24 queue_priority
.clear();
27 // By default, all work queues are set to normal priority.
28 for (size_t i
= 0; i
< work_queues
.size(); i
++) {
29 queue_priorities_
[NORMAL_PRIORITY
].insert(i
);
33 void RendererTaskQueueSelector::SetQueuePriority(size_t queue_index
,
34 QueuePriority priority
) {
35 DCHECK(main_thread_checker_
.CalledOnValidThread());
36 DCHECK_LT(queue_index
, work_queues_
.size());
37 DCHECK_LT(priority
, QUEUE_PRIORITY_COUNT
);
38 DisableQueue(queue_index
);
39 queue_priorities_
[priority
].insert(queue_index
);
42 void RendererTaskQueueSelector::EnableQueue(size_t queue_index
,
43 QueuePriority priority
) {
44 SetQueuePriority(queue_index
, priority
);
47 void RendererTaskQueueSelector::DisableQueue(size_t queue_index
) {
48 DCHECK(main_thread_checker_
.CalledOnValidThread());
49 DCHECK_LT(queue_index
, work_queues_
.size());
50 for (auto& queue_priority
: queue_priorities_
) {
51 queue_priority
.erase(queue_index
);
55 bool RendererTaskQueueSelector::IsQueueEnabled(size_t queue_index
) const {
56 DCHECK(main_thread_checker_
.CalledOnValidThread());
57 DCHECK_LT(queue_index
, work_queues_
.size());
58 for (const auto& queue_priority
: queue_priorities_
) {
59 if (queue_priority
.find(queue_index
) != queue_priority
.end())
65 bool RendererTaskQueueSelector::IsOlder(const base::TaskQueue
* queueA
,
66 const base::TaskQueue
* queueB
) {
67 // Note: the comparison is correct due to the fact that the PendingTask
68 // operator inverts its comparison operation in order to work well in a heap
69 // based priority queue.
70 return queueB
->front() < queueA
->front();
73 RendererTaskQueueSelector::QueuePriority
74 RendererTaskQueueSelector::NextPriority(QueuePriority priority
) {
75 DCHECK(priority
< QUEUE_PRIORITY_COUNT
);
76 return static_cast<QueuePriority
>(static_cast<int>(priority
) + 1);
79 bool RendererTaskQueueSelector::ChooseOldestWithPriority(
80 QueuePriority priority
,
81 size_t* out_queue_index
) const {
82 bool found_non_empty_queue
= false;
83 size_t chosen_queue
= 0;
84 for (int queue_index
: queue_priorities_
[priority
]) {
85 if (work_queues_
[queue_index
]->empty()) {
88 if (!found_non_empty_queue
||
89 IsOlder(work_queues_
[queue_index
], work_queues_
[chosen_queue
])) {
90 found_non_empty_queue
= true;
91 chosen_queue
= queue_index
;
95 if (found_non_empty_queue
) {
96 *out_queue_index
= chosen_queue
;
98 return found_non_empty_queue
;
101 bool RendererTaskQueueSelector::SelectWorkQueueToService(
102 size_t* out_queue_index
) {
103 DCHECK(main_thread_checker_
.CalledOnValidThread());
104 DCHECK(work_queues_
.size());
105 // Always service the control queue if it has any work.
106 if (ChooseOldestWithPriority(CONTROL_PRIORITY
, out_queue_index
)) {
107 DidSelectQueueWithPriority(CONTROL_PRIORITY
);
110 // Select from the normal priority queue if we are starving it.
111 if (starvation_count_
>= kMaxStarvationTasks
&&
112 ChooseOldestWithPriority(NORMAL_PRIORITY
, out_queue_index
)) {
113 DidSelectQueueWithPriority(NORMAL_PRIORITY
);
116 // Otherwise choose in priority order.
117 for (QueuePriority priority
= HIGH_PRIORITY
; priority
< QUEUE_PRIORITY_COUNT
;
118 priority
= NextPriority(priority
)) {
119 if (ChooseOldestWithPriority(priority
, out_queue_index
)) {
120 DidSelectQueueWithPriority(priority
);
127 void RendererTaskQueueSelector::DidSelectQueueWithPriority(
128 QueuePriority priority
) {
130 case CONTROL_PRIORITY
:
135 case NORMAL_PRIORITY
:
136 case BEST_EFFORT_PRIORITY
:
137 starvation_count_
= 0;
145 const char* RendererTaskQueueSelector::PriorityToString(
146 QueuePriority priority
) {
148 case CONTROL_PRIORITY
:
152 case NORMAL_PRIORITY
:
154 case BEST_EFFORT_PRIORITY
:
155 return "best_effort";
162 void RendererTaskQueueSelector::AsValueInto(
163 base::trace_event::TracedValue
* state
) const {
164 DCHECK(main_thread_checker_
.CalledOnValidThread());
165 state
->BeginDictionary("priorities");
166 for (QueuePriority priority
= FIRST_QUEUE_PRIORITY
;
167 priority
< QUEUE_PRIORITY_COUNT
; priority
= NextPriority(priority
)) {
168 state
->BeginArray(PriorityToString(priority
));
169 for (size_t queue_index
: queue_priorities_
[priority
])
170 state
->AppendInteger(queue_index
);
173 state
->EndDictionary();
174 state
->SetInteger("starvation_count", starvation_count_
);
177 } // namespace content