Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/ipc/PreallocatedProcessManager.cpp
Line
Count
Source (jump to first uncovered line)
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
0
#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
PreallocatedProcessManagerImpl::sSingleton;
76
77
/* static */ PreallocatedProcessManagerImpl*
78
PreallocatedProcessManagerImpl::Singleton()
79
0
{
80
0
  MOZ_ASSERT(NS_IsMainThread());
81
0
  if (!sSingleton) {
82
0
    sSingleton = new PreallocatedProcessManagerImpl();
83
0
    sSingleton->Init();
84
0
    ClearOnShutdown(&sSingleton);
85
0
  }
86
0
87
0
  return sSingleton;
88
0
}
89
90
NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver)
91
92
PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
93
  : mEnabled(false)
94
  , mShutdown(false)
95
0
{}
96
97
void
98
PreallocatedProcessManagerImpl::Init()
99
0
{
100
0
  Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
101
0
  // We have to respect processCount at all time. This is especially important
102
0
  // for testing.
103
0
  Preferences::AddStrongObserver(this, "dom.ipc.processCount");
104
0
  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
105
0
  if (os) {
106
0
    os->AddObserver(this, "ipc:content-shutdown",
107
0
                    /* weakRef = */ false);
108
0
    os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
109
0
                    /* weakRef = */ false);
110
0
    os->AddObserver(this, "profile-change-teardown",
111
0
                    /* weakRef = */ false);
112
0
  }
113
0
  RereadPrefs();
114
0
}
115
116
NS_IMETHODIMP
117
PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
118
                                        const char* aTopic,
119
                                        const char16_t* aData)
120
0
{
121
0
  if (!strcmp("ipc:content-shutdown", aTopic)) {
122
0
    ObserveProcessShutdown(aSubject);
123
0
  } else if (!strcmp("nsPref:changed", aTopic)) {
124
0
    // 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
0
    }
136
0
    // Let's prevent any new preallocated processes from starting. ContentParent will
137
0
    // handle the shutdown of the existing process and the mPreallocatedProcess reference
138
0
    // will be cleared by the ClearOnShutdown of the manager singleton.
139
0
    mShutdown = true;
140
0
  } else {
141
0
    MOZ_ASSERT(false);
142
0
  }
143
0
144
0
  return NS_OK;
145
0
}
146
147
void
148
PreallocatedProcessManagerImpl::RereadPrefs()
149
0
{
150
0
  if (mozilla::BrowserTabsRemoteAutostart() &&
151
0
      Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
152
0
    Enable();
153
0
  } else {
154
0
    Disable();
155
0
  }
156
0
157
0
  if (ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
158
0
    CloseProcess();
159
0
  }
160
0
}
161
162
already_AddRefed<ContentParent>
163
PreallocatedProcessManagerImpl::Take()
164
0
{
165
0
  if (!mEnabled || mShutdown) {
166
0
    return nullptr;
167
0
  }
168
0
169
0
  if (mPreallocatedProcess) {
170
0
    // The preallocated process is taken. Let's try to start up a new one soon.
171
0
    ProcessPriorityManager::SetProcessPriority(mPreallocatedProcess,
172
0
                                               PROCESS_PRIORITY_FOREGROUND);
173
0
    AllocateOnIdle();
174
0
  }
175
0
176
0
  return mPreallocatedProcess.forget();
177
0
}
178
179
bool
180
PreallocatedProcessManagerImpl::Provide(ContentParent* aParent)
181
0
{
182
0
  if (mEnabled && !mShutdown && !mPreallocatedProcess) {
183
0
    mPreallocatedProcess = aParent;
184
0
  }
185
0
186
0
  // We might get a call from both NotifyTabDestroying and NotifyTabDestroyed with the same
187
0
  // ContentParent. Returning true here for both calls is important to avoid the cached process
188
0
  // to be destroyed.
189
0
  return aParent == mPreallocatedProcess;
190
0
}
191
192
void
193
PreallocatedProcessManagerImpl::Enable()
194
0
{
195
0
  if (mEnabled) {
196
0
    return;
197
0
  }
198
0
199
0
  mEnabled = true;
200
0
  AllocateAfterDelay();
201
0
}
202
203
void
204
PreallocatedProcessManagerImpl::AddBlocker(ContentParent* aParent)
205
0
{
206
0
  uint64_t childID = aParent->ChildID();
207
0
  MOZ_ASSERT(!mBlockers.Contains(childID));
208
0
  mBlockers.PutEntry(childID);
209
0
}
210
211
void
212
PreallocatedProcessManagerImpl::RemoveBlocker(ContentParent* aParent)
213
0
{
214
0
  uint64_t childID = aParent->ChildID();
215
0
  MOZ_ASSERT(mBlockers.Contains(childID));
216
0
  mBlockers.RemoveEntry(childID);
217
0
  if (!mPreallocatedProcess && mBlockers.IsEmpty()) {
218
0
    AllocateAfterDelay();
219
0
  }
220
0
}
221
222
bool
223
PreallocatedProcessManagerImpl::CanAllocate()
224
0
{
225
0
  return mEnabled &&
226
0
         mBlockers.IsEmpty() &&
227
0
         !mPreallocatedProcess &&
228
0
         !mShutdown &&
229
0
         !ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
230
0
}
231
232
void
233
PreallocatedProcessManagerImpl::AllocateAfterDelay()
234
0
{
235
0
  if (!mEnabled) {
236
0
    return;
237
0
  }
238
0
239
0
  NS_DelayedDispatchToCurrentThread(
240
0
    NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateOnIdle",
241
0
                      this,
242
0
                      &PreallocatedProcessManagerImpl::AllocateOnIdle),
243
0
    Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
244
0
                         DEFAULT_ALLOCATE_DELAY));
