Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "platform/scheduler/base/work_queue.h" | 5 #include "platform/scheduler/base/work_queue.h" |
| 6 | 6 |
| 7 #include "platform/scheduler/base/work_queue_sets.h" | 7 #include "platform/scheduler/base/work_queue_sets.h" |
| 8 | 8 |
| 9 namespace blink { | 9 namespace blink { |
| 10 namespace scheduler { | 10 namespace scheduler { |
| 11 namespace internal { | 11 namespace internal { |
| 12 | 12 |
| 13 WorkQueue::WorkQueue(TaskQueueImpl* task_queue, | 13 WorkQueue::WorkQueue(TaskQueueImpl* task_queue, |
| 14 const char* name, | 14 const char* name, |
| 15 TaskQueueImpl::Task::ComparatorFn comparator) | 15 TaskQueueImpl::Task::ComparatorFn comparator) |
| 16 : work_queue_(comparator), | 16 : work_queue_(comparator), |
| 17 work_queue_sets_(nullptr), | 17 work_queue_sets_(nullptr), |
| 18 task_queue_(task_queue), | 18 task_queue_(task_queue), |
| 19 work_queue_set_index_(0), | 19 work_queue_set_index_(0), |
| 20 name_(name) {} | 20 name_(name), |
| 21 fence_(0), | |
| 22 fence_hit_(false) {} | |
| 21 | 23 |
| 22 void WorkQueue::AsValueInto(base::trace_event::TracedValue* state) const { | 24 void WorkQueue::AsValueInto(base::trace_event::TracedValue* state) const { |
| 23 for (const TaskQueueImpl::Task& task : work_queue_) { | 25 for (const TaskQueueImpl::Task& task : work_queue_) { |
| 24 TaskQueueImpl::TaskAsValueInto(task, state); | 26 TaskQueueImpl::TaskAsValueInto(task, state); |
| 25 } | 27 } |
| 26 } | 28 } |
| 27 | 29 |
| 28 WorkQueue::~WorkQueue() { | 30 WorkQueue::~WorkQueue() { |
| 29 DCHECK(!work_queue_sets_) << task_queue_->GetName() << " : " | 31 DCHECK(!work_queue_sets_) << task_queue_->GetName() << " : " |
| 30 << work_queue_sets_->name() << " : " << name_; | 32 << work_queue_sets_->name() << " : " << name_; |
| 31 } | 33 } |
| 32 | 34 |
| 33 const TaskQueueImpl::Task* WorkQueue::GetFrontTask() const { | 35 const TaskQueueImpl::Task* WorkQueue::GetFrontTask() const { |
| 34 if (work_queue_.empty()) | 36 if (work_queue_.empty()) |
| 35 return nullptr; | 37 return nullptr; |
| 36 return &*work_queue_.begin(); | 38 return &*work_queue_.begin(); |
| 37 } | 39 } |
| 38 | 40 |
| 41 const TaskQueueImpl::Task* WorkQueue::GetBackTask() const { | |
| 42 if (work_queue_.empty()) | |
| 43 return nullptr; | |
| 44 return &*work_queue_.rbegin(); | |
| 45 } | |
| 46 | |
| 39 bool WorkQueue::GetFrontTaskEnqueueOrder(EnqueueOrder* enqueue_order) const { | 47 bool WorkQueue::GetFrontTaskEnqueueOrder(EnqueueOrder* enqueue_order) const { |
| 40 if (work_queue_.empty()) | 48 if (work_queue_.empty() || fence_hit_) |
| 41 return false; | 49 return false; |
| 42 // Quick sanity check. | 50 // Quick sanity check. |
| 43 DCHECK_LE(work_queue_.begin()->enqueue_order(), | 51 DCHECK_LE(work_queue_.begin()->enqueue_order(), |
| 44 work_queue_.rbegin()->enqueue_order()) | 52 work_queue_.rbegin()->enqueue_order()) |
| 45 << task_queue_->GetName() << " : " | 53 << task_queue_->GetName() << " : " |
| 46 << work_queue_sets_->name() << " : " << name_; | 54 << work_queue_sets_->name() << " : " << name_; |
| 47 *enqueue_order = work_queue_.begin()->enqueue_order(); | 55 *enqueue_order = work_queue_.begin()->enqueue_order(); |
| 48 return true; | 56 return true; |
| 49 } | 57 } |
| 50 | 58 |
| 51 void WorkQueue::Push(TaskQueueImpl::Task task) { | 59 void WorkQueue::Push(TaskQueueImpl::Task task) { |
| 52 bool was_empty = work_queue_.empty(); | 60 bool was_empty = work_queue_.empty(); |
| 53 #ifndef NDEBUG | 61 #ifndef NDEBUG |
| 54 DCHECK(task.enqueue_order_set()); | 62 DCHECK(task.enqueue_order_set()); |
| 55 #endif | 63 #endif |
| 56 | 64 |
| 57 // We expect |task| to be inserted at the end. Amoritized O(1). | 65 // We expect |task| to be inserted at the end. Amoritized O(1). |
| 58 work_queue_.insert(work_queue_.end(), std::move(task)); | 66 work_queue_.insert(work_queue_.end(), std::move(task)); |
| 59 DCHECK_EQ(task.enqueue_order(), work_queue_.rbegin()->enqueue_order()) | 67 DCHECK_EQ(task.enqueue_order(), work_queue_.rbegin()->enqueue_order()) |
| 60 << task_queue_->GetName() << " : " | 68 << task_queue_->GetName() << " : " |
| 61 << work_queue_sets_->name() << " : " << name_ | 69 << work_queue_sets_->name() << " : " << name_ |
| 62 << "task [scheduled_run_time_" << task.delayed_run_time << ", " | 70 << "task [scheduled_run_time_" << task.delayed_run_time << ", " |
| 63 << task.sequence_num << | 71 << task.sequence_num << |
| 64 "] rbegin() [" << work_queue_.rbegin()->delayed_run_time << ", " | 72 "] rbegin() [" << work_queue_.rbegin()->delayed_run_time << ", " |
| 65 << work_queue_.rbegin()->sequence_num << "]"; | 73 << work_queue_.rbegin()->sequence_num << "]"; |
| 66 | 74 |
| 67 if (was_empty && work_queue_sets_) { | 75 if (was_empty && work_queue_sets_ && !fence_hit_) { |
| 68 work_queue_sets_->OnPushQueue(this); | 76 work_queue_sets_->OnPushQueue(this); |
| 69 } | 77 } |
| 70 } | 78 } |
| 71 | 79 |
| 72 bool WorkQueue::CancelTask(const TaskQueueImpl::Task& key) { | 80 bool WorkQueue::CancelTask(const TaskQueueImpl::Task& key) { |
| 73 TaskQueueImpl::ComparatorQueue::iterator it = work_queue_.find(key); | 81 TaskQueueImpl::ComparatorQueue::iterator it = work_queue_.find(key); |
| 74 if (it == work_queue_.end()) | 82 if (it == work_queue_.end()) |
| 75 return false; | 83 return false; |
| 76 | 84 |
| 77 if (it == work_queue_.begin()) { | 85 if (it == work_queue_.begin()) { |
| 78 EnqueueOrder erased_task_enqueue_order = it->enqueue_order(); | 86 EnqueueOrder erased_task_enqueue_order = it->enqueue_order(); |
| 79 work_queue_.erase(it); | 87 work_queue_.erase(it); |
| 80 // We can't guarantee this WorkQueue is the lowest value in the WorkQueueSet | 88 // We can't guarantee this WorkQueue is the lowest value in the WorkQueueSet |
| 81 // so we need to use OnQueueHeadChanged instead of OnPopQueue for | 89 // so we need to use OnQueueHeadChanged instead of OnPopQueue for |
| 82 // correctness. | 90 // correctness. |
| 83 work_queue_sets_->OnQueueHeadChanged(this, erased_task_enqueue_order); | 91 if (!fence_hit_) |
| 92 work_queue_sets_->OnQueueHeadChanged(this, erased_task_enqueue_order); | |
| 84 } else { | 93 } else { |
| 85 work_queue_.erase(it); | 94 work_queue_.erase(it); |
| 86 } | 95 } |
| 87 task_queue_->TraceQueueSize(false); | 96 task_queue_->TraceQueueSize(false); |
| 88 return true; | 97 return true; |
| 89 } | 98 } |
| 90 | 99 |
| 91 bool WorkQueue::IsTaskPending(const TaskQueueImpl::Task& key) const { | 100 bool WorkQueue::IsTaskPending(const TaskQueueImpl::Task& key) const { |
| 92 return work_queue_.find(key) != work_queue_.end(); | 101 return work_queue_.find(key) != work_queue_.end(); |
| 93 } | 102 } |
| 94 | 103 |
| 95 void WorkQueue::PopTaskForTest() { | 104 void WorkQueue::PopTaskForTest() { |
| 96 if (work_queue_.empty()) | 105 if (work_queue_.empty()) |
| 97 return; | 106 return; |
| 98 work_queue_.erase(work_queue_.begin()); | 107 work_queue_.erase(work_queue_.begin()); |
| 99 } | 108 } |
| 100 | 109 |
| 101 void WorkQueue::SwapLocked(TaskQueueImpl::ComparatorQueue& incoming_queue) { | 110 void WorkQueue::SwapLocked(TaskQueueImpl::ComparatorQueue& incoming_queue) { |
| 102 DCHECK(work_queue_.empty()); | 111 DCHECK(work_queue_.empty()); |
| 103 std::swap(work_queue_, incoming_queue); | 112 std::swap(work_queue_, incoming_queue); |
| 104 if (!work_queue_.empty() && work_queue_sets_) | 113 if (!work_queue_.empty() && work_queue_sets_ && !fence_hit_) |
| 105 work_queue_sets_->OnPushQueue(this); | 114 work_queue_sets_->OnPushQueue(this); |
| 106 task_queue_->TraceQueueSize(true); | |
|
Sami
2016/08/26 14:37:02
Did you mean to remove this?
alex clarke (OOO till 29th)
2016/08/26 16:33:14
Yes it's not useful to call it here since Swap doe
| |
| 107 } | 115 } |
| 108 | 116 |
| 109 TaskQueueImpl::Task WorkQueue::TakeTaskFromWorkQueue() { | 117 TaskQueueImpl::Task WorkQueue::TakeTaskFromWorkQueue() { |
| 110 DCHECK(work_queue_sets_); | 118 DCHECK(work_queue_sets_); |
| 111 DCHECK(!work_queue_.empty()); | 119 DCHECK(!work_queue_.empty()); |
| 112 TaskQueueImpl::ComparatorQueue::iterator it = work_queue_.begin(); | 120 TaskQueueImpl::ComparatorQueue::iterator it = work_queue_.begin(); |
| 113 TaskQueueImpl::Task pending_task = | 121 TaskQueueImpl::Task pending_task = |
| 114 std::move(const_cast<TaskQueueImpl::Task&>(*it)); | 122 std::move(const_cast<TaskQueueImpl::Task&>(*it)); |
| 115 work_queue_.erase(it); | 123 work_queue_.erase(it); |
| 124 // If we hit the fence, pretend to WorkQueueSets that we're empty until the | |
| 125 // fence is removed. | |
| 126 if (fence_ && pending_task.enqueue_order() >= fence_) | |
| 127 fence_hit_ = true; | |
| 116 work_queue_sets_->OnPopQueue(this); | 128 work_queue_sets_->OnPopQueue(this); |
| 117 task_queue_->TraceQueueSize(false); | 129 task_queue_->TraceQueueSize(false); |
| 118 return pending_task; | 130 return pending_task; |
| 119 } | 131 } |
| 120 | 132 |
| 121 void WorkQueue::AssignToWorkQueueSets(WorkQueueSets* work_queue_sets) { | 133 void WorkQueue::AssignToWorkQueueSets(WorkQueueSets* work_queue_sets) { |
| 122 work_queue_sets_ = work_queue_sets; | 134 work_queue_sets_ = work_queue_sets; |
| 123 } | 135 } |
| 124 | 136 |
| 125 void WorkQueue::AssignSetIndex(size_t work_queue_set_index) { | 137 void WorkQueue::AssignSetIndex(size_t work_queue_set_index) { |
| 126 work_queue_set_index_ = work_queue_set_index; | 138 work_queue_set_index_ = work_queue_set_index; |
| 127 } | 139 } |
| 128 | 140 |
| 141 bool WorkQueue::InsertFence(EnqueueOrder fence) { | |
| 142 bool task_unblocked = false; | |
| 143 if (fence) { | |
| 144 DCHECK_GE(fence, fence_); | |
| 145 if (fence > fence_) | |
| 146 task_unblocked = RemoveFence(); | |
| 147 } else { | |
| 148 DCHECK(work_queue_.empty()); | |
| 149 // Until the fence is removed we should pretend to remain empty even after a | |
| 150 // push or a swap. | |
| 151 fence_hit_ = true; | |
| 152 } | |
| 153 | |
| 154 fence_ = fence; | |
| 155 return task_unblocked; | |
| 156 } | |
| 157 | |
| 158 bool WorkQueue::RemoveFence() { | |
| 159 fence_ = 0; | |
| 160 bool fence_was_hit = fence_hit_; | |
| 161 fence_hit_ = false; | |
| 162 if (work_queue_sets_ && fence_was_hit && !work_queue_.empty()) { | |
| 163 work_queue_sets_->OnPushQueue(this); | |
| 164 return true; | |
| 165 } | |
| 166 return false; | |
| 167 } | |
| 168 | |
| 129 bool WorkQueue::ShouldRunBefore(const WorkQueue* other_queue) const { | 169 bool WorkQueue::ShouldRunBefore(const WorkQueue* other_queue) const { |
| 130 DCHECK(!work_queue_.empty()); | 170 DCHECK(!work_queue_.empty()); |
| 131 DCHECK(!other_queue->work_queue_.empty()); | 171 DCHECK(!other_queue->work_queue_.empty()); |
| 132 EnqueueOrder enqueue_order = 0; | 172 EnqueueOrder enqueue_order = 0; |
| 133 EnqueueOrder other_enqueue_order = 0; | 173 EnqueueOrder other_enqueue_order = 0; |
| 134 bool have_task = GetFrontTaskEnqueueOrder(&enqueue_order); | 174 bool have_task = GetFrontTaskEnqueueOrder(&enqueue_order); |
| 135 bool have_other_task = | 175 bool have_other_task = |
| 136 other_queue->GetFrontTaskEnqueueOrder(&other_enqueue_order); | 176 other_queue->GetFrontTaskEnqueueOrder(&other_enqueue_order); |
| 137 DCHECK(have_task); | 177 DCHECK(have_task); |
| 138 DCHECK(have_other_task); | 178 DCHECK(have_other_task); |
| 139 return enqueue_order < other_enqueue_order; | 179 return enqueue_order < other_enqueue_order; |
| 140 } | 180 } |
| 141 | 181 |
| 142 } // namespace internal | 182 } // namespace internal |
| 143 } // namespace scheduler | 183 } // namespace scheduler |
| 144 } // namespace blink | 184 } // namespace blink |
| OLD | NEW |