summaryrefslogtreecommitdiffstats
path: root/xpcom/base/AppShutdown.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/base/AppShutdown.cpp')
-rw-r--r--xpcom/base/AppShutdown.cpp246
1 files changed, 246 insertions, 0 deletions
diff --git a/xpcom/base/AppShutdown.cpp b/xpcom/base/AppShutdown.cpp
new file mode 100644
index 0000000000..bf47f0c76c
--- /dev/null
+++ b/xpcom/base/AppShutdown.cpp
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AppShutdown.h"
+
+#ifdef XP_WIN
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+
+#include "GeckoProfiler.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/Printf.h"
+#include "mozilla/scache/StartupCache.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/StartupTimeline.h"
+#include "mozilla/StaticPrefs_toolkit.h"
+#include "mozilla/LateWriteChecks.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsAppRunner.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsICertStorage.h"
+#include "nsThreadUtils.h"
+#include "prenv.h"
+
+#ifdef MOZ_NEW_XULSTORE
+# include "mozilla/XULStore.h"
+#endif
+
+namespace mozilla {
+
+static ShutdownPhase sFastShutdownPhase = ShutdownPhase::NotInShutdown;
+static ShutdownPhase sLateWriteChecksPhase = ShutdownPhase::NotInShutdown;
+static AppShutdownMode sShutdownMode = AppShutdownMode::Normal;
+static Atomic<bool, MemoryOrdering::Relaxed> sIsShuttingDown;
+static int sExitCode = 0;
+
+// These environment variable strings are all deliberately copied and leaked
+// due to requirements of PR_SetEnv and similar.
+static char* sSavedXulAppFile = nullptr;
+#ifdef XP_WIN
+static wchar_t* sSavedProfDEnvVar = nullptr;
+static wchar_t* sSavedProfLDEnvVar = nullptr;
+#else
+static char* sSavedProfDEnvVar = nullptr;
+static char* sSavedProfLDEnvVar = nullptr;
+#endif
+
+ShutdownPhase GetShutdownPhaseFromPrefValue(int32_t aPrefValue) {
+ switch (aPrefValue) {
+ case 1:
+ return ShutdownPhase::ShutdownPostLastCycleCollection;
+ case 2:
+ return ShutdownPhase::ShutdownThreads;
+ case 3:
+ return ShutdownPhase::Shutdown;
+ // NOTE: the remaining values from the ShutdownPhase enum will be added
+ // when we're at least reasonably confident that the world won't come
+ // crashing down if we do a fast shutdown at that point.
+ }
+ return ShutdownPhase::NotInShutdown;
+}
+
+bool AppShutdown::IsShuttingDown() { return sIsShuttingDown; }
+
+int AppShutdown::GetExitCode() { return sExitCode; }
+
+void AppShutdown::SaveEnvVarsForPotentialRestart() {
+ const char* s = PR_GetEnv("XUL_APP_FILE");
+ if (s) {
+ sSavedXulAppFile = Smprintf("%s=%s", "XUL_APP_FILE", s).release();
+ MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(sSavedXulAppFile);
+ }
+}
+
+void AppShutdown::MaybeDoRestart() {
+ if (sShutdownMode == AppShutdownMode::Restart) {
+ StopLateWriteChecks();
+
+ // Since we'll be launching our child while we're still alive, make sure
+ // we've unlocked the profile first, otherwise the child could hit its
+ // profile lock check before we've exited and thus released our lock.
+ UnlockProfile();
+
+ if (sSavedXulAppFile) {
+ PR_SetEnv(sSavedXulAppFile);
+ }
+
+#ifdef XP_WIN
+ if (sSavedProfDEnvVar && !EnvHasValue("XRE_PROFILE_PATH")) {
+ SetEnvironmentVariableW(L"XRE_PROFILE_PATH", sSavedProfDEnvVar);
+ }
+ if (sSavedProfLDEnvVar && !EnvHasValue("XRE_PROFILE_LOCAL_PATH")) {
+ SetEnvironmentVariableW(L"XRE_PROFILE_LOCAL_PATH", sSavedProfLDEnvVar);
+ }
+#else
+ if (sSavedProfDEnvVar && !EnvHasValue("XRE_PROFILE_PATH")) {
+ PR_SetEnv(sSavedProfDEnvVar);
+ }
+ if (sSavedProfLDEnvVar && !EnvHasValue("XRE_PROFILE_LOCAL_PATH")) {
+ PR_SetEnv(sSavedProfLDEnvVar);
+ }
+#endif
+
+ LaunchChild(true);
+ }
+}
+
+#ifdef XP_WIN
+wchar_t* CopyPathIntoNewWCString(nsIFile* aFile) {
+ wchar_t* result = nullptr;
+ nsAutoString resStr;
+ aFile->GetPath(resStr);
+ if (resStr.Length() > 0) {
+ result = (wchar_t*)malloc((resStr.Length() + 1) * sizeof(wchar_t));
+ if (result) {
+ wcscpy(result, resStr.get());
+ result[resStr.Length()] = 0;
+ }
+ }
+
+ return result;
+}
+#endif
+
+void AppShutdown::Init(AppShutdownMode aMode, int aExitCode) {
+ if (sShutdownMode == AppShutdownMode::Normal) {
+ sShutdownMode = aMode;
+ }
+
+ sExitCode = aExitCode;
+
+ // Late-write checks needs to find the profile directory, so it has to
+ // be initialized before services::Shutdown or (because of
+ // xpcshell tests replacing the service) modules being unloaded.
+ InitLateWriteChecks();
+
+ int32_t fastShutdownPref = StaticPrefs::toolkit_shutdown_fastShutdownStage();
+ sFastShutdownPhase = GetShutdownPhaseFromPrefValue(fastShutdownPref);
+ int32_t lateWriteChecksPref =
+ StaticPrefs::toolkit_shutdown_lateWriteChecksStage();
+ sLateWriteChecksPhase = GetShutdownPhaseFromPrefValue(lateWriteChecksPref);
+
+ // Very early shutdowns can happen before the startup cache is even
+ // initialized; don't bother initializing it during shutdown.
+ if (auto* cache = scache::StartupCache::GetSingletonNoInit()) {
+ cache->MaybeInitShutdownWrite();
+ }
+}
+
+void AppShutdown::MaybeFastShutdown(ShutdownPhase aPhase) {
+ // For writes which we want to ensure are recorded, we don't want to trip
+ // the late write checking code. Anything that writes to disk and which
+ // we don't want to skip should be listed out explicitly in this section.
+ if (aPhase == sFastShutdownPhase || aPhase == sLateWriteChecksPhase) {
+ if (auto* cache = scache::StartupCache::GetSingletonNoInit()) {
+ cache->EnsureShutdownWriteComplete();
+ }
+
+ nsresult rv;
+#ifdef MOZ_NEW_XULSTORE
+ rv = XULStore::Shutdown();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "XULStore::Shutdown() failed.");
+#endif
+
+ nsCOMPtr<nsICertStorage> certStorage =
+ do_GetService("@mozilla.org/security/certstorage;1", &rv);
+ if (NS_SUCCEEDED(rv)) {
+ SpinEventLoopUntil([&]() {
+ int32_t remainingOps;
+ nsresult rv = certStorage->GetRemainingOperationCount(&remainingOps);
+ NS_ASSERTION(NS_SUCCEEDED(rv),
+ "nsICertStorage::getRemainingOperationCount failed during "
+ "shutdown");
+ return NS_FAILED(rv) || remainingOps <= 0;
+ });
+ }
+ }
+ if (aPhase == sFastShutdownPhase) {
+ StopLateWriteChecks();
+ RecordShutdownEndTimeStamp();
+ MaybeDoRestart();
+
+#ifdef MOZ_GECKO_PROFILER
+ profiler_shutdown(IsFastShutdown::Yes);
+#endif
+
+ DoImmediateExit(sExitCode);
+ } else if (aPhase == sLateWriteChecksPhase) {
+#ifdef XP_MACOSX
+ OnlyReportDirtyWrites();
+#endif /* XP_MACOSX */
+ BeginLateWriteChecks();
+ }
+}
+
+void AppShutdown::OnShutdownConfirmed() {
+ sIsShuttingDown = true;
+ // If we're restarting, we need to save environment variables correctly
+ // while everything is still alive to do so.
+ if (sShutdownMode == AppShutdownMode::Restart) {
+ nsCOMPtr<nsIFile> profD;
+ nsCOMPtr<nsIFile> profLD;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profD));
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
+ getter_AddRefs(profLD));
+#ifdef XP_WIN
+ sSavedProfDEnvVar = CopyPathIntoNewWCString(profD);
+ sSavedProfLDEnvVar = CopyPathIntoNewWCString(profLD);
+#else
+ nsAutoCString profDStr;
+ profD->GetNativePath(profDStr);
+ sSavedProfDEnvVar =
+ Smprintf("XRE_PROFILE_PATH=%s", profDStr.get()).release();
+ nsAutoCString profLDStr;
+ profLD->GetNativePath(profLDStr);
+ sSavedProfLDEnvVar =
+ Smprintf("XRE_PROFILE_LOCAL_PATH=%s", profLDStr.get()).release();
+#endif
+ MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(sSavedProfDEnvVar);
+ MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(sSavedProfLDEnvVar);
+ }
+}
+
+void AppShutdown::DoImmediateExit(int aExitCode) {
+#ifdef XP_WIN
+ HANDLE process = ::GetCurrentProcess();
+ if (::TerminateProcess(process, aExitCode)) {
+ ::WaitForSingleObject(process, INFINITE);
+ }
+ MOZ_CRASH("TerminateProcess failed.");
+#else
+ _exit(aExitCode);
+#endif
+}
+
+bool AppShutdown::IsRestarting() {
+ return sShutdownMode == AppShutdownMode::Restart;
+}
+
+} // namespace mozilla