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
22 namespace mozilla {
23 namespace gfx {
24
25 using namespace ipc;
26
GPUProcessHost(Listener * aListener)27 GPUProcessHost::GPUProcessHost(Listener* aListener)
28 : GeckoChildProcessHost(GeckoProcessType_GPU),
29 mListener(aListener),
30 mTaskFactory(this),
31 mLaunchPhase(LaunchPhase::Unlaunched),
32 mProcessToken(0),
33 mShutdownRequested(false),
34 mChannelClosed(false) {
35 MOZ_COUNT_CTOR(GPUProcessHost);
36 }
37
~GPUProcessHost()38 GPUProcessHost::~GPUProcessHost() { MOZ_COUNT_DTOR(GPUProcessHost); }
39
Launch(StringVector aExtraOpts)40 bool GPUProcessHost::Launch(StringVector aExtraOpts) {
41 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
42 MOZ_ASSERT(!mGPUChild);
43 MOZ_ASSERT(!gfxPlatform::IsHeadless());
44
45 mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(
46 dom::ContentParent::ShouldSyncPreference);
47 if (!mPrefSerializer->SerializeToSharedMemory()) {
48 return false;
49 }
50 mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts);
51
52 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
53 mSandboxLevel = Preferences::GetInt("security.sandbox.gpu.level");
54 #endif
55
56 mLaunchPhase = LaunchPhase::Waiting;
57 mLaunchTime = TimeStamp::Now();
58
59 if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts)) {
60 mLaunchPhase = LaunchPhase::Complete;
61 mPrefSerializer = nullptr;
62 return false;
63 }
64 return true;
65 }
66
WaitForLaunch()67 bool GPUProcessHost::WaitForLaunch() {
68 if (mLaunchPhase == LaunchPhase::Complete) {
69 return !!mGPUChild;
70 }
71
72 int32_t timeoutMs =
73 StaticPrefs::layers_gpu_process_startup_timeout_ms_AtStartup();
74
75 // If one of the following environment variables are set we can effectively
76 // ignore the timeout - as we can guarantee the compositor process will be
77 // terminated
78 if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") ||
79 PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
80 timeoutMs = 0;
81 }
82
83 // Our caller expects the connection to be finished after we return, so we
84 // immediately set up the IPDL actor and fire callbacks. The IO thread will
85 // still dispatch a notification to the main thread - we'll just ignore it.
86 bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs);
87 InitAfterConnect(result);
88 return result;
89 }
90
OnChannelConnected(int32_t peer_pid)91 void GPUProcessHost::OnChannelConnected(int32_t peer_pid) {
92 MOZ_ASSERT(!NS_IsMainThread());
93
94 GeckoChildProcessHost::OnChannelConnected(peer_pid);
95
96 // Post a task to the main thread. Take the lock because mTaskFactory is not
97 // thread-safe.
98 RefPtr<Runnable> runnable;
99 {
100 MonitorAutoLock lock(mMonitor);
101 runnable =
102 mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelConnectedTask);
103 }
104 NS_DispatchToMainThread(runnable);
105 }
106
OnChannelError()107 void GPUProcessHost::OnChannelError() {
108 MOZ_ASSERT(!NS_IsMainThread());
109
110 GeckoChildProcessHost::OnChannelError();
111
112 // Post a task to the main thread. Take the lock because mTaskFactory is not
113 // thread-safe.
114 RefPtr<Runnable> runnable;
115 {
116 MonitorAutoLock lock(mMonitor);
117 runnable =
118 mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelErrorTask);
119 }
120 NS_DispatchToMainThread(runnable);
121 }
122
OnChannelConnectedTask()123 void GPUProcessHost::OnChannelConnectedTask() {
124 if (mLaunchPhase == LaunchPhase::Waiting) {
125 InitAfterConnect(true);
126 }
127 }
128
OnChannelErrorTask()129 void GPUProcessHost::OnChannelErrorTask() {
130 if (mLaunchPhase == LaunchPhase::Waiting) {
131 InitAfterConnect(false);
132 }
133 }
134
135 static uint64_t sProcessTokenCounter = 0;
136
InitAfterConnect(bool aSucceeded)137 void GPUProcessHost::InitAfterConnect(bool aSucceeded) {
138 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
139 MOZ_ASSERT(!mGPUChild);
140
141 mLaunchPhase = LaunchPhase::Complete;
142 mPrefSerializer = nullptr;
143
144 if (aSucceeded) {
145 mProcessToken = ++sProcessTokenCounter;
146 mGPUChild = MakeUnique<GPUChild>(this);
147 DebugOnly<bool> rv = mGPUChild->Open(
148 TakeInitialPort(), base::GetProcId(GetChildProcessHandle()));
149 MOZ_ASSERT(rv);
150
151 mGPUChild->Init();
152
153 #ifdef MOZ_WIDGET_ANDROID
154 nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
155 MOZ_ASSERT(launcherThread);
156 layers::SynchronousTask task(
157 "GeckoProcessManager::GetCompositorSurfaceManager");
158
159 launcherThread->Dispatch(NS_NewRunnableFunction(
160 "GeckoProcessManager::GetCompositorSurfaceManager", [&]() {
161 layers::AutoCompleteTask complete(&task);
162 mCompositorSurfaceManager =
163 java::GeckoProcessManager::GetCompositorSurfaceManager();
164 }));
165
166 task.Wait();
167 #endif
168 }
169
170 if (mListener) {
171 mListener->OnProcessLaunchComplete(this);
172 }
173 }
174
Shutdown(bool aUnexpectedShutdown)175 void GPUProcessHost::Shutdown(bool aUnexpectedShutdown) {
176 MOZ_ASSERT(!mShutdownRequested);
177
178 mListener = nullptr;
179
180 if (mGPUChild) {
181 // OnChannelClosed uses this to check if the shutdown was expected or
182 // unexpected.
183 mShutdownRequested = true;
184
185 if (aUnexpectedShutdown) {
186 mGPUChild->OnUnexpectedShutdown();
187 }
188
189 // The channel might already be closed if we got here unexpectedly.
190 if (!mChannelClosed) {
191 if (VRGPUChild::IsCreated()) {
192 VRGPUChild::Get()->Close();
193 }
194 mGPUChild->SendShutdownVR();
195 mGPUChild->Close();
196 }
197
198 #ifndef NS_FREE_PERMANENT_DATA
199 // No need to communicate shutdown, the GPU process doesn't need to
200 // communicate anything back.
201 KillHard("NormalShutdown");
202 #endif
203
204 // If we're shutting down unexpectedly, we're in the middle of handling an
205 // ActorDestroy for PGPUChild, which is still on the stack. We'll return
206 // back to OnChannelClosed.
207 //
208 // Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild
209 // acknowledges shutdown.
210 return;
211 }
212
213 DestroyProcess();
214 }
215
OnChannelClosed()216 void GPUProcessHost::OnChannelClosed() {
217 mChannelClosed = true;
218
219 if (!mShutdownRequested && mListener) {
220 // This is an unclean shutdown. Notify our listener that we're going away.
221 mListener->OnProcessUnexpectedShutdown(this);
222 } else {
223 DestroyProcess();
224 }
225
226 // Release the actor.
227 GPUChild::Destroy(std::move(mGPUChild));
228 MOZ_ASSERT(!mGPUChild);
229 }
230
KillHard(const char * aReason)231 void GPUProcessHost::KillHard(const char* aReason) {
232 ProcessHandle handle = GetChildProcessHandle();
233 if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER)) {
234 NS_WARNING("failed to kill subprocess!");
235 }
236
237 SetAlreadyDead();
238 }
239
GetProcessToken() const240 uint64_t GPUProcessHost::GetProcessToken() const { return mProcessToken; }
241
KillProcess()242 void GPUProcessHost::KillProcess() { KillHard("DiagnosticKill"); }
243
CrashProcess()244 void GPUProcessHost::CrashProcess() { mGPUChild->SendCrashProcess(); }
245
DestroyProcess()246 void GPUProcessHost::DestroyProcess() {
247 // Cancel all tasks. We don't want anything triggering after our caller
248 // expects this to go away.
249 {
250 MonitorAutoLock lock(mMonitor);
251 mTaskFactory.RevokeAll();
252 }
253
254 GetCurrentSerialEventTarget()->Dispatch(
255 NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); }));
256 }
257
258 #ifdef MOZ_WIDGET_ANDROID
259 java::CompositorSurfaceManager::Param
GetCompositorSurfaceManager()260 GPUProcessHost::GetCompositorSurfaceManager() {
261 return mCompositorSurfaceManager;
262 }
263 #endif
264
265 } // namespace gfx
266 } // namespace mozilla
267