Bug 1689953: Harmonize shutdown phase definitions across nsTerminator and AppShutdown r=dthayer,chutten

This patch wants to solve several quirks around the shutdown terminator.

 - Use the same shutdown phase definitions in AppShutdown and nsTerminator. This touches quite a few files.
 - Ensure that the terminator phase shift is handled before any shutdown observer notifications are sent and reduce its heartbeat duration.
 - Add missing phases to the shutdown telemetry.

Please note that this changes the unit of "tick" to 100ms rather than 1s.
As a side effect, we also remove the obsolete "shutdown-persist" context.

While the existing test coverage continues to prove the most important functions, we acknowledge the wish for better test coverage with [[ https://bugzilla.mozilla.org/show_bug.cgi?id=1693966 | bug 1693966 ]].

Differential Revision: https://phabricator.services.mozilla.com/D103626
This commit is contained in:
Jens Stutte 2021-02-26 21:33:29 +00:00
Родитель d270fbf50c
Коммит f6d52040b9
42 изменённых файлов: 376 добавлений и 241 удалений

Просмотреть файл

@ -22,7 +22,7 @@ DocAccessibleChild::DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager)
MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
if (!sPlatformChild) {
sPlatformChild = new PlatformChild();
ClearOnShutdown(&sPlatformChild, ShutdownPhase::Shutdown);
ClearOnShutdown(&sPlatformChild, ShutdownPhase::XPCOMShutdown);
}
SetManager(aManager);

Просмотреть файл

@ -28,7 +28,7 @@ async function fake_profile_change() {
}, "cookie-db-closed");
Services.cookies
.QueryInterface(Ci.nsIObserver)
.observe(null, "profile-before-change", "shutdown-persist");
.observe(null, "profile-before-change", null);
});
await new Promise(resolve => {
Services.obs.addObserver(function waitForDBOpen() {

Просмотреть файл

@ -575,7 +575,7 @@ ContentChild::ContentChild()
// happens without requiring the observer service at this time.
if (!sShutdownCanary) {
sShutdownCanary = new ShutdownCanary();
ClearOnShutdown(&sShutdownCanary, ShutdownPhase::Shutdown);
ClearOnShutdown(&sShutdownCanary, ShutdownPhase::XPCOMShutdown);
}
}

Просмотреть файл

@ -2839,7 +2839,7 @@ bool ContentParent::InitInternal(ProcessPriority aInitialPriority) {
// can't init the process without it, and since we're going to be canceling
// whatever load attempt that initiated this process creation anyway, just
// bail out now if shutdown has already started.
if (PastShutdownPhase(ShutdownPhase::Shutdown)) {
if (PastShutdownPhase(ShutdownPhase::XPCOMShutdown)) {
return false;
}

Просмотреть файл

@ -773,7 +773,7 @@ RefPtr<MediaCache> MediaCache::GetMediaCache(int64_t aContentLength,
thread->Shutdown();
}
} sClearThread;
ClearOnShutdown(&sClearThread, ShutdownPhase::ShutdownThreads);
ClearOnShutdown(&sClearThread, ShutdownPhase::XPCOMShutdownThreads);
}
if (!sThread) {

Просмотреть файл

@ -116,9 +116,11 @@ bool DecoderDoctorLogger::EnsureLogIsEnabled() {
TaskCategory::Other,
NS_NewRunnableFunction("DDLogger shutdown setup", [] {
sDDLogShutdowner = MakeUnique<DDLogShutdowner>();
ClearOnShutdown(&sDDLogShutdowner, ShutdownPhase::Shutdown);
ClearOnShutdown(&sDDLogShutdowner,
ShutdownPhase::XPCOMShutdown);
sDDLogDeleter = MakeUnique<DDLogDeleter>();
ClearOnShutdown(&sDDLogDeleter, ShutdownPhase::ShutdownThreads);
ClearOnShutdown(&sDDLogDeleter,
ShutdownPhase::XPCOMShutdownThreads);
})));
// Nobody else should change the state when *we* are enabling logging.

Просмотреть файл

@ -257,7 +257,7 @@ already_AddRefed<Promise> MediaCapabilities::DecodingInfo(
NS_NewRunnableFunction(
"MediaCapabilities::AllocPolicy:Video", []() {
ClearOnShutdown(&sVideoAllocPolicy,
ShutdownPhase::ShutdownThreads);
ShutdownPhase::XPCOMShutdownThreads);
}));
return new SingleAllocPolicy(TrackInfo::TrackType::kVideoTrack,
taskQueue);

Просмотреть файл

@ -97,7 +97,8 @@ NotNull<AllocPolicy*> GlobalAllocPolicy::Instance(TrackType aTrack) {
TaskCategory::Other,
NS_NewRunnableFunction(
"GlobalAllocPolicy::GlobalAllocPolicy:Audio", []() {
ClearOnShutdown(&sAudioPolicy, ShutdownPhase::ShutdownThreads);
ClearOnShutdown(&sAudioPolicy,
ShutdownPhase::XPCOMShutdownThreads);
}));
return new AllocPolicyImpl(MediaDecoderLimitDefault());
}();
@ -108,7 +109,8 @@ NotNull<AllocPolicy*> GlobalAllocPolicy::Instance(TrackType aTrack) {
TaskCategory::Other,
NS_NewRunnableFunction(
"GlobalAllocPolicy::GlobalAllocPolicy:Audio", []() {
ClearOnShutdown(&sVideoPolicy, ShutdownPhase::ShutdownThreads);
ClearOnShutdown(&sVideoPolicy,
ShutdownPhase::XPCOMShutdownThreads);
}));
return new AllocPolicyImpl(MediaDecoderLimitDefault());
}();

Просмотреть файл

@ -30,7 +30,7 @@ CubebDeviceEnumerator* CubebDeviceEnumerator::GetInstance() {
sInstance = new CubebDeviceEnumerator();
static bool clearOnShutdownSetup = []() -> bool {
auto setClearOnShutdown = []() -> void {
ClearOnShutdown(&sInstance, ShutdownPhase::ShutdownThreads);
ClearOnShutdown(&sInstance, ShutdownPhase::XPCOMShutdownThreads);
};
if (NS_IsMainThread()) {
setClearOnShutdown();

Просмотреть файл

@ -241,7 +241,7 @@ class SingletonThreadHolder final {
static StaticRefPtr<SingletonThreadHolder> sThread;
static void ClearSingletonOnShutdown() {
ClearOnShutdown(&sThread, ShutdownPhase::ShutdownLoaders);
ClearOnShutdown(&sThread, ShutdownPhase::XPCOMShutdownLoaders);
}
#endif

Просмотреть файл

@ -176,7 +176,7 @@ void PluginProcessChild::CleanUp() {
NS_LogTerm();
#endif
mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownFinal);
mozilla::KillClearOnShutdown(ShutdownPhase::XPCOMShutdownFinal);
AbstractThread::ShutdownMainThread();

Просмотреть файл

@ -625,7 +625,7 @@ BackgroundSessionStorageManager* BackgroundSessionStorageManager::GetOrCreate(
return;
}
},
ShutdownPhase::Shutdown);
ShutdownPhase::XPCOMShutdown);
}));
}

