PreallocatedProcessManager.cpp (14413B)
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 9 #include "ProcessPriorityManager.h" 10 #include "mozilla/AppShutdown.h" 11 #include "mozilla/ClearOnShutdown.h" 12 #include "mozilla/Preferences.h" 13 #include "mozilla/ProfilerMarkers.h" 14 #include "mozilla/StaticPrefs_dom.h" 15 #include "mozilla/dom/ContentParent.h" 16 #include "mozilla/dom/ScriptSettings.h" 17 #include "nsIPropertyBag2.h" 18 #include "nsIXULRuntime.h" 19 #include "nsServiceManagerUtils.h" 20 #include "nsTArray.h" 21 #include "prsystem.h" 22 23 using namespace mozilla::hal; 24 using namespace mozilla::dom; 25 26 namespace mozilla { 27 /** 28 * This singleton class implements the static methods on 29 * PreallocatedProcessManager. 30 */ 31 class PreallocatedProcessManagerImpl final : public nsIObserver { 32 friend class PreallocatedProcessManager; 33 34 public: 35 static PreallocatedProcessManagerImpl* Singleton(); 36 37 NS_DECL_ISUPPORTS 38 NS_DECL_NSIOBSERVER 39 40 // See comments on PreallocatedProcessManager for these methods. 41 void AddBlocker(ContentParent* aParent); 42 void RemoveBlocker(ContentParent* aParent); 43 UniqueContentParentKeepAlive Take(const nsACString& aRemoteType); 44 void Erase(ContentParent* aParent); 45 46 private: 47 static const char* const kObserverTopics[]; 48 49 static StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton; 50 51 PreallocatedProcessManagerImpl(); 52 ~PreallocatedProcessManagerImpl(); 53 PreallocatedProcessManagerImpl(const PreallocatedProcessManagerImpl&) = 54 delete; 55 56 const PreallocatedProcessManagerImpl& operator=( 57 const PreallocatedProcessManagerImpl&) = delete; 58 59 void Init(); 60 61 bool CanAllocate(); 62 void AllocateAfterDelay(); 63 void AllocateOnIdle(); 64 void AllocateNow(); 65 66 void RereadPrefs(); 67 void Enable(uint32_t aProcesses); 68 void Disable(); 69 void CloseProcesses(); 70 71 bool IsEmpty() const { return mPreallocatedProcesses.IsEmpty(); } 72 static bool IsShutdown() { 73 return AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed); 74 } 75 bool IsEnabled() { return mEnabled && !IsShutdown(); } 76 77 bool mEnabled; 78 uint32_t mNumberPreallocs; 79 AutoTArray<UniqueContentParentKeepAlive, 3> mPreallocatedProcesses; 80 // Even if we have multiple PreallocatedProcessManagerImpls, we'll have 81 // one blocker counter 82 static uint32_t sNumBlockers; 83 TimeStamp mBlockingStartTime; 84 }; 85 86 /* static */ 87 uint32_t PreallocatedProcessManagerImpl::sNumBlockers = 0; 88 89 const char* const PreallocatedProcessManagerImpl::kObserverTopics[] = { 90 "memory-pressure", 91 "profile-change-teardown", 92 NS_XPCOM_SHUTDOWN_OBSERVER_ID, 93 }; 94 95 /* static */ 96 StaticRefPtr<PreallocatedProcessManagerImpl> 97 PreallocatedProcessManagerImpl::sSingleton; 98 99 /* static */ 100 PreallocatedProcessManagerImpl* PreallocatedProcessManagerImpl::Singleton() { 101 MOZ_ASSERT(NS_IsMainThread()); 102 if (!sSingleton) { 103 sSingleton = new PreallocatedProcessManagerImpl; 104 sSingleton->Init(); 105 ClearOnShutdown(&sSingleton); 106 } 107 return sSingleton; 108 // PreallocatedProcessManagers live until shutdown 109 } 110 111 NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver) 112 113 PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl() 114 : mEnabled(false), mNumberPreallocs(1) {} 115 116 PreallocatedProcessManagerImpl::~PreallocatedProcessManagerImpl() { 117 // Note: mPreallocatedProcesses may not be null, but all processes should 118 // be dead (IsDead==true). We block Erase() when our observer sees 119 // shutdown starting. 120 } 121 122 void PreallocatedProcessManagerImpl::Init() { 123 Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled"); 124 // We have to respect processCount at all time. This is especially important 125 // for testing. 126 Preferences::AddStrongObserver(this, "dom.ipc.processCount"); 127 // A StaticPref, but we need to adjust the number of preallocated processes 128 // if the value goes up or down, so we need to run code on change. 129 Preferences::AddStrongObserver(this, 130 "dom.ipc.processPrelaunch.fission.number"); 131 132 nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 133 MOZ_ASSERT(os); 134 for (auto topic : kObserverTopics) { 135 os->AddObserver(this, topic, /* ownsWeak */ false); 136 } 137 RereadPrefs(); 138 } 139 140 NS_IMETHODIMP 141 PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject, 142 const char* aTopic, 143 const char16_t* aData) { 144 if (!strcmp("nsPref:changed", aTopic)) { 145 // The only other observer we registered was for our prefs. 146 RereadPrefs(); 147 } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic) || 148 !strcmp("profile-change-teardown", aTopic)) { 149 Preferences::RemoveObserver(this, "dom.ipc.processPrelaunch.enabled"); 150 Preferences::RemoveObserver(this, "dom.ipc.processCount"); 151 Preferences::RemoveObserver(this, 152 "dom.ipc.processPrelaunch.fission.number"); 153 154 nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 155 MOZ_ASSERT(os); 156 for (auto topic : kObserverTopics) { 157 os->RemoveObserver(this, topic); 158 } 159 } else if (!strcmp("memory-pressure", aTopic)) { 160 CloseProcesses(); 161 } else { 162 MOZ_ASSERT_UNREACHABLE("Unknown topic"); 163 } 164 165 return NS_OK; 166 } 167 168 void PreallocatedProcessManagerImpl::RereadPrefs() { 169 if (mozilla::BrowserTabsRemoteAutostart() && 170 Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) { 171 int32_t number = 1; 172 if (mozilla::FissionAutostart()) { 173 number = StaticPrefs::dom_ipc_processPrelaunch_fission_number(); 174 // limit preallocated processes on low-mem machines 175 PRUint64 bytes = PR_GetPhysicalMemorySize(); 176 if (bytes > 0 && 177 bytes <= 178 StaticPrefs::dom_ipc_processPrelaunch_lowmem_mb() * 1024 * 1024) { 179 number = 1; 180 } 181 } 182 if (number >= 0) { 183 Enable(number); 184 // We have one prealloc queue for all types except File now 185 if (static_cast<uint64_t>(number) < mPreallocatedProcesses.Length()) { 186 CloseProcesses(); 187 } 188 } 189 } else { 190 Disable(); 191 } 192 } 193 194 UniqueContentParentKeepAlive PreallocatedProcessManagerImpl::Take( 195 const nsACString& aRemoteType) { 196 if (!IsEnabled()) { 197 return nullptr; 198 } 199 UniqueContentParentKeepAlive process; 200 if (!IsEmpty()) { 201 process = std::move(mPreallocatedProcesses.ElementAt(0)); 202 mPreallocatedProcesses.RemoveElementAt(0); 203 204 // Don't set the priority to FOREGROUND here, since it may not have 205 // finished starting 206 207 // We took a preallocated process. Let's try to start up a new one 208 // soon. 209 ContentParent* last = mPreallocatedProcesses.SafeLastElement(nullptr).get(); 210 // There could be a launching process that isn't the last, but that's 211 // ok (and unlikely) 212 if (!last || !last->IsLaunching()) { 213 AllocateAfterDelay(); 214 } 215 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 216 ("Use prealloc process %p%s, %lu available", process.get(), 217 process->IsLaunching() ? " (still launching)" : "", 218 (unsigned long)mPreallocatedProcesses.Length())); 219 } 220 if (process && !process->IsLaunching()) { 221 ProcessPriorityManager::SetProcessPriority(process.get(), 222 PROCESS_PRIORITY_FOREGROUND); 223 } // else this will get set by the caller when they call InitInternal() 224 225 return process; 226 } 227 228 void PreallocatedProcessManagerImpl::Erase(ContentParent* aParent) { 229 (void)mPreallocatedProcesses.RemoveElement(aParent); 230 } 231 232 void PreallocatedProcessManagerImpl::Enable(uint32_t aProcesses) { 233 mNumberPreallocs = aProcesses; 234 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 235 ("Enabling preallocation: %u", aProcesses)); 236 if (mEnabled || IsShutdown()) { 237 return; 238 } 239 240 mEnabled = true; 241 AllocateAfterDelay(); 242 } 243 244 void PreallocatedProcessManagerImpl::AddBlocker(ContentParent* aParent) { 245 if (sNumBlockers == 0) { 246 mBlockingStartTime = TimeStamp::Now(); 247 } 248 sNumBlockers++; 249 } 250 251 void PreallocatedProcessManagerImpl::RemoveBlocker(ContentParent* aParent) { 252 // This used to assert that the blocker existed, but preallocated 253 // processes aren't blockers anymore because it's not useful and 254 // interferes with async launch, and it's simpler if content 255 // processes don't need to remember whether they were preallocated. 256 257 MOZ_DIAGNOSTIC_ASSERT(sNumBlockers > 0); 258 sNumBlockers--; 259 if (sNumBlockers == 0) { 260 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 261 ("Blocked preallocation for %fms", 262 (TimeStamp::Now() - mBlockingStartTime).ToMilliseconds())); 263 PROFILER_MARKER_TEXT("Process", DOM, 264 MarkerTiming::IntervalUntilNowFrom(mBlockingStartTime), 265 "Blocked preallocation"); 266 if (IsEmpty()) { 267 AllocateAfterDelay(); 268 } 269 } 270 } 271 272 bool PreallocatedProcessManagerImpl::CanAllocate() { 273 return IsEnabled() && sNumBlockers == 0 && 274 mPreallocatedProcesses.Length() < mNumberPreallocs && !IsShutdown() && 275 (FissionAutostart() || 276 !ContentParent::IsMaxProcessCountReached(DEFAULT_REMOTE_TYPE)); 277 } 278 279 void PreallocatedProcessManagerImpl::AllocateAfterDelay() { 280 if (!IsEnabled()) { 281 return; 282 } 283 long delay = StaticPrefs::dom_ipc_processPrelaunch_delayMs(); 284 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 285 ("Starting delayed process start, delay=%ld", delay)); 286 NS_DelayedDispatchToCurrentThread( 287 NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateOnIdle", this, 288 &PreallocatedProcessManagerImpl::AllocateOnIdle), 289 delay); 290 } 291 292 void PreallocatedProcessManagerImpl::AllocateOnIdle() { 293 if (!IsEnabled()) { 294 return; 295 } 296 297 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 298 ("Starting process allocate on idle")); 299 NS_DispatchToCurrentThreadQueue( 300 NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateNow", this, 301 &PreallocatedProcessManagerImpl::AllocateNow), 302 EventQueuePriority::Idle); 303 } 304 305 void PreallocatedProcessManagerImpl::AllocateNow() { 306 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 307 ("Trying to start process now")); 308 if (!CanAllocate()) { 309 if (IsEnabled() && IsEmpty() && sNumBlockers > 0) { 310 // If it's too early to allocate a process let's retry later. 311 AllocateAfterDelay(); 312 } 313 return; 314 } 315 316 UniqueContentParentKeepAlive process = ContentParent::MakePreallocProcess(); 317 if (!process) { 318 // We fully failed to create a prealloc process. Don't try to kick off a new 319 // preallocation here, to avoid possible looping. 320 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 321 ("Failed to launch prealloc process")); 322 return; 323 } 324 325 process->WaitForLaunchAsync(PROCESS_PRIORITY_PREALLOC) 326 ->Then( 327 GetCurrentSerialEventTarget(), __func__, 328 [self = RefPtr{this}, 329 process = RefPtr{process.get()}](UniqueContentParentKeepAlive&&) { 330 if (process->IsDead()) { 331 self->Erase(process); 332 // Process died in startup (before we could add it). If it 333 // dies after this, MarkAsDead() will Erase() this entry. 334 // Shouldn't be in the sBrowserContentParents, so we don't need 335 // RemoveFromList(). We won't try to kick off a new 336 // preallocation here, to avoid possible looping if something is 337 // causing them to consistently fail; if everything is ok on the 338 // next allocation request we'll kick off creation. 339 } else if (self->CanAllocate()) { 340 // Continue prestarting processes if needed 341 if (self->mPreallocatedProcesses.Length() < 342 self->mNumberPreallocs) { 343 self->AllocateOnIdle(); 344 } 345 } 346 }, 347 [self = RefPtr{this}, process = RefPtr{process.get()}]() { 348 self->Erase(process); 349 }); 350 351 mPreallocatedProcesses.AppendElement(std::move(process)); 352 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 353 ("Preallocated = %lu of %d processes", 354 (unsigned long)mPreallocatedProcesses.Length(), mNumberPreallocs)); 355 } 356 357 void PreallocatedProcessManagerImpl::Disable() { 358 if (!mEnabled) { 359 return; 360 } 361 362 mEnabled = false; 363 CloseProcesses(); 364 } 365 366 void PreallocatedProcessManagerImpl::CloseProcesses() { 367 // Drop our KeepAlives on these processes. This will automatically lead to the 368 // processes being shut down when no keepalives are left. 369 mPreallocatedProcesses.Clear(); 370 } 371 372 inline PreallocatedProcessManagerImpl* 373 PreallocatedProcessManager::GetPPMImpl() { 374 if (PreallocatedProcessManagerImpl::IsShutdown()) { 375 return nullptr; 376 } 377 return PreallocatedProcessManagerImpl::Singleton(); 378 } 379 380 /* static */ 381 bool PreallocatedProcessManager::Enabled() { 382 if (auto impl = GetPPMImpl()) { 383 return impl->IsEnabled(); 384 } 385 return false; 386 } 387 388 /* static */ 389 void PreallocatedProcessManager::AddBlocker(const nsACString& aRemoteType, 390 ContentParent* aParent) { 391 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 392 ("AddBlocker: %s %p (sNumBlockers=%d)", 393 PromiseFlatCString(aRemoteType).get(), aParent, 394 PreallocatedProcessManagerImpl::sNumBlockers)); 395 if (auto impl = GetPPMImpl()) { 396 impl->AddBlocker(aParent); 397 } 398 } 399 400 /* static */ 401 void PreallocatedProcessManager::RemoveBlocker(const nsACString& aRemoteType, 402 ContentParent* aParent) { 403 MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug, 404 ("RemoveBlocker: %s %p (sNumBlockers=%d)", 405 PromiseFlatCString(aRemoteType).get(), aParent, 406 PreallocatedProcessManagerImpl::sNumBlockers)); 407 if (auto impl = GetPPMImpl()) { 408 impl->RemoveBlocker(aParent); 409 } 410 } 411 412 /* static */ 413 UniqueContentParentKeepAlive PreallocatedProcessManager::Take( 414 const nsACString& aRemoteType) { 415 if (auto impl = GetPPMImpl()) { 416 return impl->Take(aRemoteType); 417 } 418 return nullptr; 419 } 420 421 /* static */ 422 void PreallocatedProcessManager::Erase(ContentParent* aParent) { 423 if (auto impl = GetPPMImpl()) { 424 impl->Erase(aParent); 425 } 426 } 427 428 } // namespace mozilla