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