Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: sts=8 sw=2 ts=2 tw=99 et :
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 "gfxPrefs.h"
10 : #include "mozilla/gfx/Logging.h"
11 : #include "nsITimer.h"
12 : #include "mozilla/Preferences.h"
13 :
14 : namespace mozilla {
15 : namespace gfx {
16 :
17 : using namespace ipc;
18 :
19 0 : GPUProcessHost::GPUProcessHost(Listener* aListener)
20 : : GeckoChildProcessHost(GeckoProcessType_GPU),
21 : mListener(aListener),
22 : mTaskFactory(this),
23 : mLaunchPhase(LaunchPhase::Unlaunched),
24 : mProcessToken(0),
25 : mShutdownRequested(false),
26 0 : mChannelClosed(false)
27 : {
28 0 : MOZ_COUNT_CTOR(GPUProcessHost);
29 0 : }
30 :
31 0 : GPUProcessHost::~GPUProcessHost()
32 : {
33 0 : MOZ_COUNT_DTOR(GPUProcessHost);
34 0 : }
35 :
36 : bool
37 0 : GPUProcessHost::Launch()
38 : {
39 0 : MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched);
40 0 : MOZ_ASSERT(!mGPUChild);
41 :
42 : #if defined(XP_WIN) && defined(MOZ_SANDBOX)
43 : mSandboxLevel = Preferences::GetInt("security.sandbox.gpu.level");
44 : #endif
45 :
46 0 : mLaunchPhase = LaunchPhase::Waiting;
47 0 : mLaunchTime = TimeStamp::Now();
48 :
49 0 : if (!GeckoChildProcessHost::AsyncLaunch()) {
50 0 : mLaunchPhase = LaunchPhase::Complete;
51 0 : return false;
52 : }
53 0 : return true;
54 : }
55 :
56 : bool
57 0 : GPUProcessHost::WaitForLaunch()
58 : {
59 0 : if (mLaunchPhase == LaunchPhase::Complete) {
60 0 : return !!mGPUChild;
61 : }
62 :
63 0 : int32_t timeoutMs = gfxPrefs::GPUProcessTimeoutMs();
64 :
65 : // If one of the following environment variables are set we can effectively
66 : // ignore the timeout - as we can guarantee the compositor process will be terminated
67 0 : if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") || PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
68 0 : timeoutMs = 0;
69 : }
70 :
71 : // Our caller expects the connection to be finished after we return, so we
72 : // immediately set up the IPDL actor and fire callbacks. The IO thread will
73 : // still dispatch a notification to the main thread - we'll just ignore it.
74 0 : bool result = GeckoChildProcessHost::WaitUntilConnected(timeoutMs);
75 0 : InitAfterConnect(result);
76 0 : return result;
77 : }
78 :
79 : void
80 0 : GPUProcessHost::OnChannelConnected(int32_t peer_pid)
81 : {
82 0 : MOZ_ASSERT(!NS_IsMainThread());
83 :
84 0 : GeckoChildProcessHost::OnChannelConnected(peer_pid);
85 :
86 : // Post a task to the main thread. Take the lock because mTaskFactory is not
87 : // thread-safe.
88 0 : RefPtr<Runnable> runnable;
89 : {
90 0 : MonitorAutoLock lock(mMonitor);
91 0 : runnable = mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelConnectedTask);
92 : }
93 0 : NS_DispatchToMainThread(runnable);
94 0 : }
95 :
96 : void
97 0 : GPUProcessHost::OnChannelError()
98 : {
99 0 : MOZ_ASSERT(!NS_IsMainThread());
100 :
101 0 : GeckoChildProcessHost::OnChannelError();
102 :
103 : // Post a task to the main thread. Take the lock because mTaskFactory is not
104 : // thread-safe.
105 0 : RefPtr<Runnable> runnable;
106 : {
107 0 : MonitorAutoLock lock(mMonitor);
108 0 : runnable = mTaskFactory.NewRunnableMethod(&GPUProcessHost::OnChannelErrorTask);
109 : }
110 0 : NS_DispatchToMainThread(runnable);
111 0 : }
112 :
113 : void
114 0 : GPUProcessHost::OnChannelConnectedTask()
115 : {
116 0 : if (mLaunchPhase == LaunchPhase::Waiting) {
117 0 : InitAfterConnect(true);
118 : }
119 0 : }
120 :
121 : void
122 0 : GPUProcessHost::OnChannelErrorTask()
123 : {
124 0 : if (mLaunchPhase == LaunchPhase::Waiting) {
125 0 : InitAfterConnect(false);
126 : }
127 0 : }
128 :
129 : static uint64_t sProcessTokenCounter = 0;
130 :
131 : void
132 0 : GPUProcessHost::InitAfterConnect(bool aSucceeded)
133 : {
134 0 : MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting);
135 0 : MOZ_ASSERT(!mGPUChild);
136 :
137 0 : mLaunchPhase = LaunchPhase::Complete;
138 :
139 0 : if (aSucceeded) {
140 0 : mProcessToken = ++sProcessTokenCounter;
141 0 : mGPUChild = MakeUnique<GPUChild>(this);
142 : DebugOnly<bool> rv =
143 0 : mGPUChild->Open(GetChannel(), base::GetProcId(GetChildProcessHandle()));
144 0 : MOZ_ASSERT(rv);
145 :
146 0 : mGPUChild->Init();
147 : }
148 :
149 0 : if (mListener) {
150 0 : mListener->OnProcessLaunchComplete(this);
151 : }
152 0 : }
153 :
154 : void
155 0 : GPUProcessHost::Shutdown()
156 : {
157 0 : MOZ_ASSERT(!mShutdownRequested);
158 :
159 0 : mListener = nullptr;
160 :
161 0 : if (mGPUChild) {
162 : // OnChannelClosed uses this to check if the shutdown was expected or
163 : // unexpected.
164 0 : mShutdownRequested = true;
165 :
166 : // The channel might already be closed if we got here unexpectedly.
167 0 : if (!mChannelClosed) {
168 0 : mGPUChild->Close();
169 : }
170 :
171 : #ifndef NS_FREE_PERMANENT_DATA
172 : // No need to communicate shutdown, the GPU process doesn't need to
173 : // communicate anything back.
174 : KillHard("NormalShutdown");
175 : #endif
176 :
177 : // If we're shutting down unexpectedly, we're in the middle of handling an
178 : // ActorDestroy for PGPUChild, which is still on the stack. We'll return
179 : // back to OnChannelClosed.
180 : //
181 : // Otherwise, we'll wait for OnChannelClose to be called whenever PGPUChild
182 : // acknowledges shutdown.
183 0 : return;
184 : }
185 :
186 0 : DestroyProcess();
187 : }
188 :
189 : void
190 0 : GPUProcessHost::OnChannelClosed()
191 : {
192 0 : if (!mShutdownRequested) {
193 : // This is an unclean shutdown. Notify our listener that we're going away.
194 0 : mChannelClosed = true;
195 0 : if (mListener) {
196 0 : mListener->OnProcessUnexpectedShutdown(this);
197 : }
198 : }
199 :
200 : // Release the actor.
201 0 : GPUChild::Destroy(Move(mGPUChild));
202 0 : MOZ_ASSERT(!mGPUChild);
203 :
204 : // If the owner of GPUProcessHost already requested shutdown, we can now
205 : // schedule destruction. Otherwise we must wait for someone to call
206 : // Shutdown. Note that GPUProcessManager calls Shutdown within
207 : // OnProcessUnexpectedShutdown.
208 0 : if (mShutdownRequested) {
209 0 : DestroyProcess();
210 : }
211 0 : }
212 :
213 : void
214 0 : GPUProcessHost::KillHard(const char* aReason)
215 : {
216 0 : ProcessHandle handle = GetChildProcessHandle();
217 0 : if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER, false)) {
218 0 : NS_WARNING("failed to kill subprocess!");
219 : }
220 :
221 0 : SetAlreadyDead();
222 0 : }
223 :
224 : uint64_t
225 0 : GPUProcessHost::GetProcessToken() const
226 : {
227 0 : return mProcessToken;
228 : }
229 :
230 : static void
231 0 : DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
232 : {
233 : XRE_GetIOMessageLoop()->
234 0 : PostTask(mozilla::MakeAndAddRef<DeleteTask<GeckoChildProcessHost>>(aSubprocess));
235 0 : }
236 :
237 : void
238 0 : GPUProcessHost::KillProcess()
239 : {
240 0 : KillHard("DiagnosticKill");
241 0 : }
242 :
243 : void
244 0 : GPUProcessHost::DestroyProcess()
245 : {
246 : // Cancel all tasks. We don't want anything triggering after our caller
247 : // expects this to go away.
248 : {
249 0 : MonitorAutoLock lock(mMonitor);
250 0 : mTaskFactory.RevokeAll();
251 : }
252 :
253 : MessageLoop::current()->
254 0 : PostTask(NewRunnableFunction(DelayedDeleteSubprocess, this));
255 0 : }
256 :
257 : } // namespace gfx
258 : } // namespace mozilla
|