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
,
140 bool is_new_process
)>& callback
) {
141 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
142 BrowserThread::PostTask(
145 base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess
,
154 if (process_id_for_test_
!= -1) {
155 // Let tests specify the returned process ID. Note: We may need to be able
156 // to specify the error code too.
157 BrowserThread::PostTask(
158 BrowserThread::IO
, FROM_HERE
,
159 base::Bind(callback
, SERVICE_WORKER_OK
, process_id_for_test_
,
160 false /* is_new_process */));
164 DCHECK(!ContainsKey(instance_info_
, embedded_worker_id
))
165 << embedded_worker_id
<< " already has a process allocated";
167 std::vector
<int> sorted_candidates
= SortProcessesForPattern(pattern
);
168 for (std::vector
<int>::const_iterator it
= sorted_candidates
.begin();
169 it
!= sorted_candidates
.end();
171 if (!IncrementWorkerRefCountByPid(*it
))
173 instance_info_
.insert(
174 std::make_pair(embedded_worker_id
, ProcessInfo(*it
)));
175 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
176 base::Bind(callback
, SERVICE_WORKER_OK
, *it
,
177 false /* is_new_process */));
181 if (!browser_context_
) {
182 // Shutdown has started.
183 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
184 base::Bind(callback
, SERVICE_WORKER_ERROR_ABORT
, -1,
185 false /* is_new_process */));
188 // No existing processes available; start a new one.
189 scoped_refptr
<SiteInstance
> site_instance
=
190 SiteInstance::CreateForURL(browser_context_
, script_url
);
191 RenderProcessHost
* rph
= site_instance
->GetProcess();
192 // This Init() call posts a task to the IO thread that adds the RPH's
193 // ServiceWorkerDispatcherHost to the
194 // EmbeddedWorkerRegistry::process_sender_map_.
196 LOG(ERROR
) << "Couldn't start a new process!";
197 BrowserThread::PostTask(
198 BrowserThread::IO
, FROM_HERE
,
199 base::Bind(callback
, SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND
, -1,
200 false /* is_new_process */));
204 instance_info_
.insert(
205 std::make_pair(embedded_worker_id
, ProcessInfo(site_instance
)));
207 static_cast<RenderProcessHostImpl
*>(rph
)->IncrementWorkerRefCount();
208 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
209 base::Bind(callback
, SERVICE_WORKER_OK
, rph
->GetID(),
210 true /* is_new_process */));
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
);