GPUProcessHost.cpp (10360B)
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 "GPUProcessHost.h" 8 #include "chrome/common/process_watcher.h" 9 #include "gfxPlatform.h" 10 #include "mozilla/dom/ContentParent.h" 11 #include "mozilla/gfx/GPUChild.h" 12 #include "mozilla/gfx/Logging.h" 13 #include "mozilla/layers/SynchronousTask.h" 14 #include "mozilla/Preferences.h" 15 #include "mozilla/StaticPrefs_layers.h" 16 #include "VRGPUChild.h" 17 #include "mozilla/ipc/ProcessUtils.h" 18 #ifdef MOZ_WIDGET_ANDROID 19 # include "mozilla/java/GeckoProcessManagerWrappers.h" 20 #endif 21 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 22 # include "mozilla/SandboxSettings.h" 23 #endif 24 25 namespace mozilla { 26 namespace gfx { 27 28 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 29 bool GPUProcessHost::sLaunchWithMacSandbox = false; 30 #endif 31 32 using namespace ipc; 33 34 GPUProcessHost::GPUProcessHost(Listener* aListener) 35 : GeckoChildProcessHost(GeckoProcessType_GPU), 36 mListener(aListener), 37 mLaunchPhase(LaunchPhase::Unlaunched), 38 mProcessToken(0), 39 mShutdownRequested(false), 40 mChannelClosed(false), 41 mLiveToken(new media::Refcountable<bool>(true)) { 42 MOZ_COUNT_CTOR(GPUProcessHost); 43 44 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 45 if (!sLaunchWithMacSandbox) { 46 sLaunchWithMacSandbox = IsGPUSandboxEnabled(); 47 } 48 mDisableOSActivityMode = sLaunchWithMacSandbox; 49 #endif 50 } 51 52 GPUProcessHost::~GPUProcessHost() { MOZ_COUNT_DTOR(GPUProcessHost); } 53 54 bool GPUProcessHost::Launch(geckoargs::ChildProcessArgs aExtraOpts) { 55 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched); 56 MOZ_ASSERT(!mGPUChild); 57 MOZ_ASSERT(!gfxPlatform::IsHeadless()); 58 59 mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(); 60 if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_GPU, 61 /* remoteType */ ""_ns)) { 62 return false; 63 } 64 mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts); 65 66 #if defined(XP_WIN) && defined(MOZ_SANDBOX) 67 mSandboxLevel = Preferences::GetInt("security.sandbox.gpu.level"); 68 #endif 69 70 mLaunchPhase = LaunchPhase::Waiting; 71 mLaunchTime = TimeStamp::Now(); 72 73 if (!GeckoChildProcessHost::AsyncLaunch(std::move(aExtraOpts))) { 74 mLaunchPhase = LaunchPhase::Complete; 75 mPrefSerializer = nullptr; 76 return false; 77 } 78 return true; 79 } 80 81 bool GPUProcessHost::WaitForLaunch() { 82 MOZ_ASSERT(mLaunchPhase != LaunchPhase::Unlaunched); 83 if (mLaunchPhase == LaunchPhase::Complete) { 84 return !!mGPUChild; 85 } 86 87 int32_t timeoutMs = 88 StaticPrefs::layers_gpu_process_startup_timeout_ms_AtStartup(); 89 90 // If one of the following environment variables are set we can effectively 91 // ignore the timeout - as we can guarantee the compositor process will be 92 // terminated 93 if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") || 94 PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) { 95 timeoutMs = 0; 96 } 97 98 if (mLaunchPhase == LaunchPhase::Waiting) { 99 // Our caller expects the connection to be finished by the time we return, 100 // so we immediately set up the IPDL actor and fire callbacks. The IO thread 101 // will still dispatch a notification to the main thread - we'll just ignore 102 // it. 103 bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs); 104 InitAfterConnect(result); 105 if (!result) { 106 return false; 107 } 108 } 109 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Connected); 110 // Our caller expects post-connection initialization tasks, such as ensuring 111 // the GPUChild is initialized, to be finished by the time we return, so 112 // finish these tasks synchronously now. 113 return CompleteInitSynchronously(); 114 } 115 116 void GPUProcessHost::OnChannelConnected(base::ProcessId peer_pid) { 117 MOZ_ASSERT(!NS_IsMainThread()); 118 119 GeckoChildProcessHost::OnChannelConnected(peer_pid); 120 121 NS_DispatchToMainThread(NS_NewRunnableFunction( 122 "GPUProcessHost::OnChannelConnected", 123 [self = this, liveToken = mLiveToken]() { 124 if (*liveToken && self->mLaunchPhase == LaunchPhase::Waiting) { 125 self->InitAfterConnect(true); 126 } 127 })); 128 } 129 130 static uint64_t sProcessTokenCounter = 0; 131 132 void GPUProcessHost::InitAfterConnect(bool aSucceeded) { 133 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting); 134 MOZ_ASSERT(!mGPUChild); 135 136 mPrefSerializer = nullptr; 137 138 if (aSucceeded) { 139 mLaunchPhase = LaunchPhase::Connected; 140 mProcessToken = ++sProcessTokenCounter; 141 mGPUChild = MakeRefPtr<GPUChild>(this); 142 DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mGPUChild.get()); 143 MOZ_ASSERT(rv); 144 145 nsTArray<RefPtr<GPUChild::InitPromiseType>> initPromises; 146 initPromises.AppendElement(mGPUChild->Init()); 147 148 #ifdef MOZ_WIDGET_ANDROID 149 nsCOMPtr<nsISerialEventTarget> launcherThread(GetIPCLauncher()); 150 MOZ_ASSERT(launcherThread); 151 RefPtr<GPUChild::InitPromiseType> csmPromise = 152 InvokeAsync( 153 launcherThread, __func__, 154 [] { 155 java::CompositorSurfaceManager::LocalRef csm = 156 java::GeckoProcessManager::GetCompositorSurfaceManager(); 157 return MozPromise<java::CompositorSurfaceManager::GlobalRef, Ok, 158 true>::CreateAndResolve(csm, __func__); 159 }) 160 ->Map(GetCurrentSerialEventTarget(), __func__, 161 [self = this, liveToken = mLiveToken]( 162 java::CompositorSurfaceManager::GlobalRef&& aCsm) { 163 if (*liveToken) { 164 self->mCompositorSurfaceManager = aCsm; 165 } 166 return Ok{}; 167 }); 168 initPromises.AppendElement(csmPromise); 169 #endif 170 171 GPUChild::InitPromiseType::All(GetCurrentSerialEventTarget(), initPromises) 172 ->Then(GetCurrentSerialEventTarget(), __func__, 173 [self = this, liveToken = mLiveToken]() { 174 if (*liveToken) { 175 self->OnAsyncInitComplete(); 176 } 177 }); 178 } else { 179 mLaunchPhase = LaunchPhase::Complete; 180 if (mListener) { 181 mListener->OnProcessLaunchComplete(this); 182 } 183 } 184 } 185 186 void GPUProcessHost::OnAsyncInitComplete() { 187 MOZ_ASSERT(NS_IsMainThread()); 188 if (mLaunchPhase == LaunchPhase::Connected) { 189 mLaunchPhase = LaunchPhase::Complete; 190 if (mListener) { 191 mListener->OnProcessLaunchComplete(this); 192 } 193 } 194 } 195 196 bool GPUProcessHost::CompleteInitSynchronously() { 197 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Connected); 198 199 const bool result = mGPUChild->EnsureGPUReady(); 200 201 #ifdef MOZ_WIDGET_ANDROID 202 if (!mCompositorSurfaceManager) { 203 layers::SynchronousTask task( 204 "GeckoProcessManager::GetCompositorSurfaceManager"); 205 206 nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher()); 207 MOZ_ASSERT(launcherThread); 208 launcherThread->Dispatch(NS_NewRunnableFunction( 209 "GeckoProcessManager::GetCompositorSurfaceManager", [&]() { 210 layers::AutoCompleteTask complete(&task); 211 mCompositorSurfaceManager = 212 java::GeckoProcessManager::GetCompositorSurfaceManager(); 213 })); 214 215 task.Wait(); 216 } 217 #endif 218 219 mLaunchPhase = LaunchPhase::Complete; 220 if (mListener) { 221 mListener->OnProcessLaunchComplete(this); 222 } 223 224 return result; 225 } 226 227 void GPUProcessHost::Shutdown(bool aUnexpectedShutdown) { 228 MOZ_ASSERT(!mShutdownRequested); 229 230 mListener = nullptr; 231 232 if (mGPUChild) { 233 // OnChannelClosed uses this to check if the shutdown was expected or 234 // unexpected. 235 mShutdownRequested = true; 236 237 if (aUnexpectedShutdown) { 238 mGPUChild->OnUnexpectedShutdown(); 239 } 240 241 // The channel might already be closed if we got here unexpectedly. 242 if (!mChannelClosed) { 243 if (VRGPUChild::IsCreated()) { 244 VRGPUChild::Get()->Close(); 245 } 246 mGPUChild->SendShutdownVR(); 247 mGPUChild->Close(); 248 } 249 250 #ifndef NS_FREE_PERMANENT_DATA 251 // No need to communicate shutdown, the GPU process doesn't need to 252 // communicate anything back. 253 KillHard(/* aGenerateMinidump */ false); 254 #endif 255 256 // If we're shutting down unexpectedly, we're in the middle of handling an 257 // ActorDestroy for PGPUChild, which is still on the stack. We'll return 258 // back to OnChannelClosed. 259 // 260 // Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild 261 // acknowledges shutdown. 262 return; 263 } 264 265 DestroyProcess(); 266 } 267 268 void GPUProcessHost::OnChannelClosed() { 269 mChannelClosed = true; 270 271 if (!mShutdownRequested && mListener) { 272 // This is an unclean shutdown. Notify our listener that we're going away. 273 mListener->OnProcessUnexpectedShutdown(this); 274 } else { 275 DestroyProcess(); 276 } 277 278 // Release the actor. 279 GPUChild::Destroy(std::move(mGPUChild)); 280 MOZ_ASSERT(!mGPUChild); 281 } 282 283 void GPUProcessHost::KillHard(bool aGenerateMinidump) { 284 MOZ_ASSERT(NS_IsMainThread()); 285 286 if (mGPUChild && aGenerateMinidump) { 287 mGPUChild->GeneratePairedMinidump(); 288 } 289 290 const ProcessHandle handle = GetChildProcessHandle(); 291 if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER)) { 292 if (mGPUChild) { 293 mGPUChild->DeletePairedMinidump(); 294 } 295 NS_WARNING("failed to kill subprocess!"); 296 } 297 298 SetAlreadyDead(); 299 } 300 301 uint64_t GPUProcessHost::GetProcessToken() const { return mProcessToken; } 302 303 void GPUProcessHost::KillProcess(bool aGenerateMinidump) { 304 KillHard(aGenerateMinidump); 305 } 306 307 void GPUProcessHost::CrashProcess() { mGPUChild->SendCrashProcess(); } 308 309 void GPUProcessHost::DestroyProcess() { 310 MOZ_ASSERT(NS_IsMainThread()); 311 312 // Any pending tasks will be cancelled from now on. 313 *mLiveToken = false; 314 315 NS_DispatchToMainThread( 316 NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); })); 317 } 318 319 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 320 bool GPUProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) { 321 GeckoChildProcessHost::FillMacSandboxInfo(aInfo); 322 if (!aInfo.shouldLog && PR_GetEnv("MOZ_SANDBOX_GPU_LOGGING")) { 323 aInfo.shouldLog = true; 324 } 325 aInfo.type = MacSandboxType::MacSandboxType_GPU; 326 return true; 327 } 328 #endif 329 330 #ifdef MOZ_WIDGET_ANDROID 331 java::CompositorSurfaceManager::Param 332 GPUProcessHost::GetCompositorSurfaceManager() { 333 return mCompositorSurfaceManager; 334 } 335 #endif 336 337 } // namespace gfx 338 } // namespace mozilla