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