Bug 1825336 - Make toolkit/components/url-classifier/ buildable outside of a unified...
[gecko.git] / xpcom / base / AvailableMemoryWatcher.cpp
blob014ad16f051d28f3b2920f4eaea31ddc3eb697ed
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"
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 : mNumOfTabUnloading(0),
60 mNumOfMemoryPressure(0),
61 mTabUnloader(new NullTabUnloader),
62 mInteracting(false) {
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
69 "xpcom-shutdown",
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.");
79 if (mObserverSvc) {
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);
91 return NS_OK;
94 void nsAvailableMemoryWatcherBase::Shutdown() {
95 for (auto topic : kObserverTopics) {
96 mObserverSvc->RemoveObserver(this, topic);
100 NS_IMETHODIMP
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) {
106 Shutdown();
107 } else if (strcmp(aTopic, "user-interaction-inactive") == 0) {
108 mInteracting = false;
109 #ifdef MOZ_CRASHREPORTER
110 CrashReporter::SetInactiveStateStart();
111 #endif
112 } else if (strcmp(aTopic, "user-interaction-active") == 0) {
113 mInteracting = true;
114 #ifdef MOZ_CRASHREPORTER
115 CrashReporter::ClearInactiveStateStart();
116 #endif
118 return NS_OK;
121 nsresult nsAvailableMemoryWatcherBase::RegisterTabUnloader(
122 nsITabUnloader* aTabUnloader) {
123 mTabUnloader = aTabUnloader;
124 return NS_OK;
127 nsresult nsAvailableMemoryWatcherBase::OnUnloadAttemptCompleted(
128 nsresult aResult) {
129 switch (aResult) {
130 // A tab was unloaded successfully.
131 case NS_OK:
132 ++mNumOfTabUnloading;
133 break;
135 // There was no unloadable tab.
136 case NS_ERROR_NOT_AVAILABLE:
137 ++mNumOfMemoryPressure;
138 NS_NotifyOfEventualMemoryPressure(MemoryPressureState::LowMemory);
139 break;
141 // There was a pending task to unload a tab.
142 case NS_ERROR_ABORT:
143 break;
145 default:
146 MOZ_ASSERT_UNREACHABLE("Unexpected aResult");
147 break;
149 return NS_OK;
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())),
165 Nothing());
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);
178 #endif
180 } // namespace mozilla