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 browser_context_
= NULL
;
66 for (std::map
<int, ProcessInfo
>::const_iterator it
= instance_info_
.begin();
67 it
!= instance_info_
.end();
69 RenderProcessHost
* rph
= RenderProcessHost::FromID(it
->second
.process_id
);
71 static_cast<RenderProcessHostImpl
*>(rph
)->DecrementWorkerRefCount();
73 instance_info_
.clear();
76 void ServiceWorkerProcessManager::AddProcessReferenceToPattern(
77 const GURL
& pattern
, int process_id
) {
78 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
79 BrowserThread::PostTask(
82 base::Bind(&ServiceWorkerProcessManager::AddProcessReferenceToPattern
,
89 ProcessRefMap
& process_refs
= pattern_processes_
[pattern
];
90 ++process_refs
[process_id
];
93 void ServiceWorkerProcessManager::RemoveProcessReferenceFromPattern(
94 const GURL
& pattern
, int process_id
) {
95 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
96 BrowserThread::PostTask(
100 &ServiceWorkerProcessManager::RemoveProcessReferenceFromPattern
,
107 PatternProcessRefMap::iterator it
= pattern_processes_
.find(pattern
);
108 if (it
== pattern_processes_
.end()) {
109 NOTREACHED() << "process refrences not found for pattern: " << pattern
;
112 ProcessRefMap
& process_refs
= it
->second
;
113 ProcessRefMap::iterator found
= process_refs
.find(process_id
);
114 if (found
== process_refs
.end()) {
115 NOTREACHED() << "Releasing unknown process ref " << process_id
;
118 if (--found
->second
== 0) {
119 process_refs
.erase(found
);
120 if (process_refs
.empty())
121 pattern_processes_
.erase(it
);
125 bool ServiceWorkerProcessManager::PatternHasProcessToRun(
126 const GURL
& pattern
) const {
127 PatternProcessRefMap::const_iterator it
= pattern_processes_
.find(pattern
);
128 if (it
== pattern_processes_
.end())
130 return !it
->second
.empty();
133 void ServiceWorkerProcessManager::AllocateWorkerProcess(
134 int embedded_worker_id
,
136 const GURL
& script_url
,
137 const base::Callback
<void(ServiceWorkerStatusCode
, int process_id
)>&
139 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
140 BrowserThread::PostTask(
143 base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess
,
152 if (process_id_for_test_
!= -1) {
153 // Let tests specify the returned process ID. Note: We may need to be able
154 // to specify the error code too.
155 BrowserThread::PostTask(
158 base::Bind(callback
, SERVICE_WORKER_OK
, process_id_for_test_
));
162 DCHECK(!ContainsKey(instance_info_
, embedded_worker_id
))
163 << embedded_worker_id
<< " already has a process allocated";
165 std::vector
<int> sorted_candidates
= SortProcessesForPattern(pattern
);
166 for (std::vector
<int>::const_iterator it
= sorted_candidates
.begin();
167 it
!= sorted_candidates
.end();
169 if (!IncrementWorkerRefCountByPid(*it
))
171 instance_info_
.insert(
172 std::make_pair(embedded_worker_id
, ProcessInfo(*it
)));
173 BrowserThread::PostTask(BrowserThread::IO
,
175 base::Bind(callback
, SERVICE_WORKER_OK
, *it
));
179 if (!browser_context_
) {
180 // Shutdown has started.
181 BrowserThread::PostTask(
184 base::Bind(callback
, SERVICE_WORKER_ERROR_START_WORKER_FAILED
, -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(
199 base::Bind(callback
, SERVICE_WORKER_ERROR_START_WORKER_FAILED
, -1));
203 instance_info_
.insert(
204 std::make_pair(embedded_worker_id
, ProcessInfo(site_instance
)));
206 static_cast<RenderProcessHostImpl
*>(rph
)->IncrementWorkerRefCount();
207 BrowserThread::PostTask(
210 base::Bind(callback
, SERVICE_WORKER_OK
, rph
->GetID()));
213 void ServiceWorkerProcessManager::ReleaseWorkerProcess(int embedded_worker_id
) {
214 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
215 BrowserThread::PostTask(
218 base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess
,
220 embedded_worker_id
));
223 if (process_id_for_test_
!= -1) {
224 // Unittests don't increment or decrement the worker refcount of a
225 // RenderProcessHost.
228 if (browser_context_
== NULL
) {
229 // Shutdown already released all instances.
230 DCHECK(instance_info_
.empty());
233 std::map
<int, ProcessInfo
>::iterator info
=
234 instance_info_
.find(embedded_worker_id
);
235 DCHECK(info
!= instance_info_
.end());
236 RenderProcessHost
* rph
= NULL
;
237 if (info
->second
.site_instance
.get()) {
238 rph
= info
->second
.site_instance
->GetProcess();
239 DCHECK_EQ(info
->second
.process_id
, rph
->GetID())
240 << "A SiteInstance's process shouldn't get destroyed while we're "
241 "holding a reference to it. Was the reference actually held?";
243 rph
= RenderProcessHost::FromID(info
->second
.process_id
);
245 << "Process " << info
->second
.process_id
246 << " was destroyed unexpectedly. Did we actually hold a reference?";
248 static_cast<RenderProcessHostImpl
*>(rph
)->DecrementWorkerRefCount();
249 instance_info_
.erase(info
);
252 std::vector
<int> ServiceWorkerProcessManager::SortProcessesForPattern(
253 const GURL
& pattern
) const {
254 PatternProcessRefMap::const_iterator it
= pattern_processes_
.find(pattern
);
255 if (it
== pattern_processes_
.end())
256 return std::vector
<int>();
258 std::vector
<std::pair
<int, int> > counted(
259 it
->second
.begin(), it
->second
.end());
260 std::sort(counted
.begin(), counted
.end(), SecondGreater());
262 std::vector
<int> result(counted
.size());
263 for (size_t i
= 0; i
< counted
.size(); ++i
)
264 result
[i
] = counted
[i
].first
;
268 } // namespace content
271 // Destroying ServiceWorkerProcessManagers only on the UI thread allows the
272 // member WeakPtr to safely guard the object's lifetime when used on that
274 void DefaultDeleter
<content::ServiceWorkerProcessManager
>::operator()(
275 content::ServiceWorkerProcessManager
* ptr
) const {
276 content::BrowserThread::DeleteSoon(
277 content::BrowserThread::UI
, FROM_HERE
, ptr
);