cc: Make picture pile base thread safe.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_process_manager.cc
blob16e585b9c717939f8d7626e10b7a3253142bd2d6
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 browser_context_ = NULL;
66 for (std::map<int, ProcessInfo>::const_iterator it = instance_info_.begin();
67 it != instance_info_.end();
68 ++it) {
69 RenderProcessHost* rph = RenderProcessHost::FromID(it->second.process_id);
70 DCHECK(rph);
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(
80 BrowserThread::UI,
81 FROM_HERE,
82 base::Bind(&ServiceWorkerProcessManager::AddProcessReferenceToPattern,
83 weak_this_,
84 pattern,
85 process_id));
86 return;
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(
97 BrowserThread::UI,
98 FROM_HERE,
99 base::Bind(
100 &ServiceWorkerProcessManager::RemoveProcessReferenceFromPattern,
101 weak_this_,
102 pattern,
103 process_id));
104 return;
107 PatternProcessRefMap::iterator it = pattern_processes_.find(pattern);
108 if (it == pattern_processes_.end()) {
109 NOTREACHED() << "process refrences not found for pattern: " << pattern;
110 return;
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;
116 return;
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())
129 return false;
130 return !it->second.empty();
133 void ServiceWorkerProcessManager::AllocateWorkerProcess(
134 int embedded_worker_id,
135 const GURL& pattern,
136 const GURL& script_url,
137 const base::Callback<void(ServiceWorkerStatusCode, int process_id)>&
138 callback) {
139 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
140 BrowserThread::PostTask(
141 BrowserThread::UI,
142 FROM_HERE,
143 base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess,
144 weak_this_,
145 embedded_worker_id,
146 pattern,
147 script_url,
148 callback));
149 return;
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(
156 BrowserThread::IO,
157 FROM_HERE,
158 base::Bind(callback, SERVICE_WORKER_OK, process_id_for_test_));
159 return;
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();
168 ++it) {
169 if (!IncrementWorkerRefCountByPid(*it))
170 continue;
171 instance_info_.insert(
172 std::make_pair(embedded_worker_id, ProcessInfo(*it)));
173 BrowserThread::PostTask(BrowserThread::IO,
174 FROM_HERE,
175 base::Bind(callback, SERVICE_WORKER_OK, *it));
176 return;
179 if (!browser_context_) {
180 // Shutdown has started.
181 BrowserThread::PostTask(
182 BrowserThread::IO,
183 FROM_HERE,
184 base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
185 return;
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_.
194 if (!rph->Init()) {
195 LOG(ERROR) << "Couldn't start a new process!";
196 BrowserThread::PostTask(
197 BrowserThread::IO,
198 FROM_HERE,
199 base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
200 return;
203 instance_info_.insert(
204 std::make_pair(embedded_worker_id, ProcessInfo(site_instance)));
206 static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
207 BrowserThread::PostTask(
208 BrowserThread::IO,
209 FROM_HERE,
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(
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