1 // Copyright 2017 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 "chrome/browser/task_manager/providers/fallback_task_provider.h"
6
7 #include "base/bind.h"
8 #include "base/process/process.h"
9 #include "base/stl_util.h"
10 #include "base/threading/thread_task_runner_handle.h"
11 #include "chrome/browser/task_manager/providers/render_process_host_task_provider.h"
12 #include "chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.h"
13 #include "content/public/browser/browser_thread.h"
14
15 using content::BrowserThread;
16
17 namespace task_manager {
18
19 namespace {
20
21 constexpr base::TimeDelta kTimeDelayForPendingTask =
22 base::TimeDelta::FromMilliseconds(750);
23
24 // Returns a task that is in the vector if the task in the vector shares a Pid
25 // with the other task.
GetTaskByPidFromVector(base::ProcessId process_id,std::vector<Task * > * which_vector)26 Task* GetTaskByPidFromVector(base::ProcessId process_id,
27 std::vector<Task*>* which_vector) {
28 for (Task* candidate : *which_vector) {
29 if (candidate->process_id() == process_id)
30 return candidate;
31 }
32 return nullptr;
33 }
34
35 } // namespace
36
FallbackTaskProvider(std::vector<std::unique_ptr<TaskProvider>> primary_subproviders,std::unique_ptr<TaskProvider> secondary_subprovider)37 FallbackTaskProvider::FallbackTaskProvider(
38 std::vector<std::unique_ptr<TaskProvider>> primary_subproviders,
39 std::unique_ptr<TaskProvider> secondary_subprovider)
40 : secondary_source_(std::make_unique<SubproviderSource>(
41 this,
42 std::move(secondary_subprovider))) {
43 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
44
45 for (auto& provider : primary_subproviders) {
46 primary_sources_.push_back(
47 std::make_unique<SubproviderSource>(this, std::move(provider)));
48 }
49 }
50
~FallbackTaskProvider()51 FallbackTaskProvider::~FallbackTaskProvider() {}
52
GetTaskOfUrlRequest(int child_id,int route_id)53 Task* FallbackTaskProvider::GetTaskOfUrlRequest(int child_id, int route_id) {
54 DCHECK_CURRENTLY_ON(BrowserThread::UI);
55
56 for (const auto& source : primary_sources_) {
57 Task* task = source->subprovider()->GetTaskOfUrlRequest(child_id, route_id);
58 if (task)
59 return task;
60 }
61
62 return secondary_source_->subprovider()->GetTaskOfUrlRequest(child_id,
63 route_id);
64 }
65
StartUpdating()66 void FallbackTaskProvider::StartUpdating() {
67 DCHECK_CURRENTLY_ON(BrowserThread::UI);
68 DCHECK(shown_tasks_.empty());
69
70 for (auto& source : primary_sources_) {
71 DCHECK(source->tasks()->empty());
72 source->subprovider()->SetObserver(source.get());
73 }
74
75 DCHECK(secondary_source_->tasks()->empty());
76 secondary_source_->subprovider()->SetObserver(secondary_source_.get());
77 }
78
StopUpdating()79 void FallbackTaskProvider::StopUpdating() {
80 DCHECK_CURRENTLY_ON(BrowserThread::UI);
81
82 for (auto& source : primary_sources_) {
83 source->subprovider()->ClearObserver();
84 source->tasks()->clear();
85 }
86
87 secondary_source_->subprovider()->ClearObserver();
88 secondary_source_->tasks()->clear();
89
90 shown_tasks_.clear();
91 pending_shown_tasks_.clear();
92 }
93
ShowTaskLater(Task * task)94 void FallbackTaskProvider::ShowTaskLater(Task* task) {
95 auto it = pending_shown_tasks_.lower_bound(task);
96 if (it == pending_shown_tasks_.end() || it->first != task) {
97 it = pending_shown_tasks_.emplace_hint(it, std::piecewise_construct,
98 std::forward_as_tuple(task),
99 std::forward_as_tuple(this));
100 } else {
101 NOTREACHED();
102 it->second.InvalidateWeakPtrs();
103 }
104
105 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
106 FROM_HERE,
107 base::BindOnce(&FallbackTaskProvider::ShowPendingTask,
108 it->second.GetWeakPtr(), task),
109 kTimeDelayForPendingTask);
110 }
111
ShowPendingTask(Task * task)112 void FallbackTaskProvider::ShowPendingTask(Task* task) {
113 // Pending tasks belong to the secondary source, and showing one means that
114 // Chromium is missing a primary task provider.
115 if (!allow_fallback_for_testing_) {
116 // TODO(avi): Turn this into a DCHECK once there are providers for all known
117 // processes. https://crbug.com/1083509
118 LOG(ERROR)
119 << "Every renderer should have at least one task provided by a primary "
120 << "task provider. If a fallback task is shown, it is a bug. Please "
121 << "file a new bug and tag it as a dependency of crbug.com/739782.";
122 }
123
124 pending_shown_tasks_.erase(task);
125 ShowTask(task);
126 }
127
ShowTask(Task * task)128 void FallbackTaskProvider::ShowTask(Task* task) {
129 shown_tasks_.push_back(task);
130 NotifyObserverTaskAdded(task);
131 }
132
HideTask(Task * task)133 void FallbackTaskProvider::HideTask(Task* task) {
134 auto it = std::remove(shown_tasks_.begin(), shown_tasks_.end(), task);
135 pending_shown_tasks_.erase(task);
136 if (it != shown_tasks_.end()) {
137 shown_tasks_.erase(it, shown_tasks_.end());
138 NotifyObserverTaskRemoved(task);
139 }
140 }
141
OnTaskAddedBySource(Task * task,SubproviderSource * source)142 void FallbackTaskProvider::OnTaskAddedBySource(Task* task,
143 SubproviderSource* source) {
144 if (source == secondary_source_.get()) {
145 // If a secondary task is added but a primary task is already shown for it,
146 // we can ignore showing the secondary.
147 for (const auto& primary_source : primary_sources_) {
148 if (GetTaskByPidFromVector(task->process_id(), primary_source->tasks()))
149 return;
150 }
151
152 // Always delay showing a secondary source in case a primary source comes in
153 // soon after.
154 ShowTaskLater(task);
155 return;
156 }
157
158 // If we get a primary task that has a secondary task that is both known and
159 // shown we then hide the secondary task and then show the primary task.
160 ShowTask(task);
161 for (Task* secondary_task : *secondary_source_->tasks()) {
162 if (task->process_id() == secondary_task->process_id())
163 HideTask(secondary_task);
164 }
165 }
166
OnTaskRemovedBySource(Task * task,SubproviderSource * source)167 void FallbackTaskProvider::OnTaskRemovedBySource(Task* task,
168 SubproviderSource* source) {
169 HideTask(task);
170
171 // When a task from a primary subprovider is removed, see if there are any
172 // other primary tasks for that process. If not, but there are secondary
173 // tasks, show them.
174 if (source != secondary_source_.get()) {
175 for (const auto& primary_source : primary_sources_) {
176 if (GetTaskByPidFromVector(task->process_id(), primary_source->tasks()))
177 return;
178 }
179
180 for (Task* secondary_task : *secondary_source_->tasks()) {
181 if (task->process_id() == secondary_task->process_id())
182 ShowTaskLater(secondary_task);
183 }
184 }
185 }
186
OnTaskUnresponsive(Task * task)187 void FallbackTaskProvider::OnTaskUnresponsive(Task* task) {
188 DCHECK(task);
189 if (base::Contains(shown_tasks_, task))
190 NotifyObserverTaskUnresponsive(task);
191 }
192
SubproviderSource(FallbackTaskProvider * fallback_task_provider,std::unique_ptr<TaskProvider> subprovider)193 FallbackTaskProvider::SubproviderSource::SubproviderSource(
194 FallbackTaskProvider* fallback_task_provider,
195 std::unique_ptr<TaskProvider> subprovider)
196 : fallback_task_provider_(fallback_task_provider),
197 subprovider_(std::move(subprovider)) {}
198
~SubproviderSource()199 FallbackTaskProvider::SubproviderSource::~SubproviderSource() {}
200
TaskAdded(Task * task)201 void FallbackTaskProvider::SubproviderSource::TaskAdded(Task* task) {
202 DCHECK(task);
203 tasks_.push_back(task);
204 fallback_task_provider_->OnTaskAddedBySource(task, this);
205 }
206
TaskRemoved(Task * task)207 void FallbackTaskProvider::SubproviderSource::TaskRemoved(Task* task) {
208 DCHECK(task);
209
210 base::Erase(tasks_, task);
211 fallback_task_provider_->OnTaskRemovedBySource(task, this);
212 }
213
TaskUnresponsive(Task * task)214 void FallbackTaskProvider::SubproviderSource::TaskUnresponsive(Task* task) {
215 fallback_task_provider_->OnTaskUnresponsive(task);
216 }
217
218 } // namespace task_manager
219