Просмотреть файл

@ -89,7 +89,7 @@ class U2FPrefManager final : public nsIObserver {
PREF_WEBAUTHN_ANDROID_FIDO2_ENABLED);
Preferences::AddStrongObserver(gPrefManager,
PREF_WEBAUTHN_ALLOW_DIRECT_ATTESTATION);
ClearOnShutdown(&gPrefManager, ShutdownPhase::ShutdownThreads);
ClearOnShutdown(&gPrefManager, ShutdownPhase::XPCOMShutdownThreads);
}
return gPrefManager;
}

Просмотреть файл

@ -161,7 +161,7 @@ VRManager::VRManager()
VRServiceHost::Init(mVRProcessEnabled);
mServiceHost = VRServiceHost::Get();
// We must shutdown before VRServiceHost, which is cleared
// on ShutdownPhase::ShutdownFinal, potentially before VRManager.
// on ShutdownPhase::XPCOMShutdownFinal, potentially before VRManager.
// We hold a reference to VRServiceHost to ensure it stays
// alive until we have shut down.
#else

Просмотреть файл

@ -240,7 +240,8 @@ void VRServiceHost::PuppetReset() {
// If we're already into ShutdownFinal, the VRPuppetCommandBuffer instance
// will have been cleared, so don't try to access it after that point.
if (!mVRProcessEnabled &&
!(NS_IsMainThread() && PastShutdownPhase(ShutdownPhase::ShutdownFinal))) {
!(NS_IsMainThread() &&
PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal))) {
// Puppet is running in this process, tell it to reset directly.
VRPuppetCommandBuffer::Get().Reset();
}

Просмотреть файл

@ -152,7 +152,7 @@ LocaleService* LocaleService::GetInstance() {
}
// DOM might use ICUUtils and LocaleService during UnbindFromTree by
// final cycle collection.
ClearOnShutdown(&sInstance, ShutdownPhase::ShutdownPostLastCycleCollection);
ClearOnShutdown(&sInstance, ShutdownPhase::CCPostLastCycleCollection);
}
return sInstance;
}

Просмотреть файл

