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/. */
7 #include "AvailableMemoryWatcher.h"
9 #include "mozilla/ClearOnShutdown.h"
10 #include "mozilla/dom/Promise.h"
11 #include "mozilla/ErrorResult.h"
12 #include "mozilla/RefPtr.h"
13 #include "mozilla/Services.h"
14 #include "mozilla/StaticPtr.h"
15 #include "mozilla/Telemetry.h"
16 #include "nsExceptionHandler.h"
17 #include "nsMemoryPressure.h"
18 #include "nsXULAppAPI.h"
22 // Use this class as the initial value of
23 // nsAvailableMemoryWatcherBase::mCallback until RegisterCallback() is called
24 // so that nsAvailableMemoryWatcherBase does not have to check if its callback
25 // object is valid or not.
26 class NullTabUnloader final
: public nsITabUnloader
{
27 ~NullTabUnloader() = default;
30 NullTabUnloader() = default;
33 NS_DECL_NSITABUNLOADER
36 NS_IMPL_ISUPPORTS(NullTabUnloader
, nsITabUnloader
)
38 NS_IMETHODIMP
NullTabUnloader::UnloadTabAsync() {
39 return NS_ERROR_NOT_IMPLEMENTED
;
42 StaticRefPtr
<nsAvailableMemoryWatcherBase
>
43 nsAvailableMemoryWatcherBase::sSingleton
;
46 already_AddRefed
<nsAvailableMemoryWatcherBase
>
47 nsAvailableMemoryWatcherBase::GetSingleton() {
49 sSingleton
= CreateAvailableMemoryWatcher();
50 ClearOnShutdown(&sSingleton
);
53 return do_AddRef(sSingleton
);
56 NS_IMPL_ISUPPORTS(nsAvailableMemoryWatcherBase
, nsIAvailableMemoryWatcherBase
);
58 nsAvailableMemoryWatcherBase::nsAvailableMemoryWatcherBase()
59 : mNumOfTabUnloading(0),
60 mNumOfMemoryPressure(0),
61 mTabUnloader(new NullTabUnloader
),
63 MOZ_ASSERT(XRE_IsParentProcess(),
64 "Watching memory only in the main process.");
67 const char* const nsAvailableMemoryWatcherBase::kObserverTopics
[] = {
68 // Use this shutdown phase to make sure the instance is destroyed in GTest
70 "user-interaction-active",
71 "user-interaction-inactive",
74 nsresult
nsAvailableMemoryWatcherBase::Init() {
75 MOZ_ASSERT(NS_IsMainThread(),
76 "nsAvailableMemoryWatcherBase needs to be initialized "
77 "in the main thread.");
80 return NS_ERROR_ALREADY_INITIALIZED
;
83 mObserverSvc
= services::GetObserverService();
84 MOZ_ASSERT(mObserverSvc
);
86 for (auto topic
: kObserverTopics
) {
87 nsresult rv
= mObserverSvc
->AddObserver(this, topic
,
88 /* ownsWeak */ false);
89 NS_ENSURE_SUCCESS(rv
, rv
);
94 void nsAvailableMemoryWatcherBase::Shutdown() {
95 for (auto topic
: kObserverTopics
) {
96 mObserverSvc
->RemoveObserver(this, topic
);
101 nsAvailableMemoryWatcherBase::Observe(nsISupports
* aSubject
, const char* aTopic
,
102 const char16_t
* aData
) {
103 MOZ_ASSERT(NS_IsMainThread());
105 if (strcmp(aTopic
, "xpcom-shutdown") == 0) {
107 } else if (strcmp(aTopic
, "user-interaction-inactive") == 0) {
108 mInteracting
= false;
109 #ifdef MOZ_CRASHREPORTER
110 CrashReporter::SetInactiveStateStart();
112 } else if (strcmp(aTopic
, "user-interaction-active") == 0) {
114 #ifdef MOZ_CRASHREPORTER
115 CrashReporter::ClearInactiveStateStart();
121 nsresult
nsAvailableMemoryWatcherBase::RegisterTabUnloader(
122 nsITabUnloader
* aTabUnloader
) {
123 mTabUnloader
= aTabUnloader
;
127 nsresult
nsAvailableMemoryWatcherBase::OnUnloadAttemptCompleted(
130 // A tab was unloaded successfully.
132 ++mNumOfTabUnloading
;
135 // There was no unloadable tab.
136 case NS_ERROR_NOT_AVAILABLE
:
137 ++mNumOfMemoryPressure
;
138 NS_NotifyOfEventualMemoryPressure(MemoryPressureState::LowMemory
);
141 // There was a pending task to unload a tab.
146 MOZ_ASSERT_UNREACHABLE("Unexpected aResult");
152 void nsAvailableMemoryWatcherBase::UpdateLowMemoryTimeStamp() {
153 if (mLowMemoryStart
.IsNull()) {
154 mLowMemoryStart
= TimeStamp::NowLoRes();
158 void nsAvailableMemoryWatcherBase::RecordTelemetryEventOnHighMemory() {
159 Telemetry::SetEventRecordingEnabled("memory_watcher"_ns
, true);
160 Telemetry::RecordEvent(
161 Telemetry::EventID::Memory_watcher_OnHighMemory_Stats
,
162 Some(nsPrintfCString(
163 "%u,%u,%f", mNumOfTabUnloading
, mNumOfMemoryPressure
,
164 (TimeStamp::NowLoRes() - mLowMemoryStart
).ToSeconds())),
166 mNumOfTabUnloading
= mNumOfMemoryPressure
= 0;
167 mLowMemoryStart
= TimeStamp();
170 // Define the fallback method for a platform for which a platform-specific
171 // CreateAvailableMemoryWatcher() is not defined.
172 #if defined(ANDROID) || \
173 !defined(XP_WIN) && !defined(XP_MACOSX) && !defined(XP_LINUX)
174 already_AddRefed
<nsAvailableMemoryWatcherBase
> CreateAvailableMemoryWatcher() {
175 RefPtr
instance(new nsAvailableMemoryWatcherBase
);
176 return do_AddRef(instance
);
180 } // namespace mozilla