Bug 1897589 - Simplify worker shutdown checks. r=jstutte,dom-worker-reviewers
[gecko.git] / ipc / glue / UtilityProcessChild.cpp
blob122e801ba48fef2ce6203cbdb954a93e4f13c472
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 #include "UtilityProcessChild.h"
8 #include "mozilla/AppShutdown.h"
9 #include "mozilla/Logging.h"
10 #include "mozilla/dom/ContentParent.h"
11 #include "mozilla/dom/JSOracleChild.h"
12 #include "mozilla/dom/MemoryReportRequest.h"
13 #include "mozilla/ipc/CrashReporterClient.h"
14 #include "mozilla/ipc/Endpoint.h"
15 #include "mozilla/ipc/UtilityProcessManager.h"
16 #include "mozilla/ipc/UtilityProcessSandboxing.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/RemoteDecoderManagerParent.h"
20 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
21 # include "mozilla/Sandbox.h"
22 # include "mozilla/SandboxProfilerObserver.h"
23 #endif
25 #if defined(XP_OPENBSD) && defined(MOZ_SANDBOX)
26 # include "mozilla/SandboxSettings.h"
27 #endif
29 #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
30 # include "mozilla/SandboxTestingChild.h"
31 #endif
33 #include "mozilla/Telemetry.h"
35 #if defined(XP_WIN)
36 # include "mozilla/WinDllServices.h"
37 # include "mozilla/dom/WindowsUtilsChild.h"
38 # include "mozilla/widget/filedialog/WinFileDialogChild.h"
39 #endif
41 #include "nsDebugImpl.h"
42 #include "nsIXULRuntime.h"
43 #include "nsThreadManager.h"
44 #include "GeckoProfiler.h"
46 #include "mozilla/ipc/ProcessChild.h"
47 #include "mozilla/FOGIPC.h"
48 #include "mozilla/glean/GleanMetrics.h"
50 #include "mozilla/Services.h"
52 namespace mozilla::ipc {
54 using namespace layers;
56 static StaticMutex sUtilityProcessChildMutex;
57 static StaticRefPtr<UtilityProcessChild> sUtilityProcessChild
58 MOZ_GUARDED_BY(sUtilityProcessChildMutex);
60 UtilityProcessChild::UtilityProcessChild() : mChildStartTime(TimeStamp::Now()) {
61 nsDebugImpl::SetMultiprocessMode("Utility");
64 UtilityProcessChild::~UtilityProcessChild() = default;
66 /* static */
67 RefPtr<UtilityProcessChild> UtilityProcessChild::GetSingleton() {
68 MOZ_ASSERT(XRE_IsUtilityProcess());
69 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
70 return nullptr;
72 StaticMutexAutoLock lock(sUtilityProcessChildMutex);
73 if (!sUtilityProcessChild) {
74 sUtilityProcessChild = new UtilityProcessChild();
76 return sUtilityProcessChild;
79 /* static */
80 RefPtr<UtilityProcessChild> UtilityProcessChild::Get() {
81 StaticMutexAutoLock lock(sUtilityProcessChildMutex);
82 return sUtilityProcessChild;
85 bool UtilityProcessChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
86 const nsCString& aParentBuildID,
87 uint64_t aSandboxingKind) {
88 MOZ_ASSERT(NS_IsMainThread());
90 // Initialize the thread manager before starting IPC. Otherwise, messages
91 // may be posted to the main thread and we won't be able to process them.
92 if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
93 return false;
96 // Now it's safe to start IPC.
97 if (NS_WARN_IF(!aEndpoint.Bind(this))) {
98 return false;
101 // This must be checked before any IPDL message, which may hit sentinel
102 // errors due to parent and content processes having different
103 // versions.
104 MessageChannel* channel = GetIPCChannel();
105 if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID.get())) {
106 // We need to quit this process if the buildID doesn't match the parent's.
107 // This can occur when an update occurred in the background.
108 ipc::ProcessChild::QuickExit();
111 // Init crash reporter support.
112 ipc::CrashReporterClient::InitSingleton(this);
114 if (NS_FAILED(NS_InitMinimalXPCOM())) {
115 return false;
118 mSandbox = (SandboxingKind)aSandboxingKind;
120 // At the moment, only ORB uses JSContext in the
121 // Utility Process and ORB uses GENERIC_UTILITY
122 if (mSandbox == SandboxingKind::GENERIC_UTILITY) {
123 if (!JS_FrontendOnlyInit()) {
124 return false;
126 #if defined(__OpenBSD__) && defined(MOZ_SANDBOX)
127 // Bug 1823458: delay pledge initialization, otherwise
128 // JS_FrontendOnlyInit triggers sysctl(KERN_PROC_ID) which isnt
129 // permitted with the current pledge.utility config
130 StartOpenBSDSandbox(GeckoProcessType_Utility, mSandbox);
131 #endif
134 profiler_set_process_name(nsCString("Utility Process"));
136 // Notify the parent process that we have finished our init and that it can
137 // now resolve the pending promise of process startup
138 SendInitCompleted();
140 PROFILER_MARKER_UNTYPED(
141 "UtilityProcessChild::SendInitCompleted", IPC,
142 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
144 RunOnShutdown(
145 [sandboxKind = mSandbox] {
146 StaticMutexAutoLock lock(sUtilityProcessChildMutex);
147 sUtilityProcessChild = nullptr;
148 if (sandboxKind == SandboxingKind::GENERIC_UTILITY) {
149 JS_FrontendOnlyShutDown();
152 ShutdownPhase::XPCOMShutdownFinal);
154 return true;
157 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
158 extern "C" {
159 void CGSShutdownServerConnections();
161 #endif
163 mozilla::ipc::IPCResult UtilityProcessChild::RecvInit(
164 const Maybe<FileDescriptor>& aBrokerFd,
165 const bool& aCanRecordReleaseTelemetry,
166 const bool& aIsReadyForBackgroundProcessing) {
167 // Do this now (before closing WindowServer on macOS) to avoid risking
168 // blocking in GetCurrentProcess() called on that platform
169 mozilla::ipc::SetThisProcessName("Utility Process");
171 #if defined(MOZ_SANDBOX)
172 # if defined(XP_MACOSX)
173 // Close all current connections to the WindowServer. This ensures that the
174 // Activity Monitor will not label the content process as "Not responding"
175 // because it's not running a native event loop. See bug 1384336.
176 CGSShutdownServerConnections();
178 # elif defined(XP_LINUX)
179 int fd = -1;
180 if (aBrokerFd.isSome()) {
181 fd = aBrokerFd.value().ClonePlatformHandle().release();
184 RegisterProfilerObserversForSandboxProfiler();
185 SetUtilitySandbox(fd, mSandbox);
187 # endif // XP_MACOSX/XP_LINUX
188 #endif // MOZ_SANDBOX
190 #if defined(XP_WIN)
191 if (aCanRecordReleaseTelemetry) {
192 RefPtr<DllServices> dllSvc(DllServices::Get());
193 dllSvc->StartUntrustedModulesProcessor(aIsReadyForBackgroundProcessing);
195 #endif // defined(XP_WIN)
197 PROFILER_MARKER_UNTYPED(
198 "UtilityProcessChild::RecvInit", IPC,
199 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
200 return IPC_OK();
203 mozilla::ipc::IPCResult UtilityProcessChild::RecvPreferenceUpdate(
204 const Pref& aPref) {
205 Preferences::SetPreference(aPref);
206 return IPC_OK();
209 mozilla::ipc::IPCResult UtilityProcessChild::RecvInitProfiler(
210 Endpoint<PProfilerChild>&& aEndpoint) {
211 mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
212 return IPC_OK();
215 mozilla::ipc::IPCResult UtilityProcessChild::RecvRequestMemoryReport(
216 const uint32_t& aGeneration, const bool& aAnonymize,
217 const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile,
218 const RequestMemoryReportResolver& aResolver) {
219 nsPrintfCString processName("Utility (pid %" PRIPID
220 ", sandboxingKind %" PRIu64 ")",
221 base::GetCurrentProcId(), mSandbox);
223 mozilla::dom::MemoryReportRequestClient::Start(
224 aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName,
225 [&](const MemoryReport& aReport) {
226 Unused << GetSingleton()->SendAddMemoryReport(aReport);
228 aResolver);
229 return IPC_OK();
232 #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
233 mozilla::ipc::IPCResult UtilityProcessChild::RecvInitSandboxTesting(
234 Endpoint<PSandboxTestingChild>&& aEndpoint) {
235 if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) {
236 return IPC_FAIL(
237 this, "InitSandboxTesting failed to initialise the child process.");
239 return IPC_OK();
241 #endif
243 mozilla::ipc::IPCResult UtilityProcessChild::RecvFlushFOGData(
244 FlushFOGDataResolver&& aResolver) {
245 glean::FlushFOGData(std::move(aResolver));
246 return IPC_OK();
249 mozilla::ipc::IPCResult UtilityProcessChild::RecvTestTriggerMetrics(
250 TestTriggerMetricsResolver&& aResolve) {
251 mozilla::glean::test_only_ipc::a_counter.Add(
252 nsIXULRuntime::PROCESS_TYPE_UTILITY);
253 aResolve(true);
254 return IPC_OK();
257 mozilla::ipc::IPCResult UtilityProcessChild::RecvTestTelemetryProbes() {
258 const uint32_t kExpectedUintValue = 42;
259 Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UTILITY_ONLY_UINT,
260 kExpectedUintValue);
261 return IPC_OK();
264 mozilla::ipc::IPCResult
265 UtilityProcessChild::RecvStartUtilityAudioDecoderService(
266 Endpoint<PUtilityAudioDecoderParent>&& aEndpoint,
267 nsTArray<gfx::GfxVarUpdate>&& aUpdates) {
268 PROFILER_MARKER_UNTYPED(
269 "UtilityProcessChild::RecvStartUtilityAudioDecoderService", MEDIA,
270 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
271 mUtilityAudioDecoderInstance =
272 new UtilityAudioDecoderParent(std::move(aUpdates));
273 if (!mUtilityAudioDecoderInstance) {
274 return IPC_FAIL(this, "Failed to create UtilityAudioDecoderParent");
277 mUtilityAudioDecoderInstance->Start(std::move(aEndpoint));
278 return IPC_OK();
281 mozilla::ipc::IPCResult UtilityProcessChild::RecvStartJSOracleService(
282 Endpoint<PJSOracleChild>&& aEndpoint) {
283 PROFILER_MARKER_UNTYPED(
284 "UtilityProcessChild::RecvStartJSOracleService", JS,
285 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
286 mJSOracleInstance = new mozilla::dom::JSOracleChild();
287 if (!mJSOracleInstance) {
288 return IPC_FAIL(this, "Failed to create JSOracleParent");
291 mJSOracleInstance->Start(std::move(aEndpoint));
292 return IPC_OK();
295 #if defined(XP_WIN)
296 mozilla::ipc::IPCResult UtilityProcessChild::RecvStartWindowsUtilsService(
297 Endpoint<dom::PWindowsUtilsChild>&& aEndpoint) {
298 PROFILER_MARKER_UNTYPED(
299 "UtilityProcessChild::RecvStartWindowsUtilsService", OTHER,
300 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
301 mWindowsUtilsInstance = new dom::WindowsUtilsChild();
302 if (!mWindowsUtilsInstance) {
303 return IPC_FAIL(this, "Failed to create WindowsUtilsChild");
306 [[maybe_unused]] bool ok = std::move(aEndpoint).Bind(mWindowsUtilsInstance);
307 MOZ_ASSERT(ok);
308 return IPC_OK();
311 mozilla::ipc::IPCResult UtilityProcessChild::RecvStartWinFileDialogService(
312 Endpoint<widget::filedialog::PWinFileDialogChild>&& aEndpoint) {
313 PROFILER_MARKER_UNTYPED(
314 "UtilityProcessChild::RecvStartWinFileDialogService", OTHER,
315 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
317 auto instance = MakeRefPtr<widget::filedialog::WinFileDialogChild>();
318 if (!instance) {
319 return IPC_FAIL(this, "Failed to create WinFileDialogChild");
322 bool const ok = std::move(aEndpoint).Bind(instance.get());
323 if (!ok) {
324 return IPC_FAIL(this, "Failed to bind created WinFileDialogChild");
327 return IPC_OK();
330 mozilla::ipc::IPCResult UtilityProcessChild::RecvGetUntrustedModulesData(
331 GetUntrustedModulesDataResolver&& aResolver) {
332 RefPtr<DllServices> dllSvc(DllServices::Get());
333 dllSvc->GetUntrustedModulesData()->Then(
334 GetMainThreadSerialEventTarget(), __func__,
335 [aResolver](Maybe<UntrustedModulesData>&& aData) {
336 aResolver(std::move(aData));
338 [aResolver](nsresult aReason) { aResolver(Nothing()); });
339 return IPC_OK();
342 mozilla::ipc::IPCResult
343 UtilityProcessChild::RecvUnblockUntrustedModulesThread() {
344 if (nsCOMPtr<nsIObserverService> obs =
345 mozilla::services::GetObserverService()) {
346 obs->NotifyObservers(nullptr, "unblock-untrusted-modules-thread", nullptr);
348 return IPC_OK();
350 #endif // defined(XP_WIN)
352 void UtilityProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
353 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
354 DestroySandboxProfiler();
355 #endif
357 if (AbnormalShutdown == aWhy) {
358 NS_WARNING("Shutting down Utility process early due to a crash!");
359 ipc::ProcessChild::QuickExit();
362 // Send the last bits of Glean data over to the main process.
363 glean::FlushFOGData(
364 [](ByteBuf&& aBuf) { glean::SendFOGData(std::move(aBuf)); });
366 #ifndef NS_FREE_PERMANENT_DATA
367 ProcessChild::QuickExit();
368 #else
370 if (mProfilerController) {
371 mProfilerController->Shutdown();
372 mProfilerController = nullptr;
375 uint32_t timeout = 0;
376 if (mUtilityAudioDecoderInstance) {
377 mUtilityAudioDecoderInstance = nullptr;
378 timeout = 10 * 1000;
381 mJSOracleInstance = nullptr;
383 # ifdef XP_WIN
384 mWindowsUtilsInstance = nullptr;
385 # endif
387 // Wait until all RemoteDecoderManagerParent have closed.
388 // It is still possible some may not have clean up yet, and we might hit
389 // timeout. Our xpcom-shutdown listener should take care of cleaning the
390 // reference of our singleton.
392 // FIXME: Should move from using AsyncBlockers to proper
393 // nsIAsyncShutdownService once it is not JS, see bug 1760855
394 mShutdownBlockers.WaitUntilClear(timeout)->Then(
395 GetCurrentSerialEventTarget(), __func__, [&]() {
396 # ifdef XP_WIN
398 RefPtr<DllServices> dllSvc(DllServices::Get());
399 dllSvc->DisableFull();
401 # endif // defined(XP_WIN)
403 ipc::CrashReporterClient::DestroySingleton();
404 XRE_ShutdownChildProcess();
406 #endif // NS_FREE_PERMANENT_DATA
409 } // namespace mozilla::ipc