• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
13 namespace content {
14 
IncrementWorkerRefCountByPid(int process_id)15 static bool IncrementWorkerRefCountByPid(int process_id) {
16   RenderProcessHost* rph = RenderProcessHost::FromID(process_id);
17   if (!rph || rph->FastShutdownStarted())
18     return false;
19 
20   static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
21   return true;
22 }
23 
ProcessInfo(const scoped_refptr<SiteInstance> & site_instance)24 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(
25     const scoped_refptr<SiteInstance>& site_instance)
26     : site_instance(site_instance),
27       process_id(site_instance->GetProcess()->GetID()) {
28 }
29 
ProcessInfo(int process_id)30 ServiceWorkerProcessManager::ProcessInfo::ProcessInfo(int process_id)
31     : process_id(process_id) {
32 }
33 
~ProcessInfo()34 ServiceWorkerProcessManager::ProcessInfo::~ProcessInfo() {
35 }
36 
ServiceWorkerProcessManager(BrowserContext * browser_context)37 ServiceWorkerProcessManager::ServiceWorkerProcessManager(
38     BrowserContext* browser_context)
39     : browser_context_(browser_context),
40       process_id_for_test_(-1),
41       weak_this_factory_(this),
42       weak_this_(weak_this_factory_.GetWeakPtr()) {
43 }
44 
~ServiceWorkerProcessManager()45 ServiceWorkerProcessManager::~ServiceWorkerProcessManager() {
46   DCHECK_CURRENTLY_ON(BrowserThread::UI);
47   DCHECK(browser_context_ == NULL)
48       << "Call Shutdown() before destroying |this|, so that racing method "
49       << "invocations don't use a destroyed BrowserContext.";
50 }
51 
Shutdown()52 void ServiceWorkerProcessManager::Shutdown() {
53   browser_context_ = NULL;
54   for (std::map<int, ProcessInfo>::const_iterator it = instance_info_.begin();
55        it != instance_info_.end();
56        ++it) {
57     RenderProcessHost* rph = RenderProcessHost::FromID(it->second.process_id);
58     DCHECK(rph);
59     static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
60   }
61   instance_info_.clear();
62 }
63 
AllocateWorkerProcess(int embedded_worker_id,const std::vector<int> & process_ids,const GURL & script_url,const base::Callback<void (ServiceWorkerStatusCode,int process_id)> & callback)64 void ServiceWorkerProcessManager::AllocateWorkerProcess(
65     int embedded_worker_id,
66     const std::vector<int>& process_ids,
67     const GURL& script_url,
68     const base::Callback<void(ServiceWorkerStatusCode, int process_id)>&
69         callback) {
70   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
71     BrowserThread::PostTask(
72         BrowserThread::UI,
73         FROM_HERE,
74         base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess,
75                    weak_this_,
76                    embedded_worker_id,
77                    process_ids,
78                    script_url,
79                    callback));
80     return;
81   }
82 
83   if (process_id_for_test_ != -1) {
84     // Let tests specify the returned process ID. Note: We may need to be able
85     // to specify the error code too.
86     BrowserThread::PostTask(
87         BrowserThread::IO,
88         FROM_HERE,
89         base::Bind(callback, SERVICE_WORKER_OK, process_id_for_test_));
90     return;
91   }
92 
93   DCHECK(!ContainsKey(instance_info_, embedded_worker_id))
94       << embedded_worker_id << " already has a process allocated";
95 
96   for (std::vector<int>::const_iterator it = process_ids.begin();
97        it != process_ids.end();
98        ++it) {
99     if (IncrementWorkerRefCountByPid(*it)) {
100       instance_info_.insert(
101           std::make_pair(embedded_worker_id, ProcessInfo(*it)));
102       BrowserThread::PostTask(BrowserThread::IO,
103                               FROM_HERE,
104                               base::Bind(callback, SERVICE_WORKER_OK, *it));
105       return;
106     }
107   }
108 
109   if (!browser_context_) {
110     // Shutdown has started.
111     BrowserThread::PostTask(
112         BrowserThread::IO,
113         FROM_HERE,
114         base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
115     return;
116   }
117   // No existing processes available; start a new one.
118   scoped_refptr<SiteInstance> site_instance =
119       SiteInstance::CreateForURL(browser_context_, script_url);
120   RenderProcessHost* rph = site_instance->GetProcess();
121   // This Init() call posts a task to the IO thread that adds the RPH's
122   // ServiceWorkerDispatcherHost to the
123   // EmbeddedWorkerRegistry::process_sender_map_.
124   if (!rph->Init()) {
125     LOG(ERROR) << "Couldn't start a new process!";
126     BrowserThread::PostTask(
127         BrowserThread::IO,
128         FROM_HERE,
129         base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1));
130     return;
131   }
132 
133   instance_info_.insert(
134       std::make_pair(embedded_worker_id, ProcessInfo(site_instance)));
135 
136   static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount();
137   BrowserThread::PostTask(
138       BrowserThread::IO,
139       FROM_HERE,
140       base::Bind(callback, SERVICE_WORKER_OK, rph->GetID()));
141 }
142 
ReleaseWorkerProcess(int embedded_worker_id)143 void ServiceWorkerProcessManager::ReleaseWorkerProcess(int embedded_worker_id) {
144   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
145     BrowserThread::PostTask(
146         BrowserThread::UI,
147         FROM_HERE,
148         base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess,
149                    weak_this_,
150                    embedded_worker_id));
151     return;
152   }
153   if (process_id_for_test_ != -1) {
154     // Unittests don't increment or decrement the worker refcount of a
155     // RenderProcessHost.
156     return;
157   }
158   if (browser_context_ == NULL) {
159     // Shutdown already released all instances.
160     DCHECK(instance_info_.empty());
161     return;
162   }
163   std::map<int, ProcessInfo>::iterator info =
164       instance_info_.find(embedded_worker_id);
165   DCHECK(info != instance_info_.end());
166   RenderProcessHost* rph = NULL;
167   if (info->second.site_instance) {
168     rph = info->second.site_instance->GetProcess();
169     DCHECK_EQ(info->second.process_id, rph->GetID())
170         << "A SiteInstance's process shouldn't get destroyed while we're "
171            "holding a reference to it. Was the reference actually held?";
172   } else {
173     rph = RenderProcessHost::FromID(info->second.process_id);
174     DCHECK(rph)
175         << "Process " << info->second.process_id
176         << " was destroyed unexpectedly. Did we actually hold a reference?";
177   }
178   static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount();
179   instance_info_.erase(info);
180 }
181 
182 }  // namespace content
183 
184 namespace base {
185 // Destroying ServiceWorkerProcessManagers only on the UI thread allows the
186 // member WeakPtr to safely guard the object's lifetime when used on that
187 // thread.
operator ()(content::ServiceWorkerProcessManager * ptr) const188 void DefaultDeleter<content::ServiceWorkerProcessManager>::operator()(
189     content::ServiceWorkerProcessManager* ptr) const {
190   content::BrowserThread::DeleteSoon(
191       content::BrowserThread::UI, FROM_HERE, ptr);
192 }
193 }  // namespace base
194