File: fallback_task_provider.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (231 lines) | stat: -rw-r--r-- 7,947 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/task_manager/providers/fallback_task_provider.h"

#include <vector>

#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/process/process.h"
#include "base/task/single_thread_task_runner.h"
#include "chrome/browser/task_manager/providers/render_process_host_task_provider.h"
#include "chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;

namespace task_manager {

namespace {

constexpr base::TimeDelta kTimeDelayForPendingTask = base::Milliseconds(750);

// Returns a task that is in the vector if the task in the vector shares a Pid
// with the other task.
Task* GetTaskByPidFromVector(
    base::ProcessId process_id,
    std::vector<raw_ptr<Task, VectorExperimental>>* which_vector) {
  for (Task* candidate : *which_vector) {
    if (candidate->process_id() == process_id)
      return candidate;
  }
  return nullptr;
}

}  // namespace

FallbackTaskProvider::FallbackTaskProvider(
    std::vector<std::unique_ptr<TaskProvider>> primary_subproviders,
    std::unique_ptr<TaskProvider> secondary_subprovider)
    : secondary_source_(std::make_unique<SubproviderSource>(
          this,
          std::move(secondary_subprovider))) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  for (auto& provider : primary_subproviders) {
    primary_sources_.push_back(
        std::make_unique<SubproviderSource>(this, std::move(provider)));
  }
}

FallbackTaskProvider::~FallbackTaskProvider() = default;

Task* FallbackTaskProvider::GetTaskOfUrlRequest(int child_id, int route_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  for (const auto& source : primary_sources_) {
    Task* task = source->subprovider()->GetTaskOfUrlRequest(child_id, route_id);
    if (task)
      return task;
  }

  return secondary_source_->subprovider()->GetTaskOfUrlRequest(child_id,
                                                               route_id);
}

void FallbackTaskProvider::StartUpdating() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(shown_tasks_.empty());

  for (auto& source : primary_sources_) {
    DCHECK(source->tasks()->empty());
    source->subprovider()->SetObserver(source.get());
  }

  DCHECK(secondary_source_->tasks()->empty());
  secondary_source_->subprovider()->SetObserver(secondary_source_.get());
}

void FallbackTaskProvider::StopUpdating() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  for (auto& source : primary_sources_) {
    source->subprovider()->ClearObserver();
    source->tasks()->clear();
  }

  secondary_source_->subprovider()->ClearObserver();
  secondary_source_->tasks()->clear();

  shown_tasks_.clear();
  pending_shown_tasks_.clear();
}

void FallbackTaskProvider::ShowTaskLater(Task* task) {
  auto it = pending_shown_tasks_.lower_bound(task);
  if (it == pending_shown_tasks_.end() || it->first != task) {
    it = pending_shown_tasks_.emplace_hint(it, std::piecewise_construct,
                                           std::forward_as_tuple(task),
                                           std::forward_as_tuple(this));
  } else {
    NOTREACHED();
  }

  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(&FallbackTaskProvider::ShowPendingTask,
                     it->second.GetWeakPtr(), task),
      kTimeDelayForPendingTask);
}

void FallbackTaskProvider::ShowPendingTask(Task* task) {
  // Pending tasks belong to the secondary source, and showing one means that
  // Chromium is missing a primary task provider.
  if (!allow_fallback_for_testing_) {
    // Log when we use the secondary task provider, to help drive this count to
    // zero and have providers for all known processes.
    // TODO(avi): Turn this into a DCHECK and remove the log once there are
    // providers for all known processes. See https://crbug.com/1083509.
    base::UmaHistogramBoolean("BrowserRenderProcessHost.LabeledInTaskManager",
                              false);
    LOG(ERROR)
        << "Every renderer should have at least one task provided by a primary "
        << "task provider. If a \"Renderer\" fallback task is shown, it is a "
        << "bug. If you have repro steps, please file a new bug and tag it as "
        << "a dependency of crbug.com/739782.";
  }

  pending_shown_tasks_.erase(task);
  ShowTask(task);
}

void FallbackTaskProvider::ShowTask(Task* task) {
  shown_tasks_.push_back(task);
  NotifyObserverTaskAdded(task);
}

void FallbackTaskProvider::HideTask(Task* task) {
  pending_shown_tasks_.erase(task);
  if (std::erase(shown_tasks_, task) > 0) {
    NotifyObserverTaskRemoved(task);
  }
}

void FallbackTaskProvider::OnTaskAddedBySource(Task* task,
                                               SubproviderSource* source) {
  if (source == secondary_source_.get()) {
    // If a secondary task is added but a primary task is already shown for it,
    // we can ignore showing the secondary.
    for (const auto& primary_source : primary_sources_) {
      if (GetTaskByPidFromVector(task->process_id(), primary_source->tasks()))
        return;
    }

    // Always delay showing a secondary source in case a primary source comes in
    // soon after.
    ShowTaskLater(task);
    return;
  }

  // Log when a primary task is shown instead, to provide a point of comparison
  // for cases the secondary task is shown. Remove when there are providers for
  // for all known processes. See https://crbug.com/1083509.
  base::UmaHistogramBoolean("BrowserRenderProcessHost.LabeledInTaskManager",
                            true);

  // If we get a primary task that has a secondary task that is both known and
  // shown we then hide the secondary task and then show the primary task.
  ShowTask(task);
  for (Task* secondary_task : *secondary_source_->tasks()) {
    if (task->process_id() == secondary_task->process_id())
      HideTask(secondary_task);
  }
}

void FallbackTaskProvider::OnTaskRemovedBySource(Task* task,
                                                 SubproviderSource* source) {
  HideTask(task);

  // When a task from a primary subprovider is removed, see if there are any
  // other primary tasks for that process. If not, but there are secondary
  // tasks, show them.
  if (source != secondary_source_.get()) {
    for (const auto& primary_source : primary_sources_) {
      if (GetTaskByPidFromVector(task->process_id(), primary_source->tasks()))
        return;
    }

    for (Task* secondary_task : *secondary_source_->tasks()) {
      if (task->process_id() == secondary_task->process_id())
        ShowTaskLater(secondary_task);
    }
  }
}

void FallbackTaskProvider::OnTaskUnresponsive(Task* task) {
  DCHECK(task);
  if (base::Contains(shown_tasks_, task))
    NotifyObserverTaskUnresponsive(task);
}

FallbackTaskProvider::SubproviderSource::SubproviderSource(
    FallbackTaskProvider* fallback_task_provider,
    std::unique_ptr<TaskProvider> subprovider)
    : fallback_task_provider_(fallback_task_provider),
      subprovider_(std::move(subprovider)) {}

FallbackTaskProvider::SubproviderSource::~SubproviderSource() = default;

void FallbackTaskProvider::SubproviderSource::TaskAdded(Task* task) {
  DCHECK(task);
  tasks_.push_back(task);
  fallback_task_provider_->OnTaskAddedBySource(task, this);
}

void FallbackTaskProvider::SubproviderSource::TaskRemoved(Task* task) {
  DCHECK(task);

  std::erase(tasks_, task);
  fallback_task_provider_->OnTaskRemovedBySource(task, this);
}

void FallbackTaskProvider::SubproviderSource::TaskUnresponsive(Task* task) {
  fallback_task_provider_->OnTaskUnresponsive(task);
}

}  // namespace task_manager