@ -472,7 +472,7 @@ nsresult nsStringBundleBase::ParseProperties(nsIPersistentProperties** aProps) {
nsresult nsStringBundle::LoadProperties() {
// Something such as Necko might use string bundle after ClearOnShutdown is
// called. LocaleService etc is already down, so we cannot get bundle data.
if (PastShutdownPhase(ShutdownPhase::Shutdown)) {
if (PastShutdownPhase(ShutdownPhase::XPCOMShutdown)) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
@ -500,7 +500,7 @@ nsresult SharedStringBundle::LoadProperties() {
// our string bundles come from). Since shared string bundles won't be
// useful after shutdown has started anyway (and we almost certainly got
// here from a pre-load attempt in an idle task), just bail out.
if (PastShutdownPhase(ShutdownPhase::Shutdown)) {
if (PastShutdownPhase(ShutdownPhase::XPCOMShutdown)) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}

Просмотреть файл

@ -126,7 +126,7 @@ nsCOMPtr<nsIThread> EnsureMTA::GetMTAThread() {
BackgroundMTAData* bgData = new BackgroundMTAData();
auto setClearOnShutdown = [ptr = &sMTAData]() -> void {
ClearOnShutdown(ptr, ShutdownPhase::ShutdownThreads);
ClearOnShutdown(ptr, ShutdownPhase::XPCOMShutdownThreads);
};
if (NS_IsMainThread()) {

Просмотреть файл

@ -500,7 +500,7 @@ void RegisterArrayData(const ArrayData* aArrayData, size_t aLength) {
if (!sArrayData) {
sArrayData = new Vector<std::pair<const ArrayData*, size_t>>();
ClearOnShutdown(&sArrayData, ShutdownPhase::ShutdownThreads);
ClearOnShutdown(&sArrayData, ShutdownPhase::XPCOMShutdownThreads);
}
MOZ_ALWAYS_TRUE(sArrayData->emplaceBack(std::make_pair(aArrayData, aLength)));

Просмотреть файл

@ -1220,7 +1220,7 @@ nsresult mozJSComponentLoader::Import(JSContext* aCx,
!mInProgressImports.Get(info.Key(), &mod)) {
// We're trying to import a new JSM, but we're late in shutdown and this
// will likely not succeed and might even crash, so fail here.
if (PastShutdownPhase(ShutdownPhase::ShutdownFinal)) {
if (PastShutdownPhase(ShutdownPhase::XPCOMShutdownFinal)) {
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}

Просмотреть файл

@ -172,8 +172,7 @@ already_AddRefed<nsHttpHandler> nsHttpHandler::GetInstance() {
MOZ_ASSERT(NS_SUCCEEDED(rv));
// There is code that may be executed during the final cycle collection
// shutdown and still referencing gHttpHandler.
ClearOnShutdown(&gHttpHandler,
ShutdownPhase::ShutdownPostLastCycleCollection);
ClearOnShutdown(&gHttpHandler, ShutdownPhase::CCPostLastCycleCollection);
}
RefPtr<nsHttpHandler> httpHandler = gHttpHandler;
return httpHandler.forget();

Просмотреть файл

@ -100,7 +100,7 @@ function do_close_profile(generator) {
// Close the db.
let service = Services.cookies.QueryInterface(Ci.nsIObserver);
service.observe(null, "profile-before-change", "shutdown-persist");
service.observe(null, "profile-before-change", null);
}
function _promise_observer(topic) {
@ -131,7 +131,7 @@ function promise_close_profile() {
// Close the db.
let service = Services.cookies.QueryInterface(Ci.nsIObserver);
service.observe(null, "profile-before-change", "shutdown-persist");
service.observe(null, "profile-before-change", null);
return promise;
}

Просмотреть файл

@ -100,7 +100,7 @@ static LinkedList<ChannelWrapper>& ChannelList() {
static UniquePtr<ChannelListHolder> sChannelList;
if (!sChannelList) {
sChannelList.reset(new ChannelListHolder());
ClearOnShutdown(&sChannelList, ShutdownPhase::Shutdown);
ClearOnShutdown(&sChannelList, ShutdownPhase::XPCOMShutdown);
}
return *sChannelList;
}

Просмотреть файл

@ -441,11 +441,10 @@ nsAppStartup::Quit(uint32_t aMode, int aExitCode, bool* aUserAllowedQuit) {
// No chance of the shutdown being cancelled from here on; tell people
// we're shutting down for sure while all services are still available.
if (obsService) {
bool isRestarting = mozilla::AppShutdown::IsRestarting();
obsService->NotifyObservers(nullptr, "quit-application",
isRestarting ? u"restart" : u"shutdown");
}
bool isRestarting = mozilla::AppShutdown::IsRestarting();
mozilla::AppShutdown::AdvanceShutdownPhase(
mozilla::ShutdownPhase::AppShutdownConfirmed,
isRestarting ? u"restart" : u"shutdown");
if (!mRunning) {
postedExitEvent = true;

Просмотреть файл

@ -12632,8 +12632,21 @@
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"bug_numbers": [1689953],
"alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
"description": "Duration of shutdown phase quit-application, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_NET_TEARDOWN": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec", "thunderbird"],
"expires_in_version": "never",
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"bug_numbers": [1689953],
"alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
"description": "Duration of shutdown phase profile-change-net-teardown, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec", "thunderbird"],
@ -12641,17 +12654,10 @@
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"bug_numbers": [1689953],
"alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
"description": "Duration of shutdown phase profile-change-teardown, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec", "thunderbird"],
"expires_in_version": "never",
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"description": "Duration of shutdown phase xpcom-will-shutdown, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec", "thunderbird"],
@ -12659,8 +12665,43 @@
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"bug_numbers": [1689953],
"alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
"description": "Duration of shutdown phase profile-before-change, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE_QM": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec", "thunderbird"],
"expires_in_version": "never",
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"bug_numbers": [1689953],
"alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
"description": "Duration of shutdown phase profile-before-change-qm, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec", "thunderbird"],
"expires_in_version": "never",
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"bug_numbers": [1689953],
"alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
"description": "Duration of shutdown phase xpcom-will-shutdown, as measured by the shutdown terminator, in seconds of activity"
},
"SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_SHUTDOWN": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec", "thunderbird"],
"expires_in_version": "never",
"kind": "exponential",
"high": 65,
"n_buckets": 10,
"bug_numbers": [1689953],
"alert_emails": ["dothayer@mozilla.com, jstutte@mozilla.com"],
"description": "Duration of shutdown phase xpcom-shutdown, as measured by the shutdown terminator, in seconds of activity"
},
"TAP_TO_LOAD_ENABLED": {
"record_in_processes": ["main", "content"],
"products": ["firefox", "fennec"],

Просмотреть файл

@ -316,10 +316,6 @@
"SERVICE_WORKER_WAS_SPAWNED",
"SHOULD_AUTO_DETECT_LANGUAGE",
"SHOULD_TRANSLATION_UI_APPEAR",
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE",
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN",
"SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION",
"SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN",
"SLOW_ADDON_WARNING_RESPONSE_TIME",
"SLOW_ADDON_WARNING_STATES",
"STARTUP_CRASH_DETECTED",
@ -792,10 +788,6 @@
"SERVICE_WORKER_WAS_SPAWNED",
"SHOULD_AUTO_DETECT_LANGUAGE",
"SHOULD_TRANSLATION_UI_APPEAR",
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE",
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN",
"SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION",
"SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN",
"SLOW_ADDON_WARNING_RESPONSE_TIME",
"SLOW_ADDON_WARNING_STATES",
"SLOW_SCRIPT_NOTICE_COUNT",

Просмотреть файл

@ -31,11 +31,16 @@ function nsTerminatorTelemetry() {}
var HISTOGRAMS = {
"quit-application": "SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION",
"profile-change-net-teardown":
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_NET_TEARDOWN",
"profile-change-teardown":
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN",
"profile-before-change":
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE",
"profile-before-change-qm":
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE_QM",
"xpcom-will-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN",
"xpcom-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_SHUTDOWN",
};
nsTerminatorTelemetry.prototype = {

Просмотреть файл

@ -15,6 +15,7 @@
* process as fast as possible, without any cleanup.
*/
#include "mozilla/ShutdownPhase.h"
#include "nsTerminator.h"
#include "prthread.h"
@ -39,6 +40,7 @@
# include <unistd.h>
#endif
#include "mozilla/AppShutdown.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
@ -64,6 +66,8 @@
// forcefully.
#define ADDITIONAL_WAIT_BEFORE_CRASH_MS 3000
#define HEARTBEAT_INTERVAL_MS 100
namespace mozilla {
namespace {
@ -76,25 +80,34 @@ namespace {
* ticks between the time we receive a notification and the next one.
*/
struct ShutdownStep {
char const* const mTopic;
mozilla::ShutdownPhase mPhase;
int mTicks;
constexpr explicit ShutdownStep(const char* const topic)
: mTopic(topic), mTicks(-1) {}
constexpr explicit ShutdownStep(mozilla::ShutdownPhase aPhase)
: mPhase(aPhase), mTicks(-1) {}
};
static ShutdownStep sShutdownSteps[] = {
ShutdownStep("quit-application"),
ShutdownStep("profile-change-net-teardown"),
ShutdownStep("profile-change-teardown"),
ShutdownStep("profile-before-change"),
ShutdownStep("profile-before-change-qm"),
ShutdownStep("xpcom-will-shutdown"),
ShutdownStep("xpcom-shutdown"),
ShutdownStep(mozilla::ShutdownPhase::AppShutdownConfirmed),
ShutdownStep(mozilla::ShutdownPhase::AppShutdownNetTeardown),
ShutdownStep(mozilla::ShutdownPhase::AppShutdownTeardown),
ShutdownStep(mozilla::ShutdownPhase::AppShutdown),
ShutdownStep(mozilla::ShutdownPhase::AppShutdownQM),
ShutdownStep(mozilla::ShutdownPhase::XPCOMWillShutdown),
ShutdownStep(mozilla::ShutdownPhase::XPCOMShutdown),
};
Atomic<bool> sShutdownNotified;
int GetStepForPhase(mozilla::ShutdownPhase aPhase) {
for (size_t i = 0; i < std::size(sShutdownSteps); i++) {
if (sShutdownSteps[i].mPhase >= aPhase) {
return (int)i;
}
}
return -1;
}
// Utility function: create a thread that is non-joinable,
// does not prevent the process from terminating, is never
// cooperatively scheduled, and uses a default stack size.
@ -172,9 +185,9 @@ void RunWatchdog(void* arg) {
// more reasonable.
//
#if defined(XP_WIN)
Sleep(1000 /* ms */);
Sleep(HEARTBEAT_INTERVAL_MS /* ms */);
#else
usleep(1000000 /* usec */);
usleep(HEARTBEAT_INTERVAL_MS * 1000 /* usec */);
#endif
if (gHeartbeat++ < timeToLive) {
@ -185,24 +198,24 @@ void RunWatchdog(void* arg) {
// The shutdown steps are not completed yet. Let's report the last one.
if (!sShutdownNotified) {
const char* lastStep = nullptr;
mozilla::ShutdownPhase lastStep = mozilla::ShutdownPhase::NotInShutdown;
// Looping inverse here to make the search more robust in case
// the observer that triggers UpdateHeartbeat was not called
// at all or in the expected order on some step. This should
// give us always the last known ShutdownStep.
for (int i = ArrayLength(sShutdownSteps) - 1; i >= 0; --i) {
if (sShutdownSteps[i].mTicks > -1) {
lastStep = sShutdownSteps[i].mTopic;
lastStep = sShutdownSteps[i].mPhase;
break;
}
}
if (lastStep) {
if (lastStep != mozilla::ShutdownPhase::NotInShutdown) {
nsCString msg;
msg.AppendPrintf(
"Shutdown hanging at step %s. "
"Something is blocking the main-thread.",
lastStep);
mozilla::AppShutdown::GetObserverKey(lastStep));
// This string will be leaked.
MOZ_CRASH_UNSAFE(strdup(msg.BeginReading()));
}
@ -349,6 +362,7 @@ void RunWriter(void* arg) {
// will be written correctly, but, again, we don't care enough
// about the data to make more efforts.
//
Unused << PR_Delete(destinationPath.get());
if (PR_Rename(tmpFilePath.get(), destinationPath.get()) != PR_SUCCESS) {
break;
}
@ -361,25 +375,11 @@ NS_IMPL_ISUPPORTS(nsTerminator, nsIObserver)
nsTerminator::nsTerminator() : mInitialized(false), mCurrentStep(-1) {}
// During startup, register as an observer for all interesting topics.
nsresult nsTerminator::SelfInit() {
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (!os) {
return NS_ERROR_UNEXPECTED;
}
for (auto& shutdownStep : sShutdownSteps) {
DebugOnly<nsresult> rv = os->AddObserver(this, shutdownStep.mTopic, false);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "AddObserver failed");
}
return NS_OK;
}
// Actually launch these threads. This takes place at the first sign of
// shutdown.
void nsTerminator::Start() {
MOZ_ASSERT(!mInitialized);
sShutdownNotified = false;
StartWatchdog();
#if !defined(NS_FREE_PERMANENT_DATA)
// Only allow nsTerminator to write on non-leak-checked builds so we don't
@ -388,7 +388,33 @@ void nsTerminator::Start() {
StartWriter();
#endif // !defined(NS_FREE_PERMANENT_DATA)
mInitialized = true;
sShutdownNotified = false;
}
NS_IMETHODIMP
nsTerminator::Observe(nsISupports*, const char* aTopic, const char16_t*) {
// This Observe is now only used for testing purposes.
// XXX: Check if we should change our testing strategy.
if (strcmp(aTopic, "terminator-test-quit-application") == 0) {
AdvancePhase(mozilla::ShutdownPhase::AppShutdownConfirmed);
} else if (strcmp(aTopic, "terminator-test-profile-change-net-teardown") ==
0) {
AdvancePhase(mozilla::ShutdownPhase::AppShutdownNetTeardown);
} else if (strcmp(aTopic, "terminator-test-profile-change-teardown") == 0) {
AdvancePhase(mozilla::ShutdownPhase::AppShutdownTeardown);
} else if (strcmp(aTopic, "terminator-test-profile-before-change") == 0) {
AdvancePhase(mozilla::ShutdownPhase::AppShutdown);
} else if (strcmp(aTopic, "terminator-test-profile-before-change-qm") == 0) {
AdvancePhase(mozilla::ShutdownPhase::AppShutdownQM);
} else if (strcmp(aTopic,
"terminator-test-profile-before-change-telemetry") == 0) {
AdvancePhase(mozilla::ShutdownPhase::AppShutdownTelemetry);
} else if (strcmp(aTopic, "terminator-test-xpcom-will-shutdown") == 0) {
AdvancePhase(mozilla::ShutdownPhase::XPCOMWillShutdown);
} else if (strcmp(aTopic, "terminator-test-xpcom-shutdown") == 0) {
AdvancePhase(mozilla::ShutdownPhase::XPCOMShutdown);
}
return NS_OK;
}
// Prepare, allocate and start the watchdog thread.
@ -431,11 +457,12 @@ void nsTerminator::StartWatchdog() {
#endif
UniquePtr<Options> options(new Options());
const PRIntervalTime ticksDuration = PR_MillisecondsToInterval(1000);
const PRIntervalTime ticksDuration =
PR_MillisecondsToInterval(HEARTBEAT_INTERVAL_MS);
options->crashAfterTicks = crashAfterMS / ticksDuration;
// Handle systems where ticksDuration is greater than crashAfterMS.
if (options->crashAfterTicks == 0) {
options->crashAfterTicks = crashAfterMS / 1000;
options->crashAfterTicks = crashAfterMS / HEARTBEAT_INTERVAL_MS;
}
DebugOnly<PRThread*> watchdogThread =
@ -478,14 +505,11 @@ void nsTerminator::StartWriter() {
}
}
NS_IMETHODIMP
nsTerminator::Observe(nsISupports*, const char* aTopic, const char16_t*) {
if (strcmp(aTopic, "profile-after-change") == 0) {
return SelfInit();
void nsTerminator::AdvancePhase(mozilla::ShutdownPhase aPhase) {
// If we are done, do nothing
if (sShutdownNotified) {
return;
}
// Other notifications are shutdown-related.
// As we have seen examples in the wild of shutdown notifications
// not being sent (or not being sent in the expected order), we do
// not assume a specific order.
@ -493,41 +517,29 @@ nsTerminator::Observe(nsISupports*, const char* aTopic, const char16_t*) {
Start();
}
UpdateHeartbeat(aTopic);
UpdateHeartbeat(GetStepForPhase(aPhase));
#if !defined(NS_FREE_PERMANENT_DATA)
// Only allow nsTerminator to write on non-leak checked builds so we don't get
// leak warnings on shutdown for intentional leaks (see bug 1242084). This
// will be enabled again by bug 1255484 when 1255478 lands.
UpdateTelemetry();
#endif // !defined(NS_FREE_PERMANENT_DATA)
UpdateCrashReport(aTopic);
// Perform a little cleanup
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
MOZ_RELEASE_ASSERT(os);
(void)os->RemoveObserver(this, aTopic);
return NS_OK;
UpdateCrashReport(mozilla::AppShutdown::GetObserverKey(aPhase));
}
void nsTerminator::UpdateHeartbeat(const char* aTopic) {
// Reset the clock, find out how long the current phase has lasted.
uint32_t ticks = gHeartbeat.exchange(0);
if (mCurrentStep >= 0) {
sShutdownSteps[mCurrentStep].mTicks = ticks;
}
void nsTerminator::UpdateHeartbeat(int32_t aStep) {
MOZ_ASSERT(aStep >= mCurrentStep);
// Find out where we now are in the current shutdown.
// Don't assume that shutdown takes place in the expected order.
int nextStep = -1;
for (size_t i = 0; i < ArrayLength(sShutdownSteps); ++i) {
if (strcmp(sShutdownSteps[i].mTopic, aTopic) == 0) {
nextStep = i;
break;
if (aStep > mCurrentStep) {
// Reset the clock, find out how long the current phase has lasted.
uint32_t ticks = gHeartbeat.exchange(0);
if (mCurrentStep >= 0) {
sShutdownSteps[mCurrentStep].mTicks = ticks;
}
sShutdownSteps[aStep].mTicks = 0;
mCurrentStep = aStep;
}
MOZ_ASSERT(nextStep != -1);
mCurrentStep = nextStep;
}
void nsTerminator::UpdateTelemetry() {
@ -556,7 +568,8 @@ void nsTerminator::UpdateTelemetry() {
telemetryData->AppendLiteral(", ");
}
telemetryData->AppendLiteral(R"(")");
telemetryData->Append(shutdownStep.mTopic);
telemetryData->Append(
mozilla::AppShutdown::GetObserverKey(shutdownStep.mPhase));
telemetryData->AppendLiteral(R"(": )");
telemetryData->AppendInt(shutdownStep.mTicks);
}

Просмотреть файл

@ -18,21 +18,21 @@ class nsTerminator final : public nsIObserver {
NS_DECL_NSIOBSERVER
nsTerminator();
void AdvancePhase(mozilla::ShutdownPhase aPhase);
private:
nsresult SelfInit();
void Start();
void StartWatchdog();
void StartWriter();
void UpdateHeartbeat(const char* aTopic);
void UpdateHeartbeat(int aStep);
void UpdateTelemetry();
void UpdateCrashReport(const char* aTopic);
~nsTerminator() = default;
bool mInitialized;
int32_t mCurrentStep;
int mCurrentStep;
};
// This is called by XPCOMInit when the shutdown is completed.

Просмотреть файл

@ -16,6 +16,17 @@ var PATH;
var PATH_TMP;
var terminator;
let KEYS = [
"quit-application",
"profile-change-net-teardown",
"profile-change-teardown",
"profile-before-change",
"profile-before-change-qm",
"profile-before-change-telemetry",
"xpcom-will-shutdown",
"xpcom-shutdown",
];
add_task(async function init() {
do_get_profile();
PATH = Path.join(Constants.Path.localProfileDir, "ShutdownDuration.json");
@ -28,7 +39,6 @@ add_task(async function init() {
terminator = Cc["@mozilla.org/toolkit/shutdown-terminator;1"].createInstance(
Ci.nsIObserver
);
terminator.observe(null, "profile-after-change", null);
});
var promiseShutdownDurationData = async function() {
@ -51,23 +61,22 @@ var promiseShutdownDurationData = async function() {
};
add_task(async function test_record() {
let PHASE0 = "profile-change-teardown";
let PHASE1 = "profile-before-change";
let PHASE2 = "xpcom-will-shutdown";
let t0 = Date.now();
info("Starting shutdown");
terminator.observe(null, "profile-change-teardown", null);
terminator.observe(null, "terminator-test-" + KEYS[2], null);
await new Promise(resolve => setTimeout(resolve, 200));
info("Moving to next phase");
terminator.observe(null, PHASE1, null);
terminator.observe(null, "terminator-test-" + KEYS[3], null);
await new Promise(resolve => setTimeout(resolve, 100));
let data = await promiseShutdownDurationData();
let t1 = Date.now();
Assert.ok(PHASE0 in data, "The file contains the expected key");
let duration = data[PHASE0];
Assert.ok(KEYS[2] in data, "The file contains the expected key");
let duration = data[KEYS[2]];
Assert.equal(typeof duration, "number");
Assert.ok(duration >= 0, "Duration is a non-negative number");
Assert.ok(
@ -77,7 +86,7 @@ add_task(async function test_record() {
Assert.equal(
Object.keys(data).length,
1,
2,
"Data does not contain other durations"
);
@ -89,7 +98,7 @@ add_task(async function test_record() {
let WAIT_MS = 2000;
await new Promise(resolve => setTimeout(resolve, WAIT_MS));
terminator.observe(null, PHASE2, null);
terminator.observe(null, "terminator-test-" + KEYS[4], null);
data = await promiseShutdownDurationData();
let t2 = Date.now();
@ -98,18 +107,18 @@ add_task(async function test_record() {
Object.keys(data)
.sort()
.join(", "),
[PHASE0, PHASE1].sort().join(", "),
[KEYS[2], KEYS[3], KEYS[4]].sort().join(", "),
"The file contains the expected keys"
);
Assert.equal(data[PHASE0], duration, "Duration of phase 0 hasn't changed");
let duration2 = data[PHASE1];
Assert.equal(data[KEYS[2]], duration, "Duration of phase 0 hasn't changed");
let duration2 = data[KEYS[3]];
Assert.equal(typeof duration2, "number");
Assert.ok(
duration2 >= WAIT_MS / 2000,
"We have waited at least " + WAIT_MS / 2000 + " ticks"
duration2 >= WAIT_MS / 100,
"We have waited at least " + WAIT_MS / 100 + " ticks"
);
Assert.ok(
duration2 <= Math.ceil((t2 - t1) / 1000) + 1,
duration2 <= Math.ceil((t2 - t1) / 100) + 1,
"Duration is reasonable"
);
});

Просмотреть файл

@ -15,11 +15,16 @@ var PATH;
var HISTOGRAMS = {
"quit-application": "SHUTDOWN_PHASE_DURATION_TICKS_QUIT_APPLICATION",
"profile-change-net-teardown":
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_NET_TEARDOWN",
"profile-change-teardown":
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_CHANGE_TEARDOWN",
"profile-before-change":
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE",
"profile-before-change-qm":
"SHUTDOWN_PHASE_DURATION_TICKS_PROFILE_BEFORE_CHANGE_QM",
"xpcom-will-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_WILL_SHUTDOWN",
"xpcom-shutdown": "SHUTDOWN_PHASE_DURATION_TICKS_XPCOM_SHUTDOWN",
};
add_task(async function init() {

Просмотреть файл

@ -11,7 +11,7 @@ function setup_crash() {
);
Services.prefs.setBoolPref("toolkit.terminator.testing", true);
Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", 10);
Services.prefs.setIntPref("toolkit.asyncshutdown.crash_timeout", 150);
// Initialize the terminator
// (normally, this is done through the manifest file, but xpcshell
@ -19,12 +19,12 @@ function setup_crash() {
let terminator = Cc[
"@mozilla.org/toolkit/shutdown-terminator;1"
].createInstance(Ci.nsIObserver);
terminator.observe(null, "profile-after-change", null);
terminator.observe(null, "terminator-test-profile-after-change", null);
// Inform the terminator that shutdown has started
// Pick an arbitrary notification
terminator.observe(null, "xpcom-will-shutdown", null);
terminator.observe(null, "profile-before-change", null);
terminator.observe(null, "terminator-test-profile-before-change", null);
terminator.observe(null, "terminator-test-xpcom-will-shutdown", null);
dump("Waiting (actively) for the crash\n");
Services.tm.spinEventLoopUntil(() => false);
@ -32,7 +32,7 @@ function setup_crash() {
function after_crash(mdump, extra) {
info("Crash signature: " + JSON.stringify(extra, null, "\t"));
Assert.equal(extra.ShutdownProgress, "profile-before-change");
Assert.equal(extra.ShutdownProgress, "xpcom-will-shutdown");
}
add_task(async function run_test() {

Просмотреть файл

@ -44,15 +44,6 @@ observer's Observe() method.
profile-do-change
profile-after-change
"shutdown-persist"
The user is logging out and whatever data the observer stores
for the current profile should be released from memory and
saved to disk.
The following topics happen in this context:
profile-change-net-teardown
profile-change-teardown
profile-before-change
See https://wiki.mozilla.org/XPCOM_Shutdown for more details about the shutdown
process.

Просмотреть файл

@ -37,6 +37,7 @@
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/AutoRestore.h"
#ifdef MOZ_BACKGROUNDTASKS
# include "mozilla/BackgroundTasks.h"
@ -1056,30 +1057,24 @@ void nsXREDirProvider::DoShutdown() {
AUTO_PROFILER_LABEL("nsXREDirProvider::DoShutdown", OTHER);
if (mProfileNotified) {
nsCOMPtr<nsIObserverService> obsSvc =
mozilla::services::GetObserverService();
NS_ASSERTION(obsSvc, "No observer service?");
if (obsSvc) {
static const char16_t kShutdownPersist[] = u"shutdown-persist";
obsSvc->NotifyObservers(nullptr, "profile-change-net-teardown",
kShutdownPersist);
obsSvc->NotifyObservers(nullptr, "profile-change-teardown",
kShutdownPersist);
mozilla::AppShutdown::AdvanceShutdownPhase(
mozilla::ShutdownPhase::AppShutdownNetTeardown, nullptr);
mozilla::AppShutdown::AdvanceShutdownPhase(
mozilla::ShutdownPhase::AppShutdownTeardown, nullptr);
#ifdef DEBUG
// Not having this causes large intermittent leaks. See bug 1340425.
if (JSContext* cx = mozilla::dom::danger::GetJSContext()) {
JS_GC(cx);
}
// Not having this causes large intermittent leaks. See bug 1340425.
if (JSContext* cx = mozilla::dom::danger::GetJSContext()) {
JS_GC(cx);
}
#endif
obsSvc->NotifyObservers(nullptr, "profile-before-change",
kShutdownPersist);
obsSvc->NotifyObservers(nullptr, "profile-before-change-qm",
kShutdownPersist);
obsSvc->NotifyObservers(nullptr, "profile-before-change-telemetry",
kShutdownPersist);
}
mozilla::AppShutdown::AdvanceShutdownPhase(
mozilla::ShutdownPhase::AppShutdown, nullptr);
mozilla::AppShutdown::AdvanceShutdownPhase(
mozilla::ShutdownPhase::AppShutdownQM, nullptr);
mozilla::AppShutdown::AdvanceShutdownPhase(
mozilla::ShutdownPhase::AppShutdownTelemetry, nullptr);
mProfileNotified = false;
}

Просмотреть файл

@ -400,7 +400,7 @@ void ProfilerParentTracker::EnsureInstance() {
// The tracker should get destroyed before threads are shutdown, because its
// destruction closes extant channels, which could trigger promise rejections
// that need to be dispatched to other threads.
ClearOnShutdown(&sInstance, ShutdownPhase::ShutdownThreads);
ClearOnShutdown(&sInstance, ShutdownPhase::XPCOMShutdownThreads);
}
/* static */

Просмотреть файл

@ -2137,7 +2137,7 @@ const WinUtils::WhitelistVec& WinUtils::GetWhitelistedPaths() {
static WhitelistVec sWhitelist([]() -> WhitelistVec {
auto setClearFn = [ptr = &sWhitelist]() -> void {
RunOnShutdown([ptr]() -> void { ptr->clear(); },
ShutdownPhase::ShutdownFinal);
ShutdownPhase::XPCOMShutdownFinal);
};
if (NS_IsMainThread()) {

Просмотреть файл

@ -5197,22 +5197,30 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
// Windows won't let us do that. Bug 212316.
nsCOMPtr<nsIObserverService> obsServ =
mozilla::services::GetObserverService();
const char16_t* context = u"shutdown-persist";
const char16_t* syncShutdown = u"syncShutdown";
const char16_t* quitType = GetQuitType();
AppShutdown::Init(AppShutdownMode::Normal, 0);
obsServ->NotifyObservers(nullptr, "quit-application-granted",
syncShutdown);
obsServ->NotifyObservers(nullptr, "quit-application-forced", nullptr);
obsServ->NotifyObservers(nullptr, "quit-application", quitType);
obsServ->NotifyObservers(nullptr, "profile-change-net-teardown",
context);
obsServ->NotifyObservers(nullptr, "profile-change-teardown", context);
obsServ->NotifyObservers(nullptr, "profile-before-change", context);
obsServ->NotifyObservers(nullptr, "profile-before-change-qm", context);
obsServ->NotifyObservers(nullptr, "profile-before-change-telemetry",
context);
mozilla::AppShutdown::DoImmediateExit();
AppShutdown::OnShutdownConfirmed();
AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownConfirmed,
quitType);
AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownNetTeardown,
nullptr);
AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTeardown,
nullptr);
AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdown, nullptr);
AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownQM,
nullptr);
AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTelemetry,
nullptr);
AppShutdown::DoImmediateExit();
}
sCanQuit = TRI_UNKNOWN;
result = true;

Просмотреть файл

@ -4,8 +4,6 @@
* 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
@ -13,6 +11,7 @@
#endif
#include "GeckoProfiler.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/Printf.h"
@ -21,11 +20,19 @@
#include "mozilla/StartupTimeline.h"
#include "mozilla/StaticPrefs_toolkit.h"
#include "mozilla/LateWriteChecks.h"
#include "mozilla/Services.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsAppRunner.h"
#include "nsDirectoryServiceUtils.h"
#include "nsICertStorage.h"
#include "nsThreadUtils.h"
#include "AppShutdown.h"
// TODO: understand why on Android we cannot include this and if we should
#ifndef ANDROID
# include "nsTerminator.h"
#endif
#include "prenv.h"
#ifdef MOZ_NEW_XULSTORE
@ -34,6 +41,29 @@
namespace mozilla {
const char* sPhaseObserverKeys[] = {
nullptr, // NotInShutdown
"quit-application", // AppShutdownConfirmed
"profile-change-net-teardown", // AppShutdownNetTeardown
"profile-change-teardown", // AppShutdownTeardown
"profile-before-change", // AppShutdown
"profile-before-change-qm", // AppShutdownQM
"profile-before-change-telemetry", // AppShutdownTelemetry
"xpcom-will-shutdown", // XPCOMWillShutdown
"xpcom-shutdown", // XPCOMShutdown
"xpcom-shutdown-threads", // XPCOMShutdownThreads
nullptr, // XPCOMShutdownLoaders
nullptr, // XPCOMShutdownFinal
nullptr // CCPostLastCycleCollection
};
static_assert(sizeof(sPhaseObserverKeys) / sizeof(sPhaseObserverKeys[0]) ==
(size_t)ShutdownPhase::ShutdownPhase_Length);
#ifndef ANDROID
static nsTerminator* sTerminator = nullptr;
#endif
static ShutdownPhase sFastShutdownPhase = ShutdownPhase::NotInShutdown;
static ShutdownPhase sLateWriteChecksPhase = ShutdownPhase::NotInShutdown;
static AppShutdownMode sShutdownMode = AppShutdownMode::Normal;
@ -54,11 +84,11 @@ static char* sSavedProfLDEnvVar = nullptr;
ShutdownPhase GetShutdownPhaseFromPrefValue(int32_t aPrefValue) {
switch (aPrefValue) {
case 1:
return ShutdownPhase::ShutdownPostLastCycleCollection;
return ShutdownPhase::CCPostLastCycleCollection;
case 2:
return ShutdownPhase::ShutdownThreads;
return ShutdownPhase::XPCOMShutdownThreads;
case 3:
return ShutdownPhase::Shutdown;
return ShutdownPhase::XPCOMShutdown;
// 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.
@ -78,6 +108,11 @@ void AppShutdown::SaveEnvVarsForPotentialRestart() {
}
}
const char* AppShutdown::GetObserverKey(ShutdownPhase aPhase) {
return sPhaseObserverKeys[static_cast<std::underlying_type_t<ShutdownPhase>>(
aPhase)];
}
void AppShutdown::MaybeDoRestart() {
if (sShutdownMode == AppShutdownMode::Restart) {
StopLateWriteChecks();
@ -135,6 +170,10 @@ void AppShutdown::Init(AppShutdownMode aMode, int aExitCode) {
sExitCode = aExitCode;
#ifndef ANDROID
sTerminator = new nsTerminator();
#endif
// 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.
@ -243,4 +282,28 @@ bool AppShutdown::IsRestarting() {
return sShutdownMode == AppShutdownMode::Restart;
}
void AppShutdown::AdvanceShutdownPhase(
ShutdownPhase aPhase, const char16_t* aNotificationData,
nsCOMPtr<nsISupports> aNotificationSubject) {
#ifndef ANDROID
if (sTerminator) {
sTerminator->AdvancePhase(aPhase);
}
#endif
mozilla::KillClearOnShutdown(aPhase);
MaybeFastShutdown(aPhase);
const char* aTopic = AppShutdown::GetObserverKey(aPhase);
if (aTopic) {
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
if (obsService) {
obsService->NotifyObservers(aNotificationSubject, aTopic,
aNotificationData);
}
}
}
} // namespace mozilla

Просмотреть файл

@ -7,6 +7,7 @@
#ifndef AppShutdown_h
#define AppShutdown_h
#include <type_traits>
#include "ShutdownPhase.h"
namespace mozilla {
@ -19,7 +20,6 @@ enum class AppShutdownMode {
class AppShutdown {
public:
static bool IsShuttingDown();
/**
* Returns the current exit code that the process will be terminated with.
*/
@ -47,12 +47,6 @@ class AppShutdown {
*/
static void MaybeDoRestart();
/**
* This will perform a fast shutdown via _exit(0) or similar if the user's
* prefs are configured to do so at this phase.
*/
static void MaybeFastShutdown(ShutdownPhase aPhase);
/**
* The _exit() call is not a safe way to terminate your own process on
* Windows, because _exit runs DLL detach callbacks which run static
@ -69,6 +63,25 @@ class AppShutdown {
* restart.
*/
static bool IsRestarting();
/**
* Wrapper for shutdown notifications that inform the terminator before
* we notify other observers. Calls MaybeFastShutdown.
*/
static void AdvanceShutdownPhase(
ShutdownPhase aPhase, const char16_t* aNotificationData = nullptr,
nsCOMPtr<nsISupports> aNotificationSubject = nullptr);
/**
* This will perform a fast shutdown via _exit(0) or similar if the user's
* prefs are configured to do so at this phase.
*/
static void MaybeFastShutdown(ShutdownPhase aPhase);
/**
* Map shutdown phases to observer keys
*/
static const char* GetObserverKey(ShutdownPhase aPhase);
};
} // namespace mozilla

Просмотреть файл

@ -19,7 +19,8 @@
* This header exports two public methods in the mozilla namespace:
*
* template<class SmartPtr>
* void ClearOnShutdown(SmartPtr *aPtr, aPhase=ShutdownPhase::ShutdownFinal)
* void ClearOnShutdown(SmartPtr *aPtr,
* aPhase=ShutdownPhase::XPCOMShutdownFinal)
*
* This function takes a pointer to a smart pointer and nulls the smart pointer
* on shutdown (and a particular phase of shutdown as needed). If a phase
@ -40,7 +41,7 @@
*
* template <typename CallableT>
* void RunOnShutdown(CallableT&& aCallable,
* aPhase = ShutdownPhase::ShutdownFinal)
* aPhase = ShutdownPhase::XPCOMShutdownFinal)
*
* This function takes a callable and executes it upon shutdown at the start of
* the specified phase. If the phase has already occurred when RunOnShutdown()
@ -108,7 +109,7 @@ extern ShutdownPhase sCurrentShutdownPhase;
template <class SmartPtr>
inline void ClearOnShutdown(
SmartPtr* aPtr, ShutdownPhase aPhase = ShutdownPhase::ShutdownFinal) {
SmartPtr* aPtr, ShutdownPhase aPhase = ShutdownPhase::XPCOMShutdownFinal) {
using namespace ClearOnShutdown_Internal;
MOZ_ASSERT(NS_IsMainThread());
@ -118,8 +119,9 @@ inline void ClearOnShutdown(
}
template <typename CallableT>
inline void RunOnShutdown(CallableT&& aCallable,
ShutdownPhase aPhase = ShutdownPhase::ShutdownFinal) {
inline void RunOnShutdown(
CallableT&& aCallable,
ShutdownPhase aPhase = ShutdownPhase::XPCOMShutdownFinal) {
using namespace ClearOnShutdown_Internal;
MOZ_ASSERT(NS_IsMainThread());

Просмотреть файл

@ -12,15 +12,21 @@ namespace mozilla {
// Must be contiguous starting at 0
enum class ShutdownPhase {
NotInShutdown = 0,
WillShutdown,
Shutdown,
ShutdownThreads,
ShutdownLoaders,
ShutdownFinal,
ShutdownPostLastCycleCollection,
ShutdownPhase_Length, // never pass this value
First = WillShutdown, // for iteration
Last = ShutdownFinal
AppShutdownConfirmed,
AppShutdownNetTeardown,
AppShutdownTeardown,
AppShutdown,
AppShutdownQM,
AppShutdownTelemetry,
XPCOMWillShutdown,
XPCOMShutdown,
XPCOMShutdownThreads,
XPCOMShutdownLoaders,
XPCOMShutdownFinal,
CCPostLastCycleCollection,
ShutdownPhase_Length, // never pass this value
First = AppShutdownConfirmed, // for iteration
Last = XPCOMShutdownFinal
};
} // namespace mozilla

Просмотреть файл

@ -598,45 +598,29 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
return NS_ERROR_UNEXPECTED;
}
RefPtr<nsObserverService> observerService;
CallGetService("@mozilla.org/observer-service;1",
(nsObserverService**)getter_AddRefs(observerService));
mozilla::AppShutdown::AdvanceShutdownPhase(
mozilla::ShutdownPhase::XPCOMWillShutdown);
if (observerService) {
mozilla::KillClearOnShutdown(ShutdownPhase::WillShutdown);
mozilla::AppShutdown::MaybeFastShutdown(
mozilla::ShutdownPhase::WillShutdown);
observerService->NotifyObservers(
nullptr, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, nullptr);
nsCOMPtr<nsIServiceManager> mgr;
rv = NS_GetServiceManager(getter_AddRefs(mgr));
if (NS_SUCCEEDED(rv)) {
mozilla::KillClearOnShutdown(ShutdownPhase::Shutdown);
mozilla::AppShutdown::MaybeFastShutdown(
mozilla::ShutdownPhase::Shutdown);
observerService->NotifyObservers(mgr, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
nullptr);
}
nsCOMPtr<nsIServiceManager> mgr;
rv = NS_GetServiceManager(getter_AddRefs(mgr));
if (NS_SUCCEEDED(rv)) {
// We want the service manager to be the subject of notifications
mozilla::AppShutdown::AdvanceShutdownPhase(
mozilla::ShutdownPhase::XPCOMShutdown, nullptr,
do_QueryInterface(mgr));
}
#ifndef ANDROID
mozilla::XPCOMShutdownNotified();
mozilla::XPCOMShutdownNotified();
#endif
}
// This must happen after the shutdown of media and widgets, which
// are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification.
NS_ProcessPendingEvents(thread);
gfxPlatform::ShutdownLayersIPC();
if (observerService) {
mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownThreads);
mozilla::AppShutdown::MaybeFastShutdown(
mozilla::ShutdownPhase::ShutdownThreads);
observerService->NotifyObservers(
nullptr, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, nullptr);
}
mozilla::AppShutdown::AdvanceShutdownPhase(
mozilla::ShutdownPhase::XPCOMShutdownThreads);
gXPCOMThreadsShutDown = true;
NS_ProcessPendingEvents(thread);
@ -646,8 +630,13 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
NS_ProcessPendingEvents(thread);
mozilla::KillClearOnShutdown(ShutdownPhase::XPCOMShutdownLoaders);
// XXX: Why don't we try a MaybeFastShutdown for XPCOMShutdownLoaders ?
RefPtr<nsObserverService> observerService;
CallGetService("@mozilla.org/observer-service;1",
(nsObserverService**)getter_AddRefs(observerService));
if (observerService) {
mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownLoaders);
observerService->Shutdown();
}
@ -655,7 +644,7 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
// we've finished notifying observers of XPCOM shutdown, because shutdown
// observers themselves might call ClearOnShutdown().
// Some destructors may fire extra runnables that will be processed below.
mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownFinal);
mozilla::KillClearOnShutdown(ShutdownPhase::XPCOMShutdownFinal);
// Shutdown all remaining threads. This method does not return until
// all threads created using the thread manager (with the exception of
@ -675,7 +664,7 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
AbstractThread::ShutdownMainThread();
mozilla::AppShutdown::MaybeFastShutdown(
mozilla::ShutdownPhase::ShutdownFinal);
mozilla::ShutdownPhase::XPCOMShutdownFinal);
// XPCOM is officially in shutdown mode NOW
// Set this only after the observers have been notified as this
@ -718,9 +707,9 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
// There can be code trying to refer to global objects during the final cc
// shutdown. This is the phase for such global objects to correctly release.
mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownPostLastCycleCollection);
mozilla::KillClearOnShutdown(ShutdownPhase::CCPostLastCycleCollection);
mozilla::AppShutdown::MaybeFastShutdown(
mozilla::ShutdownPhase::ShutdownPostLastCycleCollection);
mozilla::ShutdownPhase::CCPostLastCycleCollection);
mozilla::scache::StartupCache::DeleteSingleton();