Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(394)

Side by Side Diff: components/scheduler/base/task_queue_selector.cc

Issue 2118903002: scheduler: Move the Blink scheduler into Blink (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "components/scheduler/base/task_queue_selector.h"
6
7 #include "base/logging.h"
8 #include "base/trace_event/trace_event_argument.h"
9 #include "components/scheduler/base/task_queue_impl.h"
10 #include "components/scheduler/base/work_queue.h"
11
12 namespace scheduler {
13 namespace internal {
14
15 TaskQueueSelector::TaskQueueSelector()
16 : enabled_selector_(this, "enabled"),
17 blocked_selector_(this, "blocked"),
18 immediate_starvation_count_(0),
19 high_priority_starvation_count_(0),
20 num_blocked_queues_to_report_(0),
21 task_queue_selector_observer_(nullptr) {}
22
23 TaskQueueSelector::~TaskQueueSelector() {}
24
25 void TaskQueueSelector::AddQueue(internal::TaskQueueImpl* queue) {
26 DCHECK(main_thread_checker_.CalledOnValidThread());
27 DCHECK(queue->IsQueueEnabled());
28 enabled_selector_.AddQueue(queue, TaskQueue::NORMAL_PRIORITY);
29 }
30
31 void TaskQueueSelector::RemoveQueue(internal::TaskQueueImpl* queue) {
32 DCHECK(main_thread_checker_.CalledOnValidThread());
33 if (queue->IsQueueEnabled()) {
34 enabled_selector_.RemoveQueue(queue);
35 // The #if DCHECK_IS_ON() shouldn't be necessary but this doesn't compile on
36 // chromeos bots without it :(
37 #if DCHECK_IS_ON()
38 DCHECK(!blocked_selector_.CheckContainsQueueForTest(queue));
39 #endif
40 } else if (queue->should_report_when_execution_blocked()) {
41 DCHECK_GT(num_blocked_queues_to_report_, 0u);
42 num_blocked_queues_to_report_--;
43 blocked_selector_.RemoveQueue(queue);
44 #if DCHECK_IS_ON()
45 DCHECK(!enabled_selector_.CheckContainsQueueForTest(queue));
46 #endif
47 }
48 }
49
50 void TaskQueueSelector::EnableQueue(internal::TaskQueueImpl* queue) {
51 DCHECK(main_thread_checker_.CalledOnValidThread());
52 DCHECK(queue->IsQueueEnabled());
53 if (queue->should_report_when_execution_blocked()) {
54 DCHECK_GT(num_blocked_queues_to_report_, 0u);
55 num_blocked_queues_to_report_--;
56 blocked_selector_.RemoveQueue(queue);
57 }
58 enabled_selector_.AddQueue(queue, queue->GetQueuePriority());
59 if (task_queue_selector_observer_)
60 task_queue_selector_observer_->OnTaskQueueEnabled(queue);
61 }
62
63 void TaskQueueSelector::DisableQueue(internal::TaskQueueImpl* queue) {
64 DCHECK(main_thread_checker_.CalledOnValidThread());
65 DCHECK(!queue->IsQueueEnabled());
66 enabled_selector_.RemoveQueue(queue);
67 if (queue->should_report_when_execution_blocked()) {
68 blocked_selector_.AddQueue(queue, queue->GetQueuePriority());
69 num_blocked_queues_to_report_++;
70 }
71 }
72
73 void TaskQueueSelector::SetQueuePriority(internal::TaskQueueImpl* queue,
74 TaskQueue::QueuePriority priority) {
75 DCHECK_LT(priority, TaskQueue::QUEUE_PRIORITY_COUNT);
76 DCHECK(main_thread_checker_.CalledOnValidThread());
77 if (queue->IsQueueEnabled()) {
78 enabled_selector_.ChangeSetIndex(queue, priority);
79 } else if (queue->should_report_when_execution_blocked()) {
80 blocked_selector_.ChangeSetIndex(queue, priority);
81 } else {
82 // Normally blocked_selector_.ChangeSetIndex would assign the queue's
83 // priority, however if |queue->should_report_when_execution_blocked()| is
84 // false then the disabled queue is not in any set so we need to do it here.
85 queue->delayed_work_queue()->AssignSetIndex(priority);
86 queue->immediate_work_queue()->AssignSetIndex(priority);
87 }
88 DCHECK_EQ(priority, queue->GetQueuePriority());
89 }
90
91 TaskQueue::QueuePriority TaskQueueSelector::NextPriority(
92 TaskQueue::QueuePriority priority) {
93 DCHECK(priority < TaskQueue::QUEUE_PRIORITY_COUNT);
94 return static_cast<TaskQueue::QueuePriority>(static_cast<int>(priority) + 1);
95 }
96
97 TaskQueueSelector::PrioritizingSelector::PrioritizingSelector(
98 TaskQueueSelector* task_queue_selector,
99 const char* name)
100 : task_queue_selector_(task_queue_selector),
101 delayed_work_queue_sets_(TaskQueue::QUEUE_PRIORITY_COUNT, name),
102 immediate_work_queue_sets_(TaskQueue::QUEUE_PRIORITY_COUNT, name) {}
103
104 void TaskQueueSelector::PrioritizingSelector::AddQueue(
105 internal::TaskQueueImpl* queue,
106 TaskQueue::QueuePriority priority) {
107 #if DCHECK_IS_ON()
108 DCHECK(!CheckContainsQueueForTest(queue));
109 #endif
110 delayed_work_queue_sets_.AddQueue(queue->delayed_work_queue(), priority);
111 immediate_work_queue_sets_.AddQueue(queue->immediate_work_queue(), priority);
112 #if DCHECK_IS_ON()
113 DCHECK(CheckContainsQueueForTest(queue));
114 #endif
115 }
116
117 void TaskQueueSelector::PrioritizingSelector::ChangeSetIndex(
118 internal::TaskQueueImpl* queue,
119 TaskQueue::QueuePriority priority) {
120 #if DCHECK_IS_ON()
121 DCHECK(CheckContainsQueueForTest(queue));
122 #endif
123 delayed_work_queue_sets_.ChangeSetIndex(queue->delayed_work_queue(),
124 priority);
125 immediate_work_queue_sets_.ChangeSetIndex(queue->immediate_work_queue(),
126 priority);
127 #if DCHECK_IS_ON()
128 DCHECK(CheckContainsQueueForTest(queue));
129 #endif
130 }
131
132 void TaskQueueSelector::PrioritizingSelector::RemoveQueue(
133 internal::TaskQueueImpl* queue) {
134 #if DCHECK_IS_ON()
135 DCHECK(CheckContainsQueueForTest(queue));
136 #endif
137 delayed_work_queue_sets_.RemoveQueue(queue->delayed_work_queue());
138 immediate_work_queue_sets_.RemoveQueue(queue->immediate_work_queue());
139
140 #if DCHECK_IS_ON()
141 DCHECK(!CheckContainsQueueForTest(queue));
142 #endif
143 }
144
145 bool TaskQueueSelector::PrioritizingSelector::
146 ChooseOldestImmediateTaskWithPriority(TaskQueue::QueuePriority priority,
147 WorkQueue** out_work_queue) const {
148 return immediate_work_queue_sets_.GetOldestQueueInSet(priority,
149 out_work_queue);
150 }
151
152 bool TaskQueueSelector::PrioritizingSelector::
153 ChooseOldestDelayedTaskWithPriority(TaskQueue::QueuePriority priority,
154 WorkQueue** out_work_queue) const {
155 return delayed_work_queue_sets_.GetOldestQueueInSet(priority, out_work_queue);
156 }
157
158 bool TaskQueueSelector::PrioritizingSelector::
159 ChooseOldestImmediateOrDelayedTaskWithPriority(
160 TaskQueue::QueuePriority priority,
161 bool* out_chose_delayed_over_immediate,
162 WorkQueue** out_work_queue) const {
163 WorkQueue* immediate_queue;
164 DCHECK_EQ(*out_chose_delayed_over_immediate, false);
165 if (immediate_work_queue_sets_.GetOldestQueueInSet(priority,
166 &immediate_queue)) {
167 WorkQueue* delayed_queue;
168 if (delayed_work_queue_sets_.GetOldestQueueInSet(priority,
169 &delayed_queue)) {
170 if (immediate_queue->ShouldRunBefore(delayed_queue)) {
171 *out_work_queue = immediate_queue;
172 } else {
173 *out_chose_delayed_over_immediate = true;
174 *out_work_queue = delayed_queue;
175 }
176 } else {
177 *out_work_queue = immediate_queue;
178 }
179 return true;
180 }
181 return delayed_work_queue_sets_.GetOldestQueueInSet(priority, out_work_queue);
182 }
183
184 bool TaskQueueSelector::PrioritizingSelector::ChooseOldestWithPriority(
185 TaskQueue::QueuePriority priority,
186 bool* out_chose_delayed_over_immediate,
187 WorkQueue** out_work_queue) const {
188 // Select an immediate work queue if we are starving immediate tasks.
189 if (task_queue_selector_->immediate_starvation_count_ >=
190 kMaxDelayedStarvationTasks) {
191 if (ChooseOldestImmediateTaskWithPriority(priority, out_work_queue)) {
192 return true;
193 }
194 if (ChooseOldestDelayedTaskWithPriority(priority, out_work_queue)) {
195 return true;
196 }
197 return false;
198 }
199 return ChooseOldestImmediateOrDelayedTaskWithPriority(
200 priority, out_chose_delayed_over_immediate, out_work_queue);
201 }
202
203 bool TaskQueueSelector::PrioritizingSelector::SelectWorkQueueToService(
204 TaskQueue::QueuePriority max_priority,
205 WorkQueue** out_work_queue,
206 bool* out_chose_delayed_over_immediate) {
207 DCHECK(task_queue_selector_->main_thread_checker_.CalledOnValidThread());
208 DCHECK_EQ(*out_chose_delayed_over_immediate, false);
209
210 // Always service the control queue if it has any work.
211 if (max_priority > TaskQueue::CONTROL_PRIORITY &&
212 ChooseOldestWithPriority(TaskQueue::CONTROL_PRIORITY,
213 out_chose_delayed_over_immediate,
214 out_work_queue)) {
215 return true;
216 }
217
218 // Select from the normal priority queue if we are starving it.
219 if (max_priority > TaskQueue::NORMAL_PRIORITY &&
220 task_queue_selector_->high_priority_starvation_count_ >=
221 kMaxHighPriorityStarvationTasks &&
222 ChooseOldestWithPriority(TaskQueue::NORMAL_PRIORITY,
223 out_chose_delayed_over_immediate,
224 out_work_queue)) {
225 return true;
226 }
227 // Otherwise choose in priority order.
228 for (TaskQueue::QueuePriority priority = TaskQueue::HIGH_PRIORITY;
229 priority < max_priority; priority = NextPriority(priority)) {
230 if (ChooseOldestWithPriority(priority, out_chose_delayed_over_immediate,
231 out_work_queue)) {
232 return true;
233 }
234 }
235 return false;
236 }
237
238 #if DCHECK_IS_ON() || !defined(NDEBUG)
239 bool
240 TaskQueueSelector::PrioritizingSelector::CheckContainsQueueForTest(
241 const internal::TaskQueueImpl* queue) const {
242 bool contains_delayed_work_queue =
243 delayed_work_queue_sets_.ContainsWorkQueueForTest(
244 queue->delayed_work_queue());
245
246 bool contains_immediate_work_queue =
247 immediate_work_queue_sets_.ContainsWorkQueueForTest(
248 queue->immediate_work_queue());
249
250 DCHECK_EQ(contains_delayed_work_queue, contains_immediate_work_queue);
251 return contains_delayed_work_queue;
252 }
253 #endif
254
255 bool TaskQueueSelector::SelectWorkQueueToService(WorkQueue** out_work_queue) {
256 DCHECK(main_thread_checker_.CalledOnValidThread());
257 bool chose_delayed_over_immediate = false;
258 bool found_queue = enabled_selector_.SelectWorkQueueToService(
259 TaskQueue::QUEUE_PRIORITY_COUNT, out_work_queue,
260 &chose_delayed_over_immediate);
261 if (!found_queue) {
262 TrySelectingBlockedQueue();
263 return false;
264 }
265
266 TrySelectingBlockedQueueOverEnabledQueue(**out_work_queue);
267 DidSelectQueueWithPriority(
268 (*out_work_queue)->task_queue()->GetQueuePriority(),
269 chose_delayed_over_immediate);
270 return true;
271 }
272
273 void TaskQueueSelector::TrySelectingBlockedQueue() {
274 DCHECK(main_thread_checker_.CalledOnValidThread());
275 if (!num_blocked_queues_to_report_ || !task_queue_selector_observer_)
276 return;
277 WorkQueue* chosen_blocked_queue;
278 bool chose_delayed_over_immediate = false;
279 // There was nothing unblocked to run, see if we could have run a blocked
280 // task.
281 if (blocked_selector_.SelectWorkQueueToService(
282 TaskQueue::QUEUE_PRIORITY_COUNT, &chosen_blocked_queue,
283 &chose_delayed_over_immediate)) {
284 task_queue_selector_observer_->OnTriedToSelectBlockedWorkQueue(
285 chosen_blocked_queue);
286 }
287 }
288
289 void TaskQueueSelector::TrySelectingBlockedQueueOverEnabledQueue(
290 const WorkQueue& chosen_enabled_queue) {
291 DCHECK(main_thread_checker_.CalledOnValidThread());
292 if (!num_blocked_queues_to_report_ || !task_queue_selector_observer_)
293 return;
294
295 TaskQueue::QueuePriority max_priority =
296 NextPriority(chosen_enabled_queue.task_queue()->GetQueuePriority());
297
298 WorkQueue* chosen_blocked_queue;
299 bool chose_delayed_over_immediate = false;
300 bool found_queue = blocked_selector_.SelectWorkQueueToService(
301 max_priority, &chosen_blocked_queue, &chose_delayed_over_immediate);
302 if (!found_queue)
303 return;
304
305 // Check if the chosen blocked queue has a lower numerical priority than the
306 // chosen enabled queue. If so we would have chosen the blocked queue (since
307 // zero is the highest priority).
308 if (chosen_blocked_queue->task_queue()->GetQueuePriority() <
309 chosen_enabled_queue.task_queue()->GetQueuePriority()) {
310 task_queue_selector_observer_->OnTriedToSelectBlockedWorkQueue(
311 chosen_blocked_queue);
312 return;
313 }
314 DCHECK_EQ(chosen_blocked_queue->task_queue()->GetQueuePriority(),
315 chosen_enabled_queue.task_queue()->GetQueuePriority());
316 // Otherwise there was an enabled and a blocked task with the same priority.
317 // The one with the older enqueue order wins.
318 if (chosen_blocked_queue->ShouldRunBefore(&chosen_enabled_queue)) {
319 task_queue_selector_observer_->OnTriedToSelectBlockedWorkQueue(
320 chosen_blocked_queue);
321 }
322 }
323
324 void TaskQueueSelector::DidSelectQueueWithPriority(
325 TaskQueue::QueuePriority priority,
326 bool chose_delayed_over_immediate) {
327 switch (priority) {
328 case TaskQueue::CONTROL_PRIORITY:
329 break;
330 case TaskQueue::HIGH_PRIORITY:
331 high_priority_starvation_count_++;
332 break;
333 case TaskQueue::NORMAL_PRIORITY:
334 case TaskQueue::BEST_EFFORT_PRIORITY:
335 high_priority_starvation_count_ = 0;
336 break;
337 default:
338 NOTREACHED();
339 }
340 if (chose_delayed_over_immediate) {
341 immediate_starvation_count_++;
342 } else {
343 immediate_starvation_count_ = 0;
344 }
345 }
346
347 void TaskQueueSelector::AsValueInto(
348 base::trace_event::TracedValue* state) const {
349 DCHECK(main_thread_checker_.CalledOnValidThread());
350 state->SetInteger("high_priority_starvation_count",
351 high_priority_starvation_count_);
352 state->SetInteger("immediate_starvation_count", immediate_starvation_count_);
353 state->SetInteger("num_blocked_queues_to_report",
354 num_blocked_queues_to_report_);
355 }
356
357 void TaskQueueSelector::SetTaskQueueSelectorObserver(Observer* observer) {
358 task_queue_selector_observer_ = observer;
359 }
360
361 bool TaskQueueSelector::EnabledWorkQueuesEmpty() const {
362 DCHECK(main_thread_checker_.CalledOnValidThread());
363 for (TaskQueue::QueuePriority priority = TaskQueue::CONTROL_PRIORITY;
364 priority < TaskQueue::QUEUE_PRIORITY_COUNT;
365 priority = NextPriority(priority)) {
366 if (!enabled_selector_.delayed_work_queue_sets()->IsSetEmpty(priority) ||
367 !enabled_selector_.immediate_work_queue_sets()->IsSetEmpty(priority)) {
368 return false;
369 }
370 }
371 return true;
372 }
373
374 void TaskQueueSelector::SetImmediateStarvationCountForTest(
375 size_t immediate_starvation_count) {
376 immediate_starvation_count_ = immediate_starvation_count;
377 }
378
379 } // namespace internal
380 } // namespace scheduler
OLDNEW
« no previous file with comments | « components/scheduler/base/task_queue_selector.h ('k') | components/scheduler/base/task_queue_selector_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698