Bug 1910362 - Create new Nimbus helper r=aaronmt,ohorvath
[gecko.git] / xpcom / base / AvailableMemoryWatcher.cpp
blob73ad7242bf3b71fc9a6478490294d69105f9e145
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"
20 namespace mozilla {
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;
29 public:
30 NullTabUnloader() = default;
32 NS_DECL_ISUPPORTS
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;
45 /*static*/
46 already_AddRefed<nsAvailableMemoryWatcherBase>
47 nsAvailableMemoryWatcherBase::GetSingleton() {
48 if (!sSingleton) {
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),
63 mInteracting(false) {
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
70 "xpcom-shutdown",
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.");
80 if (mObserverSvc) {
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);
92 return NS_OK;
95 void nsAvailableMemoryWatcherBase::Shutdown() {
96 for (auto topic : kObserverTopics) {
97 mObserverSvc->RemoveObserver(this, topic);
101 NS_IMETHODIMP
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) {
107 Shutdown();
108 } else if (strcmp(aTopic, "user-interaction-inactive") == 0) {
109 mInteracting = false;
110 #ifdef MOZ_CRASHREPORTER
111 CrashReporter::SetInactiveStateStart();
112 #endif
113 } else if (strcmp(aTopic, "user-interaction-active") == 0) {
114 mInteracting = true;
115 #ifdef MOZ_CRASHREPORTER
116 CrashReporter::ClearInactiveStateStart();
117 #endif
119 return NS_OK;
122 nsresult nsAvailableMemoryWatcherBase::RegisterTabUnloader(
123 nsITabUnloader* aTabUnloader) {
124 mTabUnloader = aTabUnloader;
125 return NS_OK;
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
130 // to deadlock.
131 nsresult nsAvailableMemoryWatcherBase::OnUnloadAttemptCompleted(
132 nsresult aResult) {
133 MutexAutoLock lock(mMutex);
134 switch (aResult) {
135 // A tab was unloaded successfully.
136 case NS_OK:
137 ++mNumOfTabUnloading;
138 break;
140 // There was no unloadable tab.
141 case NS_ERROR_NOT_AVAILABLE:
142 ++mNumOfMemoryPressure;
143 NS_NotifyOfEventualMemoryPressure(MemoryPressureState::LowMemory);
144 break;
146 // There was a pending task to unload a tab.
147 case NS_ERROR_ABORT:
148 break;
150 default:
151 MOZ_ASSERT_UNREACHABLE("Unexpected aResult");
152 break;
154 return NS_OK;
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())),
170 }));
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);
183 #endif
185 } // namespace mozilla