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. 4 5#include "content/browser/service_worker/service_worker_process_manager.h" 6 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" 12 13namespace content { 14 15namespace { 16 17// Functor to sort by the .second element of a struct. 18struct SecondGreater { 19 template <typename Value> 20 bool operator()(const Value& lhs, const Value& rhs) { 21 return lhs.second > rhs.second; 22 } 23}; 24 25} // namespace 26 27static bool IncrementWorkerRefCountByPid(int process_id) { 28 RenderProcessHost* rph = RenderProcessHost::FromID(process_id); 29 if (!rph || rph->FastShutdownStarted()) 30 return false; 31 32 static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount(); 33 return true; 34} 35 36ServiceWorkerProcessManager::ProcessInfo::ProcessInfo( 37 const scoped_refptr<SiteInstance>& site_instance) 38 : site_instance(site_instance), 39 process_id(site_instance->GetProcess()->GetID()) { 40} 41 42ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(int process_id) 43 : process_id(process_id) { 44} 45 46ServiceWorkerProcessManager::ProcessInfo::~ProcessInfo() { 47} 48 49ServiceWorkerProcessManager::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()) { 55} 56 57ServiceWorkerProcessManager::~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."; 62} 63 64void 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(); 72 } 73 instance_info_.clear(); 74} 75 76void 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; 87 } 88 89 ProcessRefMap& process_refs = pattern_processes_[pattern]; 90 ++process_refs[process_id]; 91} 92 93void 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; 105 } 106 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; 111 } 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; 117 } 118 if (--found->second == 0) { 119 process_refs.erase(found); 120 if (process_refs.empty()) 121 pattern_processes_.erase(it); 122 } 123} 124 125bool 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(); 131} 132 133void 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; 150 } 151 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; 160 } 161 162 DCHECK(!ContainsKey(instance_info_, embedded_worker_id)) 163 << embedded_worker_id << " already has a process allocated"; 164 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; 177 } 178 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; 186 } 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; 201 } 202 203 instance_info_.insert( 204 std::make_pair(embedded_worker_id, ProcessInfo(site_instance))); 205 206 static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount(); 207 BrowserThread::PostTask( 208 BrowserThread::IO, 209 FROM_HERE, 210 base::Bind(callback, SERVICE_WORKER_OK, rph->GetID())); 211} 212 213void 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; 222 } 223 if (process_id_for_test_ != -1) { 224 // Unittests don't increment or decrement the worker refcount of a 225 // RenderProcessHost. 226 return; 227 } 228 if (browser_context_ == NULL) { 229 // Shutdown already released all instances. 230 DCHECK(instance_info_.empty()); 231 return; 232 } 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?"; 247 } 248 static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount(); 249 instance_info_.erase(info); 250} 251 252std::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>(); 257 258 std::vector<std::pair<int, int> > counted( 259 it->second.begin(), it->second.end()); 260 std::sort(counted.begin(), counted.end(), SecondGreater()); 261 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; 266} 267 268} // namespace content 269 270namespace 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. 274void DefaultDeleter<content::ServiceWorkerProcessManager>::operator()( 275 content::ServiceWorkerProcessManager* ptr) const { 276 content::BrowserThread::DeleteSoon( 277 content::BrowserThread::UI, FROM_HERE, ptr); 278} 279} // namespace base 280