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