VRProcessParent.cpp (7000B)
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 "VRProcessParent.h" 8 #include "VRGPUChild.h" 9 #include "VRProcessManager.h" 10 #include "mozilla/dom/ContentParent.h" 11 #include "mozilla/dom/MemoryReportRequest.h" 12 #include "mozilla/gfx/GPUProcessManager.h" 13 #include "mozilla/gfx/GPUChild.h" 14 #include "mozilla/ipc/Endpoint.h" 15 #include "mozilla/ipc/ProcessChild.h" 16 #include "mozilla/ipc/ProcessUtils.h" 17 #include "mozilla/ipc/ProtocolTypes.h" 18 #include "mozilla/ipc/ProtocolUtils.h" // for IToplevelProtocol 19 #include "mozilla/Preferences.h" 20 #include "mozilla/StaticPrefs_dom.h" 21 #include "mozilla/TimeStamp.h" // for TimeStamp 22 #include "VRChild.h" 23 #include "VRThread.h" 24 25 #include "nsAppRunner.h" // for IToplevelProtocol 26 27 using std::string; 28 using std::vector; 29 30 using namespace mozilla::ipc; 31 32 namespace mozilla { 33 namespace gfx { 34 35 VRProcessParent::VRProcessParent(Listener* aListener) 36 : GeckoChildProcessHost(GeckoProcessType_VR), 37 mTaskFactory(this), 38 mListener(aListener), 39 mLaunchPhase(LaunchPhase::Unlaunched), 40 mChannelClosed(false), 41 mShutdownRequested(false) {} 42 43 VRProcessParent::~VRProcessParent() = default; 44 45 bool VRProcessParent::Launch() { 46 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched); 47 MOZ_ASSERT(!mVRChild); 48 mLaunchThread = NS_GetCurrentThread(); 49 50 mLaunchPhase = LaunchPhase::Waiting; 51 52 geckoargs::ChildProcessArgs extraArgs; 53 ProcessChild::AddPlatformBuildID(extraArgs); 54 55 mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(); 56 if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_VR, 57 /* remoteType */ ""_ns)) { 58 return false; 59 } 60 mPrefSerializer->AddSharedPrefCmdLineArgs(*this, extraArgs); 61 62 if (!GeckoChildProcessHost::AsyncLaunch(std::move(extraArgs))) { 63 mLaunchPhase = LaunchPhase::Complete; 64 mPrefSerializer = nullptr; 65 return false; 66 } 67 return true; 68 } 69 70 bool VRProcessParent::WaitForLaunch() { 71 if (mLaunchPhase == LaunchPhase::Complete) { 72 return !!mVRChild; 73 } 74 75 int32_t timeoutMs = 76 StaticPrefs::dom_vr_process_startup_timeout_ms_AtStartup(); 77 78 // If one of the following environment variables are set we can effectively 79 // ignore the timeout - as we can guarantee the compositor process will be 80 // terminated 81 if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") || 82 PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) { 83 timeoutMs = 0; 84 } 85 86 // Our caller expects the connection to be finished after we return, so we 87 // immediately set up the IPDL actor and fire callbacks. The IO thread will 88 // still dispatch a notification to the main thread - we'll just ignore it. 89 bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs); 90 result &= InitAfterConnect(result); 91 return result; 92 } 93 94 void VRProcessParent::Shutdown() { 95 MOZ_ASSERT(!mShutdownRequested); 96 mListener = nullptr; 97 98 if (mVRChild) { 99 // The channel might already be closed if we got here unexpectedly. 100 if (!mChannelClosed) { 101 mVRChild->Close(); 102 } 103 // OnChannelClosed uses this to check if the shutdown was expected or 104 // unexpected. 105 mShutdownRequested = true; 106 107 #ifndef NS_FREE_PERMANENT_DATA 108 // No need to communicate shutdown, the VR process doesn't need to 109 // communicate anything back. 110 KillHard("NormalShutdown"); 111 #endif 112 113 // If we're shutting down unexpectedly, we're in the middle of handling an 114 // ActorDestroy for PVRChild, which is still on the stack. We'll return 115 // back to OnChannelClosed. 116 // 117 // Otherwise, we'll wait for OnChannelClose to be called whenever PVRChild 118 // acknowledges shutdown. 119 return; 120 } 121 122 DestroyProcess(); 123 } 124 125 void VRProcessParent::DestroyProcess() { 126 if (mLaunchThread) { 127 // Cancel all tasks. We don't want anything triggering after our caller 128 // expects this to go away. 129 { 130 MonitorAutoLock lock(mMonitor); 131 mTaskFactory.RevokeAll(); 132 } 133 134 mLaunchThread->Dispatch(NS_NewRunnableFunction("DestroyProcessRunnable", 135 [this] { Destroy(); })); 136 } 137 } 138 139 bool VRProcessParent::InitAfterConnect(bool aSucceeded) { 140 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting); 141 MOZ_ASSERT(!mVRChild); 142 143 mLaunchPhase = LaunchPhase::Complete; 144 mPrefSerializer = nullptr; 145 146 if (aSucceeded) { 147 GPUChild* gpuChild = GPUProcessManager::Get()->GetGPUChild(); 148 if (!gpuChild) { 149 NS_WARNING( 150 "GPU process haven't connected with the parent process yet" 151 "when creating VR process."); 152 return false; 153 } 154 155 if (!StaticPrefs::dom_vr_enabled() && 156 !StaticPrefs::dom_vr_webxr_enabled()) { 157 NS_WARNING("VR is not enabled when trying to create a VRChild"); 158 return false; 159 } 160 161 mVRChild = MakeRefPtr<VRChild>(this); 162 163 DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mVRChild.get()); 164 MOZ_ASSERT(rv); 165 166 mVRChild->Init(); 167 168 if (mListener) { 169 mListener->OnProcessLaunchComplete(this); 170 } 171 172 // Make vr-gpu process connection 173 Endpoint<PVRGPUChild> vrGPUBridge; 174 VRProcessManager* vpm = VRProcessManager::Get(); 175 DebugOnly<bool> opened = 176 vpm->CreateGPUBridges(gpuChild->OtherEndpointProcInfo(), &vrGPUBridge); 177 MOZ_ASSERT(opened); 178 179 (void)gpuChild->SendInitVR(std::move(vrGPUBridge)); 180 } 181 182 return true; 183 } 184 185 void VRProcessParent::KillHard(const char* aReason) { 186 ProcessHandle handle = GetChildProcessHandle(); 187 if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER)) { 188 NS_WARNING("failed to kill subprocess!"); 189 } 190 191 SetAlreadyDead(); 192 } 193 194 void VRProcessParent::OnChannelConnected(base::ProcessId peer_pid) { 195 MOZ_ASSERT(!NS_IsMainThread()); 196 197 GeckoChildProcessHost::OnChannelConnected(peer_pid); 198 199 // Post a task to the main thread. Take the lock because mTaskFactory is not 200 // thread-safe. 201 RefPtr<Runnable> runnable; 202 { 203 MonitorAutoLock lock(mMonitor); 204 runnable = mTaskFactory.NewRunnableMethod( 205 &VRProcessParent::OnChannelConnectedTask); 206 } 207 NS_DispatchToMainThread(runnable); 208 } 209 210 void VRProcessParent::OnChannelConnectedTask() { 211 if (mLaunchPhase == LaunchPhase::Waiting) { 212 InitAfterConnect(true); 213 } 214 } 215 216 void VRProcessParent::OnChannelErrorTask() { 217 if (mLaunchPhase == LaunchPhase::Waiting) { 218 InitAfterConnect(false); 219 } 220 } 221 222 void VRProcessParent::OnChannelClosed() { 223 mChannelClosed = true; 224 if (!mShutdownRequested && mListener) { 225 // This is an unclean shutdown. Notify we're going away. 226 mListener->OnProcessUnexpectedShutdown(this); 227 } else { 228 DestroyProcess(); 229 } 230 231 // Release the actor. 232 VRChild::Destroy(std::move(mVRChild)); 233 MOZ_ASSERT(!mVRChild); 234 } 235 236 bool VRProcessParent::IsConnected() const { return !!mVRChild; } 237 238 } // namespace gfx 239 } // namespace mozilla