Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/PreallocatedProcessManager.h"
8 : #include "mozilla/ClearOnShutdown.h"
9 : #include "mozilla/Preferences.h"
10 : #include "mozilla/Unused.h"
11 : #include "mozilla/dom/ContentParent.h"
12 : #include "mozilla/dom/ScriptSettings.h"
13 : #include "nsIPropertyBag2.h"
14 : #include "ProcessPriorityManager.h"
15 : #include "nsServiceManagerUtils.h"
16 :
17 : // This number is fairly arbitrary ... the intention is to put off
18 : // launching another app process until the last one has finished
19 : // loading its content, to reduce CPU/memory/IO contention.
20 : #define DEFAULT_ALLOCATE_DELAY 1000
21 :
22 : using namespace mozilla;
23 : using namespace mozilla::hal;
24 : using namespace mozilla::dom;
25 :
26 : namespace mozilla {
27 :
28 : /**
29 : * This singleton class implements the static methods on
30 : * PreallocatedProcessManager.
31 : */
32 : class PreallocatedProcessManagerImpl final
33 : : public nsIObserver
34 : {
35 : public:
36 : static PreallocatedProcessManagerImpl* Singleton();
37 :
38 : NS_DECL_ISUPPORTS
39 : NS_DECL_NSIOBSERVER
40 :
41 : // See comments on PreallocatedProcessManager for these methods.
42 : void AddBlocker(ContentParent* aParent);
43 : void RemoveBlocker(ContentParent* aParent);
44 : already_AddRefed<ContentParent> Take();
45 : bool Provide(ContentParent* aParent);
46 :
47 : private:
48 : static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
49 :
50 : PreallocatedProcessManagerImpl();
51 0 : ~PreallocatedProcessManagerImpl() {}
52 : DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl);
53 :
54 : void Init();
55 :
56 : bool CanAllocate();
57 : void AllocateAfterDelay();
58 : void AllocateOnIdle();
59 : void AllocateNow();
60 :
61 : void RereadPrefs();
62 : void Enable();
63 : void Disable();
64 : void CloseProcess();
65 :
66 : void ObserveProcessShutdown(nsISupports* aSubject);
67 :
68 : bool mEnabled;
69 : bool mShutdown;
70 : RefPtr<ContentParent> mPreallocatedProcess;
71 : nsTHashtable<nsUint64HashKey> mBlockers;
72 : };
73 :
74 : /* static */ StaticRefPtr<PreallocatedProcessManagerImpl>
75 3 : PreallocatedProcessManagerImpl::sSingleton;
76 :
77 : /* static */ PreallocatedProcessManagerImpl*
78 4 : PreallocatedProcessManagerImpl::Singleton()
79 : {
80 4 : MOZ_ASSERT(NS_IsMainThread());
81 4 : if (!sSingleton) {
82 1 : sSingleton = new PreallocatedProcessManagerImpl();
83 1 : sSingleton->Init();
84 1 : ClearOnShutdown(&sSingleton);
85 : }
86 :
87 4 : return sSingleton;
88 : }
89 :
90 27 : NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver)
91 :
92 1 : PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
93 : : mEnabled(false)
94 1 : , mShutdown(false)
95 1 : {}
96 :
97 : void
98 1 : PreallocatedProcessManagerImpl::Init()
99 : {
100 1 : Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
101 : // We have to respect processCount at all time. This is especially important
102 : // for testing.
103 1 : Preferences::AddStrongObserver(this, "dom.ipc.processCount");
104 2 : nsCOMPtr<nsIObserverService> os = services::GetObserverService();
105 1 : if (os) {
106 2 : os->AddObserver(this, "ipc:content-shutdown",
107 2 : /* weakRef = */ false);
108 2 : os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
109 2 : /* weakRef = */ false);
110 2 : os->AddObserver(this, "profile-change-teardown",
111 2 : /* weakRef = */ false);
112 : }
113 1 : RereadPrefs();
114 1 : }
115 :
116 : NS_IMETHODIMP
117 0 : PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
118 : const char* aTopic,
119 : const char16_t* aData)
120 : {
121 0 : if (!strcmp("ipc:content-shutdown", aTopic)) {
122 0 : ObserveProcessShutdown(aSubject);
123 0 : } else if (!strcmp("nsPref:changed", aTopic)) {
124 : // The only other observer we registered was for our prefs.
125 0 : RereadPrefs();
126 0 : } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic) ||
127 0 : !strcmp("profile-change-teardown", aTopic)) {
128 0 : Preferences::RemoveObserver(this, "dom.ipc.processPrelaunch.enabled");
129 0 : Preferences::RemoveObserver(this, "dom.ipc.processCount");
130 0 : nsCOMPtr<nsIObserverService> os = services::GetObserverService();
131 0 : if (os) {
132 0 : os->RemoveObserver(this, "ipc:content-shutdown");
133 0 : os->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
134 0 : os->RemoveObserver(this, "profile-change-teardown");
135 : }
136 0 : mShutdown = true;
137 0 : CloseProcess();
138 : } else {
139 0 : MOZ_ASSERT(false);
140 : }
141 :
142 0 : return NS_OK;
143 : }
144 :
145 : void
146 1 : PreallocatedProcessManagerImpl::RereadPrefs()
147 : {
148 2 : if (mozilla::BrowserTabsRemoteAutostart() &&
149 1 : Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
150 1 : Enable();
151 : } else {
152 0 : Disable();
153 : }
154 :
155 1 : if (ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
156 0 : CloseProcess();
157 : }
158 1 : }
159 :
160 : already_AddRefed<ContentParent>
161 1 : PreallocatedProcessManagerImpl::Take()
162 : {
163 1 : if (!mEnabled || mShutdown) {
164 0 : return nullptr;
165 : }
166 :
167 1 : if (mPreallocatedProcess) {
168 : // The preallocated process is taken. Let's try to start up a new one soon.
169 0 : AllocateOnIdle();
170 : }
171 :
172 1 : return mPreallocatedProcess.forget();
173 : }
174 :
175 : bool
176 0 : PreallocatedProcessManagerImpl::Provide(ContentParent* aParent)
177 : {
178 0 : if (mEnabled && !mShutdown && !mPreallocatedProcess) {
179 0 : mPreallocatedProcess = aParent;
180 : }
181 :
182 : // We might get a call from both NotifyTabDestroying and NotifyTabDestroyed with the same
183 : // ContentParent. Returning true here for both calls is important to avoid the cached process
184 : // to be destroyed.
185 0 : return aParent == mPreallocatedProcess;
186 : }
187 :
188 : void
189 1 : PreallocatedProcessManagerImpl::Enable()
190 : {
191 1 : if (mEnabled) {
192 0 : return;
193 : }
194 :
195 1 : mEnabled = true;
196 1 : AllocateAfterDelay();
197 : }
198 :
199 : void
200 2 : PreallocatedProcessManagerImpl::AddBlocker(ContentParent* aParent)
201 : {
202 2 : uint64_t childID = aParent->ChildID();
203 2 : MOZ_ASSERT(!mBlockers.Contains(childID));
204 2 : mBlockers.PutEntry(childID);
205 2 : }
206 :
207 : void
208 1 : PreallocatedProcessManagerImpl::RemoveBlocker(ContentParent* aParent)
209 : {
210 1 : uint64_t childID = aParent->ChildID();
211 1 : MOZ_ASSERT(mBlockers.Contains(childID));
212 1 : mBlockers.RemoveEntry(childID);
213 1 : if (!mPreallocatedProcess && mBlockers.IsEmpty()) {
214 1 : AllocateAfterDelay();
215 : }
216 1 : }
217 :
218 : bool
219 1 : PreallocatedProcessManagerImpl::CanAllocate()
220 : {
221 2 : return mEnabled &&
222 2 : mBlockers.IsEmpty() &&
223 2 : !mPreallocatedProcess &&
224 5 : !mShutdown &&
225 3 : !ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
226 : }
227 :
228 : void
229 2 : PreallocatedProcessManagerImpl::AllocateAfterDelay()
230 : {
231 2 : if (!mEnabled) {
232 0 : return;
233 : }
234 :
235 6 : NS_DelayedDispatchToCurrentThread(
236 4 : NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateOnIdle",
237 : this,
238 : &PreallocatedProcessManagerImpl::AllocateOnIdle),
239 : Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
240 2 : DEFAULT_ALLOCATE_DELAY));
241 : }
242 :
243 : void
244 2 : PreallocatedProcessManagerImpl::AllocateOnIdle()
245 : {
246 2 : if (!mEnabled) {
247 0 : return;
248 : }
249 :
250 4 : NS_IdleDispatchToCurrentThread(
251 4 : NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateNow",
252 : this,
253 2 : &PreallocatedProcessManagerImpl::AllocateNow));
254 : }
255 :
256 : void
257 1 : PreallocatedProcessManagerImpl::AllocateNow()
258 : {
259 1 : if (!CanAllocate()) {
260 0 : if (mEnabled && !mShutdown && !mPreallocatedProcess && !mBlockers.IsEmpty()) {
261 : // If it's too early to allocate a process let's retry later.
262 0 : AllocateAfterDelay();
263 : }
264 0 : return;
265 : }
266 :
267 1 : mPreallocatedProcess = ContentParent::PreallocateProcess();
268 : }
269 :
270 : void
271 0 : PreallocatedProcessManagerImpl::Disable()
272 : {
273 0 : if (!mEnabled) {
274 0 : return;
275 : }
276 :
277 0 : mEnabled = false;
278 0 : CloseProcess();
279 : }
280 :
281 : void
282 0 : PreallocatedProcessManagerImpl::CloseProcess()
283 : {
284 0 : if (mPreallocatedProcess) {
285 0 : mPreallocatedProcess->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE);
286 0 : mPreallocatedProcess = nullptr;
287 : }
288 0 : }
289 :
290 : void
291 0 : PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
292 : {
293 0 : nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
294 0 : NS_ENSURE_TRUE_VOID(props);
295 :
296 0 : uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
297 0 : props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
298 0 : NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
299 :
300 0 : if (mPreallocatedProcess && childID == mPreallocatedProcess->ChildID()) {
301 0 : mPreallocatedProcess = nullptr;
302 : }
303 :
304 0 : mBlockers.RemoveEntry(childID);
305 : }
306 :
307 4 : inline PreallocatedProcessManagerImpl* GetPPMImpl()
308 : {
309 4 : return PreallocatedProcessManagerImpl::Singleton();
310 : }
311 :
312 : /* static */ void
313 2 : PreallocatedProcessManager::AddBlocker(ContentParent* aParent)
314 : {
315 2 : GetPPMImpl()->AddBlocker(aParent);
316 2 : }
317 :
318 : /* static */ void
319 1 : PreallocatedProcessManager::RemoveBlocker(ContentParent* aParent)
320 : {
321 1 : GetPPMImpl()->RemoveBlocker(aParent);
322 1 : }
323 :
324 : /* static */ already_AddRefed<ContentParent>
325 1 : PreallocatedProcessManager::Take()
326 : {
327 1 : return GetPPMImpl()->Take();
328 : }
329 :
330 : /* static */ bool
331 0 : PreallocatedProcessManager::Provide(ContentParent* aParent)
332 : {
333 0 : return GetPPMImpl()->Provide(aParent);
334 : }
335 :
336 : } // namespace mozilla
|