Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_process_manager.cc
bloba310bcc1770b5ca7ec279ad496b594db7ecaba50
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"
11 #include "url/gurl.h"
13 namespace content {
15 namespace {
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;
25 } // namespace
27 static bool IncrementWorkerRefCountByPid(int process_id) {
28 RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
29 if (!rph || rph->FastShutdownStarted())
30 return false;
32 static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
33 return true;
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();
69 ++it) {
70 RenderProcessHost* rph = RenderProcessHost::FromID(it->second.process_id);
71 DCHECK(rph);
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(
81 BrowserThread::UI,
82 FROM_HERE,
83 base::Bind(&ServiceWorkerProcessManager::AddProcessReferenceToPattern,
84 weak_this_,
85 pattern,
86 process_id));
87 return;
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(
98 BrowserThread::UI,
99 FROM_HERE,
100 base::Bind(
101 &ServiceWorkerProcessManager::RemoveProcessReferenceFromPattern,
102 weak_this_,
103 pattern,
104 process_id));
105 return;
108 PatternProcessRefMap::iterator it = pattern_processes_.find(pattern);
109 if (it == pattern_processes_.end()) {
110 NOTREACHED() << "process refrences not found for pattern: " << pattern;
111 return;
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;
117 return;
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())
130 return false;
131 return !it->second.empty();
134 void ServiceWorkerProcessManager::AllocateWorkerProcess(
135 int embedded_worker_id,
136 const GURL& pattern,
137 const GURL& script_url,
138 const base::Callback<void(ServiceWorkerStatusCode,
139 int process_id,
140 bool is_new_process)>& callback) {
141 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
142 BrowserThread::PostTask(
143 BrowserThread::UI,
144 FROM_HERE,
145 base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess,
146 weak_this_,
147 embedded_worker_id,
148 pattern,
149 script_url,
150 callback));
151 return;
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 */));
161 return;
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();
170 ++it) {
171 if (!IncrementWorkerRefCountByPid(*it))
172 continue;
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 */));
178 return;
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 */));
186 return;
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_.
195 if (!rph->Init()) {
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 */));
201 return;
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(
216 BrowserThread::UI,
217 FROM_HERE,
218 base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess,
219 weak_this_,
220 embedded_worker_id));
221 return;
223 if (process_id_for_test_ != -1) {
224 // Unittests don't increment or decrement the worker refcount of a
225 // RenderProcessHost.
226 return;
228 if (browser_context_ == NULL) {
229 // Shutdown already released all instances.
230 DCHECK(instance_info_.empty());
231 return;
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?";
242 } else {
243 rph = RenderProcessHost::FromID(info->second.process_id);
244 DCHECK(rph)
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;
265 return result;
268 } // namespace content
270 namespace base {
271 // Destroying ServiceWorkerProcessManagers only on the UI thread allows the
272 // member WeakPtr to safely guard the object's lifetime when used on that
273 // thread.
274 void DefaultDeleter<content::ServiceWorkerProcessManager>::operator()(
275 content::ServiceWorkerProcessManager* ptr) const {
276 content::BrowserThread::DeleteSoon(
277 content::BrowserThread::UI, FROM_HERE, ptr);
279 } // namespace base