245
0
}
246
247
void
248
PreallocatedProcessManagerImpl::AllocateOnIdle()
249
0
{
250
0
  if (!mEnabled) {
251
0
    return;
252
0
  }
253
0
254
0
  NS_IdleDispatchToCurrentThread(
255
0
    NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateNow",
256
0
                      this,
257
0
                      &PreallocatedProcessManagerImpl::AllocateNow));
258
0
}
259
260
void
261
PreallocatedProcessManagerImpl::AllocateNow()
262
0
{
263
0
  if (!CanAllocate()) {
264
0
    if (mEnabled && !mShutdown && !mPreallocatedProcess && !mBlockers.IsEmpty()) {
265
0
      // If it's too early to allocate a process let's retry later.
266
0
      AllocateAfterDelay();
267
0
    }
268
0
    return;
269
0
  }
270
0
271
0
  mPreallocatedProcess = ContentParent::PreallocateProcess();
272
0
}
273
274
void
275
PreallocatedProcessManagerImpl::Disable()
276
0
{
277
0
  if (!mEnabled) {
278
0
    return;
279
0
  }
280
0
281
0
  mEnabled = false;
282
0
  CloseProcess();
283
0
}
284
285
void
286
PreallocatedProcessManagerImpl::CloseProcess()
287
0
{
288
0
  if (mPreallocatedProcess) {
289
0
    mPreallocatedProcess->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE);
290
0
    mPreallocatedProcess = nullptr;
291
0
  }
292
0
}
293
294
void
295
PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
296
0
{
297
0
  nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
298
0
  NS_ENSURE_TRUE_VOID(props);
299
0
300
0
  uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
301
0
  props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
302
0
  NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
303
0
304
0
  if (mPreallocatedProcess && childID == mPreallocatedProcess->ChildID()) {
305
0
    mPreallocatedProcess = nullptr;
306
0
  }
307
0
308
0
  mBlockers.RemoveEntry(childID);
309
0
}
310
311
inline PreallocatedProcessManagerImpl* GetPPMImpl()
312
0
{
313
0
  return PreallocatedProcessManagerImpl::Singleton();
314
0
}
315
316
/* static */ void
317
PreallocatedProcessManager::AddBlocker(ContentParent* aParent)
318
0
{
319
0
  GetPPMImpl()->AddBlocker(aParent);
320
0
}
321
322
/* static */ void
323
PreallocatedProcessManager::RemoveBlocker(ContentParent* aParent)
324
0
{
325
0
  GetPPMImpl()->RemoveBlocker(aParent);
326
0
}
327
328
/* static */ already_AddRefed<ContentParent>
329
PreallocatedProcessManager::Take()
330
0
{
331
0
  return GetPPMImpl()->Take();
332
0
}
333
334
/* static */ bool
335
PreallocatedProcessManager::Provide(ContentParent* aParent)
336
0
{
337
0
  return GetPPMImpl()->Provide(aParent);
338
0
}
339
340
} // namespace mozilla