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