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/glean/GleanMetrics.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 : mMutex("nsAvailableMemoryWatcher mutex"),
60 mNumOfTabUnloading(0),
61 mNumOfMemoryPressure(0),
62 mTabUnloader(new NullTabUnloader
),
64 MOZ_ASSERT(XRE_IsParentProcess(),
65 "Watching memory only in the main process.");
68 const char* const nsAvailableMemoryWatcherBase::kObserverTopics
[] = {
69 // Use this shutdown phase to make sure the instance is destroyed in GTest
71 "user-interaction-active",
72 "user-interaction-inactive",
75 nsresult
nsAvailableMemoryWatcherBase::Init() {
76 MOZ_ASSERT(NS_IsMainThread(),
77 "nsAvailableMemoryWatcherBase needs to be initialized "
78 "in the main thread.");
81 return NS_ERROR_ALREADY_INITIALIZED
;
84 mObserverSvc
= services::GetObserverService();
85 MOZ_ASSERT(mObserverSvc
);
87 for (auto topic
: kObserverTopics
) {
88 nsresult rv
= mObserverSvc
->AddObserver(this, topic
,
89 /* ownsWeak */ false);
90 NS_ENSURE_SUCCESS(rv
, rv
);
95 void nsAvailableMemoryWatcherBase::Shutdown() {
96 for (auto topic
: kObserverTopics
) {
97 mObserverSvc
->RemoveObserver(this, topic
);
102 nsAvailableMemoryWatcherBase::Observe(nsISupports
* aSubject
, const char* aTopic
,
103 const char16_t
* aData
) {
104 MOZ_ASSERT(NS_IsMainThread());
106 if (strcmp(aTopic
, "xpcom-shutdown") == 0) {
108 } else if (strcmp(aTopic
, "user-interaction-inactive") == 0) {
109 mInteracting
= false;
110 #ifdef MOZ_CRASHREPORTER
111 CrashReporter::SetInactiveStateStart();
113 } else if (strcmp(aTopic
, "user-interaction-active") == 0) {
115 #ifdef MOZ_CRASHREPORTER
116 CrashReporter::ClearInactiveStateStart();
122 nsresult
nsAvailableMemoryWatcherBase::RegisterTabUnloader(
123 nsITabUnloader
* aTabUnloader
) {
124 mTabUnloader
= aTabUnloader
;
128 // This method is called as a part of UnloadTabAsync(). Like Notify(), if we
129 // call this method synchronously without releasing the lock first we can lead
131 nsresult
nsAvailableMemoryWatcherBase::OnUnloadAttemptCompleted(
133 MutexAutoLock
lock(mMutex
);
135 // A tab was unloaded successfully.
137 ++mNumOfTabUnloading
;
140 // There was no unloadable tab.
141 case NS_ERROR_NOT_AVAILABLE
:
142 ++mNumOfMemoryPressure
;
143 NS_NotifyOfEventualMemoryPressure(MemoryPressureState::LowMemory
);
146 // There was a pending task to unload a tab.
151 MOZ_ASSERT_UNREACHABLE("Unexpected aResult");
157 void nsAvailableMemoryWatcherBase::UpdateLowMemoryTimeStamp() {
158 if (mLowMemoryStart
.IsNull()) {
159 mLowMemoryStart
= TimeStamp::NowLoRes();
163 void nsAvailableMemoryWatcherBase::RecordTelemetryEventOnHighMemory(
164 const MutexAutoLock
&) {
165 glean::memory_watcher::on_high_memory_stats
.Record(
166 Some(glean::memory_watcher::OnHighMemoryStatsExtra
{
167 Some(nsPrintfCString(
168 "%u,%u,%f", mNumOfTabUnloading
, mNumOfMemoryPressure
,
169 (TimeStamp::NowLoRes() - mLowMemoryStart
).ToSeconds())),
171 mNumOfTabUnloading
= mNumOfMemoryPressure
= 0;
172 mLowMemoryStart
= TimeStamp();
175 // Define the fallback method for a platform for which a platform-specific
176 // CreateAvailableMemoryWatcher() is not defined.
177 #if defined(ANDROID) || \
178 !defined(XP_WIN) && !defined(XP_DARWIN) && !defined(XP_LINUX)
179 already_AddRefed
<nsAvailableMemoryWatcherBase
> CreateAvailableMemoryWatcher() {
180 RefPtr
instance(new nsAvailableMemoryWatcherBase
);
181 return do_AddRef(instance
);
185 } // namespace mozilla