1 // Copyright 2014 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.
5 #include "content/browser/service_worker/service_worker_process_manager.h"
7 #include "content/browser/renderer_host/render_process_host_impl.h"
8 #include "content/browser/service_worker/service_worker_context_wrapper.h"
9 #include "content/public/browser/browser_thread.h"
10 #include "content/public/browser/site_instance.h"
15 static bool IncrementWorkerRefCountByPid(int process_id
) {
16 RenderProcessHost
* rph
= RenderProcessHost::FromID(process_id
);
17 if (!rph
|| rph
->FastShutdownStarted())
20 static_cast<RenderProcessHostImpl
*>(rph
)->IncrementWorkerRefCount();
24 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(
25 const scoped_refptr
<SiteInstance
>& site_instance
)
26 : site_instance(site_instance
),
27 process_id(site_instance
->GetProcess()->GetID()) {
30 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(int process_id
)
31 : process_id(process_id
) {
34 ServiceWorkerProcessManager::ProcessInfo::~ProcessInfo() {
37 ServiceWorkerProcessManager::ServiceWorkerProcessManager(
38 BrowserContext
* browser_context
)
39 : browser_context_(browser_context
),
40 process_id_for_test_(-1),
41 weak_this_factory_(this),
42 weak_this_(weak_this_factory_
.GetWeakPtr()) {
45 ServiceWorkerProcessManager::~ServiceWorkerProcessManager() {
46 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
47 DCHECK(browser_context_
== NULL
)
48 << "Call Shutdown() before destroying |this|, so that racing method "
49 << "invocations don't use a destroyed BrowserContext.";
52 void ServiceWorkerProcessManager::Shutdown() {
53 browser_context_
= NULL
;
54 for (std::map
<int, ProcessInfo
>::const_iterator it
= instance_info_
.begin();
55 it
!= instance_info_
.end();
57 RenderProcessHost
* rph
= RenderProcessHost::FromID(it
->second
.process_id
);
59 static_cast<RenderProcessHostImpl
*>(rph
)->DecrementWorkerRefCount();
61 instance_info_
.clear();
64 void ServiceWorkerProcessManager::AllocateWorkerProcess(
65 int embedded_worker_id
,
66 const std::vector
<int>& process_ids
,
67 const GURL
& script_url
,
68 const base::Callback
<void(ServiceWorkerStatusCode
, int process_id
)>&
70 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
71 BrowserThread::PostTask(
74 base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess
,
83 if (process_id_for_test_
!= -1) {
84 // Let tests specify the returned process ID. Note: We may need to be able
85 // to specify the error code too.
86 BrowserThread::PostTask(
89 base::Bind(callback
, SERVICE_WORKER_OK
, process_id_for_test_
));
93 DCHECK(!ContainsKey(instance_info_
, embedded_worker_id
))
94 << embedded_worker_id
<< " already has a process allocated";
96 for (std::vector
<int>::const_iterator it
= process_ids
.begin();
97 it
!= process_ids
.end();
99 if (IncrementWorkerRefCountByPid(*it
)) {
100 instance_info_
.insert(
101 std::make_pair(embedded_worker_id
, ProcessInfo(*it
)));
102 BrowserThread::PostTask(BrowserThread::IO
,
104 base::Bind(callback
, SERVICE_WORKER_OK
, *it
));
109 if (!browser_context_
) {
110 // Shutdown has started.
111 BrowserThread::PostTask(
114 base::Bind(callback
, SERVICE_WORKER_ERROR_START_WORKER_FAILED
, -1));
117 // No existing processes available; start a new one.
118 scoped_refptr
<SiteInstance
> site_instance
=
119 SiteInstance::CreateForURL(browser_context_
, script_url
);
120 RenderProcessHost
* rph
= site_instance
->GetProcess();
121 // This Init() call posts a task to the IO thread that adds the RPH's
122 // ServiceWorkerDispatcherHost to the
123 // EmbeddedWorkerRegistry::process_sender_map_.
125 LOG(ERROR
) << "Couldn't start a new process!";
126 BrowserThread::PostTask(
129 base::Bind(callback
, SERVICE_WORKER_ERROR_START_WORKER_FAILED
, -1));
133 instance_info_
.insert(
134 std::make_pair(embedded_worker_id
, ProcessInfo(site_instance
)));
136 static_cast<RenderProcessHostImpl
*>(rph
)->IncrementWorkerRefCount();
137 BrowserThread::PostTask(
140 base::Bind(callback
, SERVICE_WORKER_OK
, rph
->GetID()));
143 void ServiceWorkerProcessManager::ReleaseWorkerProcess(int embedded_worker_id
) {
144 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
145 BrowserThread::PostTask(
148 base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess
,
150 embedded_worker_id
));
153 if (process_id_for_test_
!= -1) {
154 // Unittests don't increment or decrement the worker refcount of a
155 // RenderProcessHost.
158 if (browser_context_
== NULL
) {
159 // Shutdown already released all instances.
160 DCHECK(instance_info_
.empty());
163 std::map
<int, ProcessInfo
>::iterator info
=
164 instance_info_
.find(embedded_worker_id
);
165 DCHECK(info
!= instance_info_
.end());
166 RenderProcessHost
* rph
= NULL
;
167 if (info
->second
.site_instance
.get()) {
168 rph
= info
->second
.site_instance
->GetProcess();
169 DCHECK_EQ(info
->second
.process_id
, rph
->GetID())
170 << "A SiteInstance's process shouldn't get destroyed while we're "
171 "holding a reference to it. Was the reference actually held?";
173 rph
= RenderProcessHost::FromID(info
->second
.process_id
);
175 << "Process " << info
->second
.process_id
176 << " was destroyed unexpectedly. Did we actually hold a reference?";
178 static_cast<RenderProcessHostImpl
*>(rph
)->DecrementWorkerRefCount();
179 instance_info_
.erase(info
);
182 } // namespace content
185 // Destroying ServiceWorkerProcessManagers only on the UI thread allows the
186 // member WeakPtr to safely guard the object's lifetime when used on that
188 void DefaultDeleter
<content::ServiceWorkerProcessManager
>::operator()(
189 content::ServiceWorkerProcessManager
* ptr
) const {
190 content::BrowserThread::DeleteSoon(
191 content::BrowserThread::UI
, FROM_HERE
, ptr
);