RDDProcessManager.cpp (12516B)
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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #include "RDDProcessManager.h" 7 8 #include "RDDChild.h" 9 #include "RDDProcessHost.h" 10 #include "mozilla/MemoryReportingProcess.h" 11 #include "mozilla/Preferences.h" 12 #include "mozilla/RemoteMediaManagerChild.h" 13 #include "mozilla/RemoteMediaManagerParent.h" 14 #include "mozilla/StaticPrefs_media.h" 15 #include "mozilla/SyncRunnable.h" // for LaunchRDDProcess 16 #include "mozilla/dom/ContentParent.h" 17 #include "mozilla/gfx/GPUProcessManager.h" 18 #include "mozilla/ipc/Endpoint.h" 19 #include "mozilla/ipc/ProcessChild.h" 20 #include "mozilla/layers/CompositorThread.h" 21 #include "mozilla/layers/VideoBridgeParent.h" 22 #include "nsAppRunner.h" 23 #include "nsContentUtils.h" 24 25 namespace mozilla { 26 27 using namespace gfx; 28 using namespace layers; 29 30 static StaticAutoPtr<RDDProcessManager> sRDDSingleton; 31 32 static bool sRDDProcessShutdown = false; 33 34 bool RDDProcessManager::IsShutdown() const { 35 MOZ_ASSERT(NS_IsMainThread()); 36 return sRDDProcessShutdown || !sRDDSingleton; 37 } 38 39 RDDProcessManager* RDDProcessManager::Get() { return sRDDSingleton; } 40 41 void RDDProcessManager::Initialize() { 42 MOZ_ASSERT(XRE_IsParentProcess()); 43 sRDDSingleton = new RDDProcessManager(); 44 } 45 46 void RDDProcessManager::Shutdown() { sRDDSingleton = nullptr; } 47 48 void RDDProcessManager::RDDProcessShutdown() { 49 MOZ_ASSERT(NS_IsMainThread()); 50 sRDDProcessShutdown = true; 51 if (sRDDSingleton) { 52 sRDDSingleton->DestroyProcess(); 53 } 54 } 55 56 RDDProcessManager::RDDProcessManager() 57 : mObserver(new Observer(this)), mTaskFactory(this) { 58 MOZ_COUNT_CTOR(RDDProcessManager); 59 // Start listening for pref changes so we can 60 // forward them to the process once it is running. 61 nsContentUtils::RegisterShutdownObserver(mObserver); 62 Preferences::AddStrongObserver(mObserver, ""); 63 } 64 65 RDDProcessManager::~RDDProcessManager() { 66 MOZ_COUNT_DTOR(RDDProcessManager); 67 MOZ_ASSERT(NS_IsMainThread()); 68 69 // The RDD process should have already been shut down. 70 MOZ_ASSERT(!mProcess && !mRDDChild); 71 } 72 73 NS_IMPL_ISUPPORTS(RDDProcessManager::Observer, nsIObserver); 74 75 RDDProcessManager::Observer::Observer(RDDProcessManager* aManager) 76 : mManager(aManager) {} 77 78 NS_IMETHODIMP 79 RDDProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic, 80 const char16_t* aData) { 81 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 82 mManager->OnXPCOMShutdown(); 83 } else if (!strcmp(aTopic, "nsPref:changed")) { 84 mManager->OnPreferenceChange(aData); 85 } 86 return NS_OK; 87 } 88 89 void RDDProcessManager::OnXPCOMShutdown() { 90 MOZ_ASSERT(NS_IsMainThread()); 91 nsContentUtils::UnregisterShutdownObserver(mObserver); 92 Preferences::RemoveObserver(mObserver, ""); 93 } 94 95 void RDDProcessManager::OnPreferenceChange(const char16_t* aData) { 96 MOZ_ASSERT(NS_IsMainThread()); 97 if (!mProcess) { 98 // Process hasn't been launched yet 99 return; 100 } 101 102 // We know prefs are ASCII here. 103 NS_LossyConvertUTF16toASCII strData(aData); 104 105 mozilla::dom::Pref pref(strData, /* isLocked */ false, 106 /* isSanitized */ false, Nothing(), Nothing()); 107 108 Preferences::GetPreference(&pref, GeckoProcessType_RDD, 109 /* remoteType */ ""_ns); 110 if (!!mRDDChild) { 111 MOZ_ASSERT(mQueuedPrefs.IsEmpty()); 112 mRDDChild->SendPreferenceUpdate(pref); 113 } else if (IsRDDProcessLaunching()) { 114 mQueuedPrefs.AppendElement(pref); 115 } 116 } 117 118 RefPtr<GenericNonExclusivePromise> RDDProcessManager::LaunchRDDProcess() { 119 MOZ_ASSERT(NS_IsMainThread()); 120 121 if (IsShutdown()) { 122 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, 123 __func__); 124 } 125 126 if (mNumProcessAttempts && !StaticPrefs::media_rdd_retryonfailure_enabled()) { 127 // We failed to start the RDD process earlier, abort now. 128 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, 129 __func__); 130 } 131 132 if (mProcess) { 133 MOZ_DIAGNOSTIC_ASSERT(mLaunchRDDPromise); 134 return mLaunchRDDPromise; 135 } 136 137 geckoargs::ChildProcessArgs extraArgs; 138 ipc::ProcessChild::AddPlatformBuildID(extraArgs); 139 140 // The subprocess is launched asynchronously, so we 141 // wait for the promise to be resolved to acquire the IPDL actor. 142 mProcess = new RDDProcessHost(this); 143 if (!mProcess->Launch(std::move(extraArgs))) { 144 mNumProcessAttempts++; 145 DestroyProcess(); 146 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, 147 __func__); 148 } 149 150 mLaunchRDDPromise = mProcess->LaunchPromise()->Then( 151 GetMainThreadSerialEventTarget(), __func__, 152 [this](bool) { 153 if (IsShutdown()) { 154 return GenericNonExclusivePromise::CreateAndReject( 155 NS_ERROR_NOT_AVAILABLE, __func__); 156 } 157 158 if (NS_WARN_IF(!IsRDDProcessLaunching())) { 159 return GenericNonExclusivePromise::CreateAndReject( 160 NS_ERROR_NOT_AVAILABLE, __func__); 161 } 162 163 mRDDChild = mProcess->GetActor(); 164 mProcessToken = mProcess->GetProcessToken(); 165 166 // Flush any pref updates that happened during 167 // launch and weren't included in the blobs set 168 // up in LaunchRDDProcess. 169 for (const mozilla::dom::Pref& pref : mQueuedPrefs) { 170 (void)NS_WARN_IF(!mRDDChild->SendPreferenceUpdate(pref)); 171 } 172 mQueuedPrefs.Clear(); 173 174 CrashReporter::RecordAnnotationCString( 175 CrashReporter::Annotation::RDDProcessStatus, "Running"); 176 177 auto* gpm = GPUProcessManager::Get(); 178 if (NS_WARN_IF(!mRDDChild->CanSend()) || NS_WARN_IF(!gpm) || 179 NS_WARN_IF(NS_FAILED(gpm->EnsureGPUReady())) || 180 NS_WARN_IF(NS_FAILED(gpm->CreateRddVideoBridge(this, mRDDChild)))) { 181 mNumProcessAttempts++; 182 DestroyProcess(); 183 return GenericNonExclusivePromise::CreateAndReject( 184 NS_ERROR_NOT_AVAILABLE, __func__); 185 } 186 187 gpm->AddListener(mRDDChild); 188 return GenericNonExclusivePromise::CreateAndResolve(true, __func__); 189 }, 190 [this](nsresult aError) { 191 if (Get()) { 192 mNumProcessAttempts++; 193 DestroyProcess(); 194 } 195 return GenericNonExclusivePromise::CreateAndReject(aError, __func__); 196 }); 197 return mLaunchRDDPromise; 198 } 199 200 auto RDDProcessManager::EnsureRDDProcessAndCreateBridge( 201 ipc::EndpointProcInfo aOtherProcess, dom::ContentParentId aParentId) 202 -> RefPtr<EnsureRDDPromise> { 203 return InvokeAsync( 204 GetMainThreadSerialEventTarget(), __func__, 205 [aOtherProcess, aParentId, this]() -> RefPtr<EnsureRDDPromise> { 206 return LaunchRDDProcess()->Then( 207 GetMainThreadSerialEventTarget(), __func__, 208 [aOtherProcess, aParentId, this]() { 209 if (IsShutdown()) { 210 return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, 211 __func__); 212 } 213 ipc::Endpoint<PRemoteMediaManagerChild> endpoint; 214 if (!CreateContentBridge(aOtherProcess, aParentId, &endpoint)) { 215 return EnsureRDDPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, 216 __func__); 217 } 218 mNumProcessAttempts = 0; 219 return EnsureRDDPromise::CreateAndResolve(std::move(endpoint), 220 __func__); 221 }, 222 [](nsresult aError) { 223 return EnsureRDDPromise::CreateAndReject(aError, __func__); 224 }); 225 }); 226 } 227 228 bool RDDProcessManager::IsRDDProcessLaunching() const { 229 MOZ_ASSERT(NS_IsMainThread()); 230 return !!mProcess && !mRDDChild; 231 } 232 233 bool RDDProcessManager::IsRDDProcessAlive() const { 234 MOZ_ASSERT(NS_IsMainThread()); 235 return mRDDChild && mRDDChild->CanSend() && mProcess; 236 } 237 238 void RDDProcessManager::OnProcessUnexpectedShutdown(RDDProcessHost* aHost) { 239 MOZ_ASSERT(NS_IsMainThread()); 240 MOZ_ASSERT(mProcess && mProcess == aHost); 241 242 mNumUnexpectedCrashes++; 243 244 DestroyProcess(); 245 } 246 247 void RDDProcessManager::NotifyRemoteActorDestroyed( 248 const uint64_t& aProcessToken) { 249 if (!NS_IsMainThread()) { 250 RefPtr<Runnable> task = mTaskFactory.NewRunnableMethod( 251 &RDDProcessManager::NotifyRemoteActorDestroyed, aProcessToken); 252 NS_DispatchToMainThread(task.forget()); 253 return; 254 } 255 256 if (mProcessToken != aProcessToken) { 257 // This token is for an older process; we can safely ignore it. 258 return; 259 } 260 261 // One of the bridged top-level actors for the RDD process has been 262 // prematurely terminated, and we're receiving a notification. This 263 // can happen if the ActorDestroy for a bridged protocol fires 264 // before the ActorDestroy for PRDDChild. 265 OnProcessUnexpectedShutdown(mProcess); 266 } 267 268 void RDDProcessManager::DestroyProcess() { 269 MOZ_ASSERT(NS_IsMainThread()); 270 271 if (!mProcess) { 272 return; 273 } 274 275 // Move onto the stack to ensure we don't re-enter from a chained promise 276 // rejection on the process shutdown. 277 RDDProcessHost* process = mProcess; 278 mProcess = nullptr; 279 280 process->Shutdown(); 281 mProcessToken = 0; 282 mRDDChild = nullptr; 283 mLaunchRDDPromise = nullptr; 284 mQueuedPrefs.Clear(); 285 286 CrashReporter::RecordAnnotationCString( 287 CrashReporter::Annotation::RDDProcessStatus, "Destroyed"); 288 } 289 290 bool RDDProcessManager::CreateContentBridge( 291 ipc::EndpointProcInfo aOtherProcess, dom::ContentParentId aParentId, 292 ipc::Endpoint<PRemoteMediaManagerChild>* aOutRemoteMediaManager) { 293 MOZ_ASSERT(NS_IsMainThread()); 294 295 if (NS_WARN_IF(!IsRDDProcessAlive())) { 296 MOZ_LOG(sPDMLog, LogLevel::Debug, 297 ("RDD shutdown before creating content bridge")); 298 return false; 299 } 300 301 ipc::Endpoint<PRemoteMediaManagerParent> parentPipe; 302 ipc::Endpoint<PRemoteMediaManagerChild> childPipe; 303 304 nsresult rv = PRemoteMediaManager::CreateEndpoints( 305 mRDDChild->OtherEndpointProcInfo(), aOtherProcess, &parentPipe, 306 &childPipe); 307 if (NS_FAILED(rv)) { 308 MOZ_LOG(sPDMLog, LogLevel::Debug, 309 ("Could not create content remote decoder: %d", int(rv))); 310 return false; 311 } 312 313 mRDDChild->SendNewContentRemoteMediaManager(std::move(parentPipe), aParentId); 314 315 *aOutRemoteMediaManager = std::move(childPipe); 316 return true; 317 } 318 319 base::ProcessId RDDProcessManager::RDDProcessPid() { 320 MOZ_ASSERT(NS_IsMainThread()); 321 base::ProcessId rddPid = 322 mRDDChild ? mRDDChild->OtherPid() : base::kInvalidProcessId; 323 return rddPid; 324 } 325 326 ipc::EndpointProcInfo RDDProcessManager::RDDEndpointProcInfo() { 327 MOZ_ASSERT(NS_IsMainThread()); 328 return mRDDChild ? mRDDChild->OtherEndpointProcInfo() 329 : ipc::EndpointProcInfo::Invalid(); 330 } 331 332 class RDDMemoryReporter : public MemoryReportingProcess { 333 public: 334 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RDDMemoryReporter, override) 335 336 bool IsAlive() const override { return !!GetChild(); } 337 338 bool SendRequestMemoryReport( 339 const uint32_t& aGeneration, const bool& aAnonymize, 340 const bool& aMinimizeMemoryUsage, 341 const Maybe<ipc::FileDescriptor>& aDMDFile) override { 342 RDDChild* child = GetChild(); 343 if (!child) { 344 return false; 345 } 346 347 return child->SendRequestMemoryReport(aGeneration, aAnonymize, 348 aMinimizeMemoryUsage, aDMDFile); 349 } 350 351 int32_t Pid() const override { 352 if (RDDChild* child = GetChild()) { 353 return (int32_t)child->OtherPid(); 354 } 355 return 0; 356 } 357 358 private: 359 RDDChild* GetChild() const { 360 if (RDDProcessManager* rddpm = RDDProcessManager::Get()) { 361 if (RDDChild* child = rddpm->GetRDDChild()) { 362 return child; 363 } 364 } 365 return nullptr; 366 } 367 368 protected: 369 ~RDDMemoryReporter() = default; 370 }; 371 372 RefPtr<MemoryReportingProcess> RDDProcessManager::GetProcessMemoryReporter() { 373 if (!mProcess || !mProcess->IsConnected()) { 374 return nullptr; 375 } 376 return new RDDMemoryReporter(); 377 } 378 379 RefPtr<PRDDChild::TestTriggerMetricsPromise> 380 RDDProcessManager::TestTriggerMetrics() { 381 if (!NS_WARN_IF(!mRDDChild)) { 382 return mRDDChild->SendTestTriggerMetrics(); 383 } 384 385 return PRDDChild::TestTriggerMetricsPromise::CreateAndReject( 386 ipc::ResponseRejectReason::SendError, __func__); 387 } 388 389 } // namespace mozilla