VRProcessManager.cpp (8058B)
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 7 #include "VRProcessManager.h" 8 9 #include "VRProcessParent.h" 10 #include "VRChild.h" 11 #include "VRGPUChild.h" 12 #include "VRGPUParent.h" 13 #include "mozilla/dom/ContentParent.h" 14 #include "mozilla/gfx/Logging.h" 15 #include "mozilla/ipc/Endpoint.h" 16 #include "mozilla/MemoryReportingProcess.h" 17 #include "mozilla/Preferences.h" 18 19 namespace mozilla { 20 namespace gfx { 21 22 static StaticAutoPtr<VRProcessManager> sSingleton; 23 24 /* static */ 25 VRProcessManager* VRProcessManager::Get() { return sSingleton; } 26 27 /* static */ 28 void VRProcessManager::Initialize() { 29 MOZ_ASSERT(XRE_IsParentProcess()); 30 if (sSingleton == nullptr) { 31 sSingleton = new VRProcessManager(); 32 } 33 } 34 35 /* static */ 36 void VRProcessManager::Shutdown() { sSingleton = nullptr; } 37 38 VRProcessManager::VRProcessManager() : mProcess(nullptr), mVRChild(nullptr) { 39 MOZ_COUNT_CTOR(VRProcessManager); 40 41 mObserver = new Observer(this); 42 nsContentUtils::RegisterShutdownObserver(mObserver); 43 Preferences::AddStrongObserver(mObserver, ""); 44 } 45 46 VRProcessManager::~VRProcessManager() { 47 MOZ_COUNT_DTOR(VRProcessManager); 48 49 if (mObserver) { 50 nsContentUtils::UnregisterShutdownObserver(mObserver); 51 Preferences::RemoveObserver(mObserver, ""); 52 mObserver = nullptr; 53 } 54 55 DestroyProcess(); 56 // The VR process should have already been shut down. 57 MOZ_ASSERT(!mProcess); 58 } 59 60 void VRProcessManager::LaunchVRProcess() { 61 if (mProcess) { 62 return; 63 } 64 65 // The subprocess is launched asynchronously, so we wait for a callback to 66 // acquire the IPDL actor. 67 mProcess = new VRProcessParent(this); 68 if (!mProcess->Launch()) { 69 DisableVRProcess("Failed to launch VR process"); 70 } 71 } 72 73 void VRProcessManager::DisableVRProcess(const char* aMessage) { 74 if (!StaticPrefs::dom_vr_process_enabled_AtStartup()) { 75 return; 76 } 77 78 DestroyProcess(); 79 } 80 81 void VRProcessManager::DestroyProcess() { 82 if (!mProcess) { 83 return; 84 } 85 86 mProcess->Shutdown(); 87 mProcess = nullptr; 88 mVRChild = nullptr; 89 90 CrashReporter::RecordAnnotationCString( 91 CrashReporter::Annotation::VRProcessStatus, "Destroyed"); 92 } 93 94 bool VRProcessManager::EnsureVRReady() { 95 if (mProcess && !mProcess->IsConnected()) { 96 if (!mProcess->WaitForLaunch()) { 97 // If this fails, we should have fired OnProcessLaunchComplete and 98 // removed the process. 99 MOZ_ASSERT(!mProcess && !mVRChild); 100 return false; 101 } 102 } 103 104 if (mVRChild) { 105 if (mVRChild->EnsureVRReady()) { 106 return true; 107 } 108 109 // If the initialization above fails, we likely have a GPU process teardown 110 // waiting in our message queue (or will soon). We need to ensure we don't 111 // restart it later because if we fail here, our callers assume they should 112 // fall back to a combined UI/GPU process. This also ensures our internal 113 // state is consistent (e.g. process token is reset). 114 DisableVRProcess("Failed to initialize VR process"); 115 } 116 117 return false; 118 } 119 120 void VRProcessManager::OnProcessLaunchComplete(VRProcessParent* aParent) { 121 MOZ_ASSERT(mProcess && mProcess == aParent); 122 123 mVRChild = mProcess->GetActor(); 124 125 if (!mProcess->IsConnected()) { 126 DestroyProcess(); 127 return; 128 } 129 130 // Flush any pref updates that happened during launch and weren't 131 // included in the blobs set up in LaunchGPUProcess. 132 for (const mozilla::dom::Pref& pref : mQueuedPrefs) { 133 (void)NS_WARN_IF(!mVRChild->SendPreferenceUpdate(pref)); 134 } 135 mQueuedPrefs.Clear(); 136 137 CrashReporter::RecordAnnotationCString( 138 CrashReporter::Annotation::VRProcessStatus, "Running"); 139 } 140 141 void VRProcessManager::OnProcessUnexpectedShutdown(VRProcessParent* aParent) { 142 MOZ_ASSERT(mProcess && mProcess == aParent); 143 144 DestroyProcess(); 145 } 146 147 bool VRProcessManager::CreateGPUBridges( 148 mozilla::ipc::EndpointProcInfo aOtherProcess, 149 mozilla::ipc::Endpoint<PVRGPUChild>* aOutVRBridge) { 150 if (!CreateGPUVRManager(aOtherProcess, aOutVRBridge)) { 151 return false; 152 } 153 return true; 154 } 155 156 bool VRProcessManager::CreateGPUVRManager( 157 mozilla::ipc::EndpointProcInfo aOtherProcess, 158 mozilla::ipc::Endpoint<PVRGPUChild>* aOutEndpoint) { 159 if (mProcess && !mProcess->IsConnected()) { 160 NS_WARNING("VR process haven't connected with the parent process yet"); 161 return false; 162 } 163 164 ipc::EndpointProcInfo vrparentInfo = 165 mProcess ? ipc::EndpointProcInfo{.mPid = mProcess->GetChildProcessId(), 166 .mChildID = mProcess->GetChildID()} 167 : ipc::EndpointProcInfo::Current(); 168 169 ipc::Endpoint<PVRGPUParent> vrparentPipe; 170 ipc::Endpoint<PVRGPUChild> vrchildPipe; 171 nsresult rv = PVRGPU::CreateEndpoints(vrparentInfo, aOtherProcess, 172 &vrparentPipe, &vrchildPipe); 173 174 if (NS_FAILED(rv)) { 175 gfxCriticalNote << "Could not create gpu-vr bridge: " << hexa(int(rv)); 176 return false; 177 } 178 179 // Bind vr-gpu pipe to VRParent and make a PVRGPU connection. 180 VRChild* vrChild = mProcess->GetActor(); 181 vrChild->SendNewGPUVRManager(std::move(vrparentPipe)); 182 183 *aOutEndpoint = std::move(vrchildPipe); 184 return true; 185 } 186 187 NS_IMPL_ISUPPORTS(VRProcessManager::Observer, nsIObserver); 188 189 VRProcessManager::Observer::Observer(VRProcessManager* aManager) 190 : mManager(aManager) {} 191 192 NS_IMETHODIMP 193 VRProcessManager::Observer::Observe(nsISupports* aSubject, const char* aTopic, 194 const char16_t* aData) { 195 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 196 mManager->OnXPCOMShutdown(); 197 } else if (!strcmp(aTopic, "nsPref:changed")) { 198 mManager->OnPreferenceChange(aData); 199 } 200 return NS_OK; 201 } 202 203 void VRProcessManager::CleanShutdown() { DestroyProcess(); } 204 205 void VRProcessManager::OnXPCOMShutdown() { 206 if (mObserver) { 207 nsContentUtils::UnregisterShutdownObserver(mObserver); 208 Preferences::RemoveObserver(mObserver, ""); 209 mObserver = nullptr; 210 } 211 212 CleanShutdown(); 213 } 214 215 void VRProcessManager::OnPreferenceChange(const char16_t* aData) { 216 // We know prefs are ASCII here. 217 NS_LossyConvertUTF16toASCII strData(aData); 218 219 mozilla::dom::Pref pref(strData, /* isLocked */ false, 220 /* isSanitized */ false, Nothing(), Nothing()); 221 222 Preferences::GetPreference(&pref, GeckoProcessType_VR, 223 /* remoteType */ ""_ns); 224 if (!!mVRChild) { 225 MOZ_ASSERT(mQueuedPrefs.IsEmpty()); 226 mVRChild->SendPreferenceUpdate(pref); 227 } else { 228 mQueuedPrefs.AppendElement(pref); 229 } 230 } 231 232 VRChild* VRProcessManager::GetVRChild() { return mProcess->GetActor(); } 233 234 class VRMemoryReporter : public MemoryReportingProcess { 235 public: 236 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRMemoryReporter, override) 237 238 bool IsAlive() const override { 239 if (VRProcessManager* vpm = VRProcessManager::Get()) { 240 return !!vpm->GetVRChild(); 241 } 242 return false; 243 } 244 245 bool SendRequestMemoryReport( 246 const uint32_t& aGeneration, const bool& aAnonymize, 247 const bool& aMinimizeMemoryUsage, 248 const Maybe<ipc::FileDescriptor>& aDMDFile) override { 249 VRChild* child = GetChild(); 250 if (!child) { 251 return false; 252 } 253 254 return child->SendRequestMemoryReport(aGeneration, aAnonymize, 255 aMinimizeMemoryUsage, aDMDFile); 256 } 257 258 int32_t Pid() const override { 259 if (VRChild* child = GetChild()) { 260 return (int32_t)child->OtherPid(); 261 } 262 return 0; 263 } 264 265 private: 266 VRChild* GetChild() const { 267 if (VRProcessManager* vpm = VRProcessManager::Get()) { 268 if (VRChild* child = vpm->GetVRChild()) { 269 return child; 270 } 271 } 272 return nullptr; 273 } 274 275 protected: 276 ~VRMemoryReporter() = default; 277 }; 278 279 RefPtr<MemoryReportingProcess> VRProcessManager::GetProcessMemoryReporter() { 280 if (!EnsureVRReady()) { 281 return nullptr; 282 } 283 return new VRMemoryReporter(); 284 } 285 286 } // namespace gfx 287 } // namespace mozilla