blob: 4e8c7adfdc93fd0828fbf868d9e164d529e0fa32 [file] [log] [blame]
// Copyright 2015 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_management/providers/child_process_task_provider.h"
#include "base/process/process.h"
#include "base/stl_util.h"
#include "chrome/browser/task_management/providers/child_process_task.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
using content::BrowserChildProcessHostIterator;
using content::BrowserThread;
using content::ChildProcessData;
namespace task_management {
namespace {
// Collects and returns the child processes data on the IO thread to get all the
// pre-existing child process before we start observing
// |BrowserChildProcessObserver|.
scoped_ptr<std::vector<ChildProcessData>> CollectChildProcessData() {
// The |BrowserChildProcessHostIterator| must only be used on the IO thread.
DCHECK_CURRENTLY_ON(BrowserThread::IO);
scoped_ptr<std::vector<ChildProcessData>> child_processes(
new std::vector<ChildProcessData>());
for (BrowserChildProcessHostIterator itr; !itr.Done(); ++itr) {
const ChildProcessData& process_data = itr.GetData();
// Only add processes that have already started, i.e. with valid handles.
if (process_data.handle == base::kNullProcessHandle)
continue;
child_processes->push_back(process_data);
}
return child_processes;
}
} // namespace
ChildProcessTaskProvider::ChildProcessTaskProvider()
: tasks_by_handle_(),
tasks_by_pid_(),
weak_ptr_factory_(this) {
}
ChildProcessTaskProvider::~ChildProcessTaskProvider() {
}
Task* ChildProcessTaskProvider::GetTaskOfUrlRequest(int origin_pid,
int child_id,
int route_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto itr = tasks_by_pid_.find(static_cast<base::ProcessId>(origin_pid));
if (itr == tasks_by_pid_.end())
return nullptr;
return itr->second;
}
void ChildProcessTaskProvider::BrowserChildProcessLaunchedAndConnected(
const content::ChildProcessData& data) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (data.handle == base::kNullProcessHandle)
return;
CreateTask(data);
}
void ChildProcessTaskProvider::BrowserChildProcessHostDisconnected(
const content::ChildProcessData& data) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DeleteTask(data.handle);
}
void ChildProcessTaskProvider::StartUpdating() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(tasks_by_handle_.empty());
DCHECK(tasks_by_pid_.empty());
// First, get the pre-existing child processes data.
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::IO,
FROM_HERE,
base::Bind(&CollectChildProcessData),
base::Bind(&ChildProcessTaskProvider::ChildProcessDataCollected,
weak_ptr_factory_.GetWeakPtr()));
}
void ChildProcessTaskProvider::StopUpdating() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// ChildProcessDataCollected() should never be called after this, and hence
// we must invalidate the weak pointers.
weak_ptr_factory_.InvalidateWeakPtrs();
// First, stop observing.
BrowserChildProcessObserver::Remove(this);
// Remember: You can't notify the observer of tasks removal here,
// StopUpdating() is called after the observer has been cleared.
// Then delete all tasks (if any).
STLDeleteValues(&tasks_by_handle_); // This will clear |tasks_by_handle_|.
tasks_by_pid_.clear();
}
void ChildProcessTaskProvider::ChildProcessDataCollected(
scoped_ptr<const std::vector<content::ChildProcessData>> child_processes) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& process_data : *child_processes)
CreateTask(process_data);
// Now start observing.
BrowserChildProcessObserver::Add(this);
}
void ChildProcessTaskProvider::CreateTask(
const content::ChildProcessData& data) {
if (tasks_by_handle_.find(data.handle) != tasks_by_handle_.end()) {
// This case can happen when some of the child process data we collect upon
// StartUpdating() might be of BrowserChildProcessHosts whose process
// hadn't launched yet. So we just return.
return;
}
// Create the task and notify the observer.
ChildProcessTask* task = new ChildProcessTask(data);
tasks_by_handle_[data.handle] = task;
tasks_by_pid_[task->process_id()] = task;
NotifyObserverTaskAdded(task);
}
void ChildProcessTaskProvider::DeleteTask(base::ProcessHandle handle) {
auto itr = tasks_by_handle_.find(handle);
// The following case should never happen since we start observing
// |BrowserChildProcessObserver| only after we collect all pre-existing child
// processes and are notified (on the UI thread) that the collection is
// completed at |ChildProcessDataCollected()|.
if (itr == tasks_by_handle_.end()) {
NOTREACHED();
return;
}
ChildProcessTask* task = itr->second;
NotifyObserverTaskRemoved(task);
// Finally delete the task.
tasks_by_handle_.erase(itr);
tasks_by_pid_.erase(task->process_id());
delete task;
}
} // namespace task_management