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

Side by Side Diff: content/renderer/scheduler/task_queue_manager.cc

Issue 966813003: [content]: Ensure TaskQueueManager AFTER_WAKEUP autopump policy wakes only on newer tasks. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add check for null tasks Created 5 years, 9 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/scheduler/task_queue_manager.h" 5 #include "content/renderer/scheduler/task_queue_manager.h"
6 6
7 #include <queue> 7 #include <queue>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/trace_event/trace_event.h" 10 #include "base/trace_event/trace_event.h"
(...skipping 25 matching lines...) Expand all
36 base::TimeDelta delay) override { 36 base::TimeDelta delay) override {
37 return PostDelayedTaskImpl(from_here, task, delay, TaskType::NON_NESTABLE); 37 return PostDelayedTaskImpl(from_here, task, delay, TaskType::NON_NESTABLE);
38 } 38 }
39 39
40 bool IsQueueEmpty() const; 40 bool IsQueueEmpty() const;
41 41
42 void SetPumpPolicy(TaskQueueManager::PumpPolicy pump_policy); 42 void SetPumpPolicy(TaskQueueManager::PumpPolicy pump_policy);
43 void PumpQueue(); 43 void PumpQueue();
44 44
45 bool UpdateWorkQueue(base::TimeTicks* next_pending_delayed_task, 45 bool UpdateWorkQueue(base::TimeTicks* next_pending_delayed_task,
46 TaskQueueManager::WorkQueueUpdateEventType event_type); 46 const base::PendingTask& previous_task);
47 base::PendingTask TakeTaskFromWorkQueue(); 47 base::PendingTask TakeTaskFromWorkQueue();
48 48
49 void WillDeleteTaskQueueManager(); 49 void WillDeleteTaskQueueManager();
50 50
51 base::TaskQueue& work_queue() { return work_queue_; } 51 base::TaskQueue& work_queue() { return work_queue_; }
52 52
53 void set_name(const char* name) { name_ = name; } 53 void set_name(const char* name) { name_ = name; }
54 54
55 void AsValueInto(base::trace_event::TracedValue* state) const; 55 void AsValueInto(base::trace_event::TracedValue* state) const;
56 56
57 private: 57 private:
58 enum class TaskType { 58 enum class TaskType {
59 NORMAL, 59 NORMAL,
60 NON_NESTABLE, 60 NON_NESTABLE,
61 }; 61 };
62 62
63 ~TaskQueue() override; 63 ~TaskQueue() override;
64 64
65 bool PostDelayedTaskImpl(const tracked_objects::Location& from_here, 65 bool PostDelayedTaskImpl(const tracked_objects::Location& from_here,
66 const base::Closure& task, 66 const base::Closure& task,
67 base::TimeDelta delay, 67 base::TimeDelta delay,
68 TaskType task_type); 68 TaskType task_type);
69 69
70 // Adds a task at the end of the incoming task queue and schedules a call to 70 // Adds a task at the end of the incoming task queue and schedules a call to
71 // TaskQueueManager::DoWork() if the incoming queue was empty and automatic 71 // TaskQueueManager::DoWork() if the incoming queue was empty and automatic
72 // pumping is enabled. Can be called on an arbitrary thread. 72 // pumping is enabled. Can be called on an arbitrary thread.
73 void EnqueueTask(const base::PendingTask& pending_task); 73 void EnqueueTask(const base::PendingTask& pending_task);
74 74
75 void PumpQueueLocked(); 75 void PumpQueueLocked();
76 bool ShouldAutoPumpQueueLocked( 76 bool TaskIsOlderThanQueuedTasks(const base::PendingTask& task);
77 TaskQueueManager::WorkQueueUpdateEventType event_type); 77 bool ShouldAutoPumpQueueLocked(const base::PendingTask& previous_task);
78 void EnqueueTaskLocked(const base::PendingTask& pending_task); 78 void EnqueueTaskLocked(const base::PendingTask& pending_task);
79 79
80 void TraceQueueSize(bool is_locked) const; 80 void TraceQueueSize(bool is_locked) const;
81 static const char* PumpPolicyToString( 81 static const char* PumpPolicyToString(
82 TaskQueueManager::PumpPolicy pump_policy); 82 TaskQueueManager::PumpPolicy pump_policy);
83 static void QueueAsValueInto(const base::TaskQueue& queue, 83 static void QueueAsValueInto(const base::TaskQueue& queue,
84 base::trace_event::TracedValue* state); 84 base::trace_event::TracedValue* state);
85 static void TaskAsValueInto(const base::PendingTask& task, 85 static void TaskAsValueInto(const base::PendingTask& task,
86 base::trace_event::TracedValue* state); 86 base::trace_event::TracedValue* state);
87 87
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
146 bool TaskQueue::IsQueueEmpty() const { 146 bool TaskQueue::IsQueueEmpty() const {
147 if (!work_queue_.empty()) 147 if (!work_queue_.empty())
148 return false; 148 return false;
149 149
150 { 150 {
151 base::AutoLock lock(lock_); 151 base::AutoLock lock(lock_);
152 return incoming_queue_.empty(); 152 return incoming_queue_.empty();
153 } 153 }
154 } 154 }
155 155
156 bool TaskQueue::TaskIsOlderThanQueuedTasks(const base::PendingTask& task) {
157 lock_.AssertAcquired();
158 if (incoming_queue_.empty())
159 return false;
160
161 // Always return true if passed a NULL task.
162 if (task.sequence_num == 0)
163 return true;
rmcilroy 2015/03/02 11:44:13 I realized that the previous patch set might incor
Sami 2015/03/02 12:28:56 Note that the sequence number starts at zero so th
rmcilroy 2015/03/02 15:34:51 Yeah that would probably be better - done.
164
165 base::PendingTask oldest_queued_task = incoming_queue_.front();
166
167 // Note: the comparison is correct due to the fact that the PendingTask
168 // operator inverts its comparison operation in order to work well in a heap
169 // based priority queue.
170 return oldest_queued_task < task;
Sami 2015/03/02 12:28:56 We should really go and swap that operator around
rmcilroy 2015/03/02 15:34:51 This would break the use of priority queues - we s
Sami 2015/03/02 15:55:57 We could just define the priority queue type with
rmcilroy 2015/03/02 16:47:52 Hmm, good point :). I'll do this as a followup cha
171 }
172
156 bool TaskQueue::ShouldAutoPumpQueueLocked( 173 bool TaskQueue::ShouldAutoPumpQueueLocked(
157 TaskQueueManager::WorkQueueUpdateEventType event_type) { 174 const base::PendingTask& previous_task) {
158 lock_.AssertAcquired(); 175 lock_.AssertAcquired();
159 if (pump_policy_ == TaskQueueManager::PumpPolicy::MANUAL) 176 if (pump_policy_ == TaskQueueManager::PumpPolicy::MANUAL)
160 return false; 177 return false;
161 if (pump_policy_ == TaskQueueManager::PumpPolicy::AFTER_WAKEUP && 178 if (pump_policy_ == TaskQueueManager::PumpPolicy::AFTER_WAKEUP &&
162 event_type != TaskQueueManager::WorkQueueUpdateEventType::AFTER_WAKEUP) 179 TaskIsOlderThanQueuedTasks(previous_task))
163 return false; 180 return false;
164 if (incoming_queue_.empty()) 181 if (incoming_queue_.empty())
165 return false; 182 return false;
166 return true; 183 return true;
167 } 184 }
168 185
169 bool TaskQueue::UpdateWorkQueue( 186 bool TaskQueue::UpdateWorkQueue(
170 base::TimeTicks* next_pending_delayed_task, 187 base::TimeTicks* next_pending_delayed_task,
171 TaskQueueManager::WorkQueueUpdateEventType event_type) { 188 const base::PendingTask& previous_task) {
172 if (!work_queue_.empty()) 189 if (!work_queue_.empty())
173 return true; 190 return true;
174 191
175 { 192 {
176 base::AutoLock lock(lock_); 193 base::AutoLock lock(lock_);
177 if (!delayed_task_run_times_.empty()) { 194 if (!delayed_task_run_times_.empty()) {
178 *next_pending_delayed_task = 195 *next_pending_delayed_task =
179 std::min(*next_pending_delayed_task, delayed_task_run_times_.top()); 196 std::min(*next_pending_delayed_task, delayed_task_run_times_.top());
180 } 197 }
181 if (!ShouldAutoPumpQueueLocked(event_type)) 198 if (!ShouldAutoPumpQueueLocked(previous_task))
182 return false; 199 return false;
183 work_queue_.Swap(&incoming_queue_); 200 work_queue_.Swap(&incoming_queue_);
184 TraceQueueSize(true); 201 TraceQueueSize(true);
185 return true; 202 return true;
186 } 203 }
187 } 204 }
188 205
189 base::PendingTask TaskQueue::TakeTaskFromWorkQueue() { 206 base::PendingTask TaskQueue::TakeTaskFromWorkQueue() {
190 base::PendingTask pending_task = work_queue_.front(); 207 base::PendingTask pending_task = work_queue_.front();
191 work_queue_.pop(); 208 work_queue_.pop();
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
376 } 393 }
377 394
378 void TaskQueueManager::PumpQueue(size_t queue_index) { 395 void TaskQueueManager::PumpQueue(size_t queue_index) {
379 DCHECK(main_thread_checker_.CalledOnValidThread()); 396 DCHECK(main_thread_checker_.CalledOnValidThread());
380 internal::TaskQueue* queue = Queue(queue_index); 397 internal::TaskQueue* queue = Queue(queue_index);
381 queue->PumpQueue(); 398 queue->PumpQueue();
382 } 399 }
383 400
384 bool TaskQueueManager::UpdateWorkQueues( 401 bool TaskQueueManager::UpdateWorkQueues(
385 base::TimeTicks* next_pending_delayed_task, 402 base::TimeTicks* next_pending_delayed_task,
386 WorkQueueUpdateEventType event_type) { 403 const base::PendingTask& previous_task) {
387 // TODO(skyostil): This is not efficient when the number of queues grows very 404 // TODO(skyostil): This is not efficient when the number of queues grows very
388 // large due to the number of locks taken. Consider optimizing when we get 405 // large due to the number of locks taken. Consider optimizing when we get
389 // there. 406 // there.
390 DCHECK(main_thread_checker_.CalledOnValidThread()); 407 DCHECK(main_thread_checker_.CalledOnValidThread());
391 bool has_work = false; 408 bool has_work = false;
392 for (auto& queue : queues_) { 409 for (auto& queue : queues_) {
393 has_work |= queue->UpdateWorkQueue(next_pending_delayed_task, event_type); 410 has_work |= queue->UpdateWorkQueue(next_pending_delayed_task,
411 previous_task);
394 if (!queue->work_queue().empty()) { 412 if (!queue->work_queue().empty()) {
395 // Currently we should not be getting tasks with delayed run times in any 413 // Currently we should not be getting tasks with delayed run times in any
396 // of the work queues. 414 // of the work queues.
397 DCHECK(queue->work_queue().front().delayed_run_time.is_null()); 415 DCHECK(queue->work_queue().front().delayed_run_time.is_null());
398 } 416 }
399 } 417 }
400 return has_work; 418 return has_work;
401 } 419 }
402 420
403 void TaskQueueManager::MaybePostDoWorkOnMainRunner() { 421 void TaskQueueManager::MaybePostDoWorkOnMainRunner() {
(...skipping 14 matching lines...) Expand all
418 436
419 void TaskQueueManager::DoWork(bool posted_from_main_thread) { 437 void TaskQueueManager::DoWork(bool posted_from_main_thread) {
420 if (posted_from_main_thread) { 438 if (posted_from_main_thread) {
421 pending_dowork_count_--; 439 pending_dowork_count_--;
422 DCHECK_GE(pending_dowork_count_, 0); 440 DCHECK_GE(pending_dowork_count_, 0);
423 } 441 }
424 DCHECK(main_thread_checker_.CalledOnValidThread()); 442 DCHECK(main_thread_checker_.CalledOnValidThread());
425 443
426 base::TimeTicks next_pending_delayed_task( 444 base::TimeTicks next_pending_delayed_task(
427 base::TimeTicks::FromInternalValue(kMaxTimeTicks)); 445 base::TimeTicks::FromInternalValue(kMaxTimeTicks));
446 base::PendingTask previous_task((tracked_objects::Location()),
447 (base::Closure()));
428 448
429 if (!UpdateWorkQueues(&next_pending_delayed_task, 449 if (!UpdateWorkQueues(&next_pending_delayed_task, previous_task))
430 WorkQueueUpdateEventType::BEFORE_WAKEUP))
431 return; 450 return;
432 451
433 base::PendingTask previous_task((tracked_objects::Location()),
434 (base::Closure()));
435 for (int i = 0; i < work_batch_size_; i++) { 452 for (int i = 0; i < work_batch_size_; i++) {
436 // Interrupt the work batch if we should run the next delayed task. 453 // Interrupt the work batch if we should run the next delayed task.
437 if (i > 0 && next_pending_delayed_task.ToInternalValue() != kMaxTimeTicks && 454 if (i > 0 && next_pending_delayed_task.ToInternalValue() != kMaxTimeTicks &&
438 Now() >= next_pending_delayed_task) 455 Now() >= next_pending_delayed_task)
439 return; 456 return;
440 457
441 size_t queue_index; 458 size_t queue_index;
442 if (!SelectWorkQueueToService(&queue_index)) 459 if (!SelectWorkQueueToService(&queue_index))
443 return; 460 return;
444 // Note that this function won't post another call to DoWork if one is 461 // Note that this function won't post another call to DoWork if one is
445 // already pending, so it is safe to call it in a loop. 462 // already pending, so it is safe to call it in a loop.
446 MaybePostDoWorkOnMainRunner(); 463 MaybePostDoWorkOnMainRunner();
447 ProcessTaskFromWorkQueue(queue_index, i > 0, &previous_task); 464 ProcessTaskFromWorkQueue(queue_index, i > 0, &previous_task);
448 465
449 if (!UpdateWorkQueues(&next_pending_delayed_task, 466 if (!UpdateWorkQueues(&next_pending_delayed_task, previous_task))
450 WorkQueueUpdateEventType::AFTER_WAKEUP))
451 return; 467 return;
452 } 468 }
453 } 469 }
454 470
455 bool TaskQueueManager::SelectWorkQueueToService(size_t* out_queue_index) { 471 bool TaskQueueManager::SelectWorkQueueToService(size_t* out_queue_index) {
456 bool should_run = selector_->SelectWorkQueueToService(out_queue_index); 472 bool should_run = selector_->SelectWorkQueueToService(out_queue_index);
457 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID( 473 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
458 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "TaskQueueManager", this, 474 TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"), "TaskQueueManager", this,
459 AsValueWithSelectorResult(should_run, *out_queue_index)); 475 AsValueWithSelectorResult(should_run, *out_queue_index));
460 return should_run; 476 return should_run;
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 state->EndArray(); 570 state->EndArray();
555 state->BeginDictionary("selector"); 571 state->BeginDictionary("selector");
556 selector_->AsValueInto(state.get()); 572 selector_->AsValueInto(state.get());
557 state->EndDictionary(); 573 state->EndDictionary();
558 if (should_run) 574 if (should_run)
559 state->SetInteger("selected_queue", selected_queue); 575 state->SetInteger("selected_queue", selected_queue);
560 return state; 576 return state;
561 } 577 }
562 578
563 } // namespace content 579 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698