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