blob: f510b479d860d3006c1118229620e2567f16aff6 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// 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 "base/bind.h"
#include "base/process/process.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.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::TimeDelta::FromMilliseconds(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<Task*>* which_vector) {
for (Task* candidate : *which_vector) {
if (candidate->process_id() == process_id)
return candidate;
}
return nullptr;
}
} // namespace
FallbackTaskProvider::FallbackTaskProvider(
std::unique_ptr<TaskProvider> primary_subprovider,
std::unique_ptr<TaskProvider> secondary_subprovider)
: sources_{
std::make_unique<SubproviderSource>(this,
std::move(primary_subprovider)),
std::make_unique<SubproviderSource>(
this,
std::move(secondary_subprovider))} {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
FallbackTaskProvider::~FallbackTaskProvider() {}
Task* FallbackTaskProvider::GetTaskOfUrlRequest(int child_id, int route_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Task* task_of_url_request;
for (const auto& source : sources_) {
task_of_url_request =
source->subprovider()->GetTaskOfUrlRequest(child_id, route_id);
if (base::Contains(shown_tasks_, task_of_url_request))
return task_of_url_request;
}
return nullptr;
}
void FallbackTaskProvider::StartUpdating() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(shown_tasks_.empty());
for (auto& source : sources_) {
DCHECK(source->tasks()->empty());
source->subprovider()->SetObserver(source.get());
}
}
void FallbackTaskProvider::StopUpdating() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& source : sources_) {
source->subprovider()->ClearObserver();
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();
it->second.InvalidateWeakPtrs();
}
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&FallbackTaskProvider::ShowPendingTask,
it->second.GetWeakPtr(), task),
kTimeDelayForPendingTask);
}
void FallbackTaskProvider::ShowPendingTask(Task* task) {
pending_shown_tasks_.erase(task);
ShowTask(task);
}
void FallbackTaskProvider::ShowTask(Task* task) {
shown_tasks_.push_back(task);
NotifyObserverTaskAdded(task);
}
void FallbackTaskProvider::HideTask(Task* task) {
auto it = std::remove(shown_tasks_.begin(), shown_tasks_.end(), task);
pending_shown_tasks_.erase(task);
if (it != shown_tasks_.end()) {
shown_tasks_.erase(it, shown_tasks_.end());
NotifyObserverTaskRemoved(task);
}
}
void FallbackTaskProvider::OnTaskAddedBySource(Task* task,
SubproviderSource* source) {
DCHECK(source == primary_source() || source == secondary_source());
// If a secondary task is added but a primary task is already shown for it, we
// can ignore showing the secondary.
if (source == secondary_source()) {
if (GetTaskByPidFromVector(task->process_id(), primary_source()->tasks()))
return;
}
// 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.
if (source == primary_source()) {
ShowTask(task);
for (Task* secondary_task : *secondary_source()->tasks()) {
if (task->process_id() == secondary_task->process_id())
HideTask(secondary_task);
}
} else {
ShowTaskLater(task);
}
}
void FallbackTaskProvider::OnTaskRemovedBySource(Task* task,
SubproviderSource* source) {
DCHECK(source == primary_source() || source == secondary_source());
// When a task from the 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 == primary_source()) {
Task* primary_task =
GetTaskByPidFromVector(task->process_id(), primary_source()->tasks());
if (!primary_task) {
for (Task* secondary_task : *secondary_source()->tasks()) {
if (task->process_id() == secondary_task->process_id()) {
ShowTaskLater(secondary_task);
}
}
}
}
HideTask(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() {}
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);
base::Erase(tasks_, task);
fallback_task_provider_->OnTaskRemovedBySource(task, this);
}
void FallbackTaskProvider::SubproviderSource::TaskUnresponsive(Task* task) {
fallback_task_provider_->OnTaskUnresponsive(task);
}
} // namespace task_manager