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(
185 base::Bind(callback
, SERVICE_WORKER_ERROR_START_WORKER_FAILED
, -1));
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(
200 base::Bind(callback
, SERVICE_WORKER_ERROR_START_WORKER_FAILED
, -1));
204 instance_info_
.insert(
205 std::make_pair(embedded_worker_id
, ProcessInfo(site_instance
)));
207 static_cast<RenderProcessHostImpl
*>(rph
)->IncrementWorkerRefCount();
208 BrowserThread::PostTask(
211 base::Bind(callback
, SERVICE_WORKER_OK
, rph
->GetID()));
214 void ServiceWorkerProcessManager::ReleaseWorkerProcess(int embedded_worker_id
) {
215 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
216 BrowserThread::PostTask(
219 base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess
,
221 embedded_worker_id
));
224 if (process_id_for_test_
!= -1) {
225 // Unittests don't increment or decrement the worker refcount of a
226 // RenderProcessHost.
229 if (browser_context_
== NULL
) {
230 // Shutdown already released all instances.
231 DCHECK(instance_info_
.empty());
234 std::map
<int, ProcessInfo
>::iterator info
=
235 instance_info_
.find(embedded_worker_id
);
236 DCHECK(info
!= instance_info_
.end());
237 RenderProcessHost
* rph
= NULL
;
238 if (info
->second
.site_instance
.get()) {
239 rph
= info
->second
.site_instance
->GetProcess();
240 DCHECK_EQ(info
->second
.process_id
, rph
->GetID())
241 << "A SiteInstance's process shouldn't get destroyed while we're "
242 "holding a reference to it. Was the reference actually held?";
244 rph
= RenderProcessHost::FromID(info
->second
.process_id
);
246 << "Process " << info
->second
.process_id
247 << " was destroyed unexpectedly. Did we actually hold a reference?";
249 static_cast<RenderProcessHostImpl
*>(rph
)->DecrementWorkerRefCount();
250 instance_info_
.erase(info
);
253 std::vector
<int> ServiceWorkerProcessManager::SortProcessesForPattern(
254 const GURL
& pattern
) const {
255 PatternProcessRefMap::const_iterator it
= pattern_processes_
.find(pattern
);
256 if (it
== pattern_processes_
.end())
257 return std::vector
<int>();
259 std::vector
<std::pair
<int, int> > counted(
260 it
->second
.begin(), it
->second
.end());
261 std::sort(counted
.begin(), counted
.end(), SecondGreater());
263 std::vector
<int> result(counted
.size());
264 for (size_t i
= 0; i
< counted
.size(); ++i
)
265 result
[i
] = counted
[i
].first
;
269 } // namespace content
272 // Destroying ServiceWorkerProcessManagers only on the UI thread allows the
273 // member WeakPtr to safely guard the object's lifetime when used on that
275 void DefaultDeleter
<content::ServiceWorkerProcessManager
>::operator()(
276 content::ServiceWorkerProcessManager
* ptr
) const {
277 content::BrowserThread::DeleteSoon(
278 content::BrowserThread::UI
, FROM_HERE
, ptr
);