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"
17 // Functor to sort by the .second element of a struct.
18 struct SecondGreater
{
19 template <typename Value
>
20 bool operator()(const Value
& lhs
, const Value
& rhs
) {
21 return lhs
.second
> rhs
.second
;
27 static bool IncrementWorkerRefCountByPid(int process_id
) {
28 RenderProcessHost
* rph
= RenderProcessHost::FromID(process_id
);
29 if (!rph
|| rph
->FastShutdownStarted())
32 static_cast<RenderProcessHostImpl
*>(rph
)->IncrementWorkerRefCount();
36 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(
37 const scoped_refptr
<SiteInstance
>& site_instance
)
38 : site_instance(site_instance
),
39 process_id(site_instance
->GetProcess()->GetID()) {
42 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(int process_id
)
43 : process_id(process_id
) {
46 ServiceWorkerProcessManager::ProcessInfo::~ProcessInfo() {
49 ServiceWorkerProcessManager::ServiceWorkerProcessManager(
50 BrowserContext
* browser_context
)
51 : browser_context_(browser_context
),
52 process_id_for_test_(-1),
53 weak_this_factory_(this) {
54 weak_this_
= weak_this_factory_
.GetWeakPtr();
57 ServiceWorkerProcessManager::~ServiceWorkerProcessManager() {
58 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
59 DCHECK(browser_context_
== NULL
)
60 << "Call Shutdown() before destroying |this|, so that racing method "
61 << "invocations don't use a destroyed BrowserContext.";
64 void ServiceWorkerProcessManager::Shutdown() {
65 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
66 browser_context_
= NULL
;
67 for (std::map
<int, ProcessInfo
>::const_iterator it
= instance_info_
.begin();
68 it
!= instance_info_
.end();
70 RenderProcessHost
* rph
= RenderProcessHost::FromID(it
->second
.process_id
);
72 static_cast<RenderProcessHostImpl
*>(rph
)->DecrementWorkerRefCount();
74 instance_info_
.clear();
77 void ServiceWorkerProcessManager::AddProcessReferenceToPattern(
78 const GURL
& pattern
, int process_id
) {
79 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
80 BrowserThread::PostTask(
83 base::Bind(&ServiceWorkerProcessManager::AddProcessReferenceToPattern
,
90 ProcessRefMap
& process_refs
= pattern_processes_
[pattern
];
91 ++process_refs
[process_id
];
94 void ServiceWorkerProcessManager::RemoveProcessReferenceFromPattern(
95 const GURL
& pattern
, int process_id
) {
96 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
97 BrowserThread::PostTask(
101 &ServiceWorkerProcessManager::RemoveProcessReferenceFromPattern
,
108 PatternProcessRefMap::iterator it
= pattern_processes_
.find(pattern
);
109 if (it
== pattern_processes_
.end()) {
110 NOTREACHED() << "process refrences not found for pattern: " << pattern
;
113 ProcessRefMap
& process_refs
= it
->second
;
114 ProcessRefMap::iterator found
= process_refs
.find(process_id
);
115 if (found
== process_refs
.end()) {
116 NOTREACHED() << "Releasing unknown process ref " << process_id
;
119 if (--found
->second
== 0) {
120 process_refs
.erase(found
);
121 if (process_refs
.empty())
122 pattern_processes_
.erase(it
);
126 bool ServiceWorkerProcessManager::PatternHasProcessToRun(
127 const GURL
& pattern
) const {
128 PatternProcessRefMap::const_iterator it
= pattern_processes_
.find(pattern
);
129 if (it
== pattern_processes_
.end())
131 return !it
->second
.empty();
134 void ServiceWorkerProcessManager::AllocateWorkerProcess(
135 int embedded_worker_id
,
137 const GURL
& script_url
,
138 const base::Callback
<void(ServiceWorkerStatusCode
, int process_id
)>&
140 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
141 BrowserThread::PostTask(
144 base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess
,
153 if (process_id_for_test_
!= -1) {
154 // Let tests specify the returned process ID. Note: We may need to be able
155 // to specify the error code too.
156 BrowserThread::PostTask(
159 base::Bind(callback
, SERVICE_WORKER_OK
, process_id_for_test_
));
163 DCHECK(!ContainsKey(instance_info_
, embedded_worker_id
))
164 << embedded_worker_id
<< " already has a process allocated";
166 std::vector
<int> sorted_candidates
= SortProcessesForPattern(pattern
);
167 for (std::vector
<int>::const_iterator it
= sorted_candidates
.begin();
168 it
!= sorted_candidates
.end();
170 if (!IncrementWorkerRefCountByPid(*it
))
172 instance_info_
.insert(
173 std::make_pair(embedded_worker_id
, ProcessInfo(*it
)));
174 BrowserThread::PostTask(BrowserThread::IO
,
176 base::Bind(callback
, SERVICE_WORKER_OK
, *it
));
180 if (!browser_context_
) {
181 // Shutdown has started.
182 BrowserThread::PostTask(
183 BrowserThread::IO
, FROM_HERE
,
184 base::Bind(callback
, SERVICE_WORKER_ERROR_ABORT
, -1));
187 // No existing processes available; start a new one.
188 scoped_refptr
<SiteInstance
> site_instance
=
189 SiteInstance::CreateForURL(browser_context_
, script_url
);
190 RenderProcessHost
* rph
= site_instance
->GetProcess();
191 // This Init() call posts a task to the IO thread that adds the RPH's
192 // ServiceWorkerDispatcherHost to the
193 // EmbeddedWorkerRegistry::process_sender_map_.
195 LOG(ERROR
) << "Couldn't start a new process!";
196 BrowserThread::PostTask(
197 BrowserThread::IO
, FROM_HERE
,
198 base::Bind(callback
, SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND
, -1));
202 instance_info_
.insert(
203 std::make_pair(embedded_worker_id
, ProcessInfo(site_instance
)));
205 static_cast<RenderProcessHostImpl
*>(rph
)->IncrementWorkerRefCount();
206 BrowserThread::PostTask(
209 base::Bind(callback
, SERVICE_WORKER_OK
, rph
->GetID()));
212 void ServiceWorkerProcessManager::ReleaseWorkerProcess(int embedded_worker_id
) {
213 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
214 BrowserThread::PostTask(
217 base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess
,
219 embedded_worker_id
));
222 if (process_id_for_test_
!= -1) {
223 // Unittests don't increment or decrement the worker refcount of a
224 // RenderProcessHost.
227 if (browser_context_
== NULL
) {
228 // Shutdown already released all instances.
229 DCHECK(instance_info_
.empty());
232 std::map
<int, ProcessInfo
>::iterator info
=
233 instance_info_
.find(embedded_worker_id
);
234 DCHECK(info
!= instance_info_
.end());
235 RenderProcessHost
* rph
= NULL
;
236 if (info
->second
.site_instance
.get()) {
237 rph
= info
->second
.site_instance
->GetProcess();
238 DCHECK_EQ(info
->second
.process_id
, rph
->GetID())
239 << "A SiteInstance's process shouldn't get destroyed while we're "
240 "holding a reference to it. Was the reference actually held?";
242 rph
= RenderProcessHost::FromID(info
->second
.process_id
);
244 << "Process " << info
->second
.process_id
245 << " was destroyed unexpectedly. Did we actually hold a reference?";
247 static_cast<RenderProcessHostImpl
*>(rph
)->DecrementWorkerRefCount();
248 instance_info_
.erase(info
);
251 std::vector
<int> ServiceWorkerProcessManager::SortProcessesForPattern(
252 const GURL
& pattern
) const {
253 PatternProcessRefMap::const_iterator it
= pattern_processes_
.find(pattern
);
254 if (it
== pattern_processes_
.end())
255 return std::vector
<int>();
257 std::vector
<std::pair
<int, int> > counted(
258 it
->second
.begin(), it
->second
.end());
259 std::sort(counted
.begin(), counted
.end(), SecondGreater());
261 std::vector
<int> result(counted
.size());
262 for (size_t i
= 0; i
< counted
.size(); ++i
)
263 result
[i
] = counted
[i
].first
;
267 } // namespace content
270 // Destroying ServiceWorkerProcessManagers only on the UI thread allows the
271 // member WeakPtr to safely guard the object's lifetime when used on that
273 void DefaultDeleter
<content::ServiceWorkerProcessManager
>::operator()(
274 content::ServiceWorkerProcessManager
* ptr
) const {
275 content::BrowserThread::DeleteSoon(
276 content::BrowserThread::UI
, FROM_HERE
, ptr
);