blob: 7395e7b7b7768675ed4d9225df4436e6a6ba5917 [file] [log] [blame]
// Copyright 2014 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 "content/browser/service_worker/service_worker_process_manager.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/site_instance.h"
#include "url/gurl.h"
namespace content {
static bool IncrementWorkerRefCountByPid(int process_id) {
RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
if (!rph || rph->FastShutdownStarted())
return false;
static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
return true;
}
ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(
const scoped_refptr<SiteInstance>& site_instance)
: site_instance(site_instance),
process_id(site_instance->GetProcess()->GetID()) {
}
ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(int process_id)
: process_id(process_id) {
}
ServiceWorkerProcessManager::ProcessInfo::~ProcessInfo() {
}
ServiceWorkerProcessManager::ServiceWorkerProcessManager(
BrowserContext* browser_context)
: browser_context_(browser_context),
process_id_for_test_(-1),
weak_this_factory_(this),
weak_this_(weak_this_factory_.GetWeakPtr()) {
}
ServiceWorkerProcessManager::~ServiceWorkerProcessManager() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(browser_context_ == NULL)
<< "Call Shutdown() before destroying |this|, so that racing method "
<< "invocations don't use a destroyed BrowserContext.";
}
void ServiceWorkerProcessManager::Shutdown() {
browser_context_ = NULL;
for (std::map<int, ProcessInfo>::const_iterator it = instance_info_.begin();
it != instance_info_.end();
++it) {
RenderProcessHost* rph = RenderProcessHost::FromID(it->second.process_id);
DCHECK(rph);
static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
}
instance_info_.clear();
}
void ServiceWorkerProcessManager::AllocateWorkerProcess(
int embedded_worker_id,
const std::vector<int>& process_ids,
const GURL& script_url,
const base::Callback<void(ServiceWorkerStatusCode, int process_id)>&
callback) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess,
weak_this_,
embedded_worker_id,
process_ids,
script_url,
callback));
return;
}
if (process_id_for_test_ != -1) {
// Let tests specify the returned process ID. Note: We may need to be able
// to specify the error code too.
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(callback, SERVICE_WORKER_OK, process_id_for_test_));
return;
}
DCHECK(!ContainsKey(instance_info_, embedded_worker_id))
<< embedded_worker_id << " already has a process allocated";
for (std::vector<int>::const_iterator it = process_ids.begin();
it != process_ids.end();
++it) {
if (IncrementWorkerRefCountByPid(*it)) {
instance_info_.insert(
std::make_pair(embedded_worker_id, ProcessInfo(*it)));
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(callback, SERVICE_WORKER_OK, *it));
return;
}
}
if (!browser_context_) {
// Shutdown has started.
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
return;
}
// No existing processes available; start a new one.
scoped_refptr<SiteInstance> site_instance =
SiteInstance::CreateForURL(browser_context_, script_url);
RenderProcessHost* rph = site_instance->GetProcess();
// This Init() call posts a task to the IO thread that adds the RPH's
// ServiceWorkerDispatcherHost to the
// EmbeddedWorkerRegistry::process_sender_map_.
if (!rph->Init()) {
LOG(ERROR) << "Couldn't start a new process!";
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
return;
}
instance_info_.insert(
std::make_pair(embedded_worker_id, ProcessInfo(site_instance)));
static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(callback, SERVICE_WORKER_OK, rph->GetID()));
}
void ServiceWorkerProcessManager::ReleaseWorkerProcess(int embedded_worker_id) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess,
weak_this_,
embedded_worker_id));
return;
}
if (process_id_for_test_ != -1) {
// Unittests don't increment or decrement the worker refcount of a
// RenderProcessHost.
return;
}
if (browser_context_ == NULL) {
// Shutdown already released all instances.
DCHECK(instance_info_.empty());
return;
}
std::map<int, ProcessInfo>::iterator info =
instance_info_.find(embedded_worker_id);
DCHECK(info != instance_info_.end());
RenderProcessHost* rph = NULL;
if (info->second.site_instance) {
rph = info->second.site_instance->GetProcess();
DCHECK_EQ(info->second.process_id, rph->GetID())
<< "A SiteInstance's process shouldn't get destroyed while we're "
"holding a reference to it. Was the reference actually held?";
} else {
rph = RenderProcessHost::FromID(info->second.process_id);
DCHECK(rph)
<< "Process " << info->second.process_id
<< " was destroyed unexpectedly. Did we actually hold a reference?";
}
static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
instance_info_.erase(info);
}
} // namespace content
namespace base {
// Destroying ServiceWorkerProcessManagers only on the UI thread allows the
// member WeakPtr to safely guard the object's lifetime when used on that
// thread.
void DefaultDeleter<content::ServiceWorkerProcessManager>::operator()(
content::ServiceWorkerProcessManager* ptr) const {
content::BrowserThread::DeleteSoon(
content::BrowserThread::UI, FROM_HERE, ptr);
}
} // namespace base