Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / xpcom / base / AvailableMemoryWatcherMac.cpp
blobd6f2d16b30658ba4ff2c5416e21ecef68e67f8b8
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 <sys/sysctl.h>
8 #include <sys/types.h>
9 #include <time.h>
11 #include "AvailableMemoryWatcher.h"
12 #include "Logging.h"
13 #include "mozilla/Preferences.h"
14 #include "nsICrashReporter.h"
15 #include "nsISupports.h"
16 #include "nsITimer.h"
17 #include "nsMemoryPressure.h"
18 #include "nsPrintfCString.h"
20 #define MP_LOG(...) MOZ_LOG(gMPLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
21 static mozilla::LazyLogModule gMPLog("MemoryPressure");
23 namespace mozilla {
26 * The Mac AvailableMemoryWatcher works as follows. When the OS memory pressure
27 * level changes on macOS, nsAvailableMemoryWatcher::OnMemoryPressureChanged()
28 * is called with the new memory pressure level. The level is represented in
29 * Gecko by a MacMemoryPressureLevel instance and represents the states of
30 * normal, warning, or critical which correspond to the native levels. When the
31 * browser launches, the initial level is determined using a sysctl. Which
32 * actions are taken in the browser in response to memory pressure, and the
33 * level (warning or critical) which trigger the reponse is configurable with
34 * prefs to make it easier to perform experiments to study how the response
35 * affects the user experience.
37 * By default, the browser responds by attempting to reduce memory use when the
38 * OS transitions to the critical level and while it stays in the critical
39 * level. i.e., "critical" OS memory pressure is the default threshold for the
40 * low memory response. Setting pref "browser.lowMemoryResponseOnWarn" to true
41 * changes the memory response to occur at the "warning" level which is less
42 * severe than "critical". When entering the critical level, we begin polling
43 * the memory pressure level every 'n' milliseconds (specified via the pref
44 * "browser.lowMemoryPollingIntervalMS"). Each time the poller wakes up and
45 * finds the OS still under memory pressure, the low memory response is
46 * executed.
48 * By default, the memory pressure response is, in order, to
49 * 1) call nsITabUnloader::UnloadTabAsync(),
50 * 2) if no tabs could be unloaded, issue a Gecko
51 * MemoryPressureState::LowMemory notification.
52 * The response can be changed via the pref "browser.lowMemoryResponseMask" to
53 * limit the actions to only tab unloading or Gecko memory pressure
54 * notifications.
56 * Polling occurs on the main thread because, at each polling interval, we
57 * call into the tab unloader which requires being on the main thread.
58 * Polling only occurs while under OS memory pressure at the critical (by
59 * default) level.
61 class nsAvailableMemoryWatcher final : public nsITimerCallback,
62 public nsINamed,
63 public nsAvailableMemoryWatcherBase {
64 public:
65 NS_DECL_ISUPPORTS_INHERITED
66 NS_DECL_NSIOBSERVER
67 NS_DECL_NSITIMERCALLBACK
68 NS_DECL_NSINAMED
70 nsAvailableMemoryWatcher();
71 nsresult Init() override;
73 void OnMemoryPressureChanged(MacMemoryPressureLevel aLevel) override;
74 void AddChildAnnotations(
75 const UniquePtr<ipc::CrashReporterHost>& aCrashReporter) override;
77 private:
78 ~nsAvailableMemoryWatcher(){};
80 void OnMemoryPressureChangedInternal(MacMemoryPressureLevel aNewLevel,
81 bool aIsInitialLevel);
83 // Override OnUnloadAttemptCompleted() so that we can control whether
84 // or not a Gecko memory-pressure event is sent after a tab unload attempt.
85 // This method is called externally by the tab unloader after a tab unload
86 // attempt. It is used internally when tab unloading is disabled in
87 // mResponseMask.
88 nsresult OnUnloadAttemptCompleted(nsresult aResult) override;
90 void OnShutdown();
91 void OnPrefChange();
93 void InitParentAnnotations();
94 void UpdateParentAnnotations();
96 void AddParentAnnotation(CrashReporter::Annotation aAnnotation,
97 nsAutoCString aString) {
98 CrashReporter::AnnotateCrashReport(aAnnotation, aString);
100 void AddParentAnnotation(CrashReporter::Annotation aAnnotation,
101 uint32_t aData) {
102 CrashReporter::AnnotateCrashReport(aAnnotation, aData);
105 void LowMemoryResponse();
106 void StartPolling();
107 void StopPolling();
108 void RestartPolling();
109 inline bool IsPolling() { return mTimer; }
111 void ReadSysctls();
113 // This enum represents the allowed values for the pref that controls
114 // the low memory response - "browser.lowMemoryResponseMask". Specifically,
115 // whether or not we unload tabs and/or issue the Gecko "memory-pressure"
116 // internal notification. For tab unloading, the pref
117 // "browser.tabs.unloadOnLowMemory" must also be set.
118 enum ResponseMask {
119 eNone = 0x0,
120 eTabUnload = 0x1,
121 eInternalMemoryPressure = 0x2,
122 eAll = 0x3,
124 static constexpr char kResponseMask[] = "browser.lowMemoryResponseMask";
125 static const uint32_t kResponseMaskDefault;
126 static const uint32_t kResponseMaskMax;
128 // Pref for controlling how often we wake up during an OS memory pressure
129 // time period. At each wakeup, we unload tabs and issue the Gecko
130 // "memory-pressure" internal notification. When not under OS memory pressure,
131 // polling is disabled.
132 static constexpr char kPollingIntervalMS[] =
133 "browser.lowMemoryPollingIntervalMS";
134 static const uint32_t kPollingIntervalMaxMS;
135 static const uint32_t kPollingIntervalMinMS;
136 static const uint32_t kPollingIntervalDefaultMS;
138 static constexpr char kResponseOnWarn[] = "browser.lowMemoryResponseOnWarn";
139 static const bool kResponseLevelOnWarnDefault = false;
141 // Init has been called.
142 bool mInitialized;
144 // The memory pressure reported to the application by macOS.
145 MacMemoryPressureLevel mLevel;
147 // The OS memory pressure level that triggers the response.
148 MacMemoryPressureLevel mResponseLevel;
150 // The value of the kern.memorystatus_vm_pressure_level sysctl. The OS
151 // notifies the application when the memory pressure level changes,
152 // but the sysctl value can be read at any time. Unofficially, the sysctl
153 // value corresponds to the OS memory pressure level with 4=>critical,
154 // 2=>warning, and 1=>normal (values from kernel event.h file).
155 uint32_t mLevelSysctl;
156 static const int kSysctlLevelNormal = 0x1;
157 static const int kSysctlLevelWarning = 0x2;
158 static const int kSysctlLevelCritical = 0x4;
160 // The value of the kern.memorystatus_level sysctl. Unofficially,
161 // this is the percentage of available memory. (Also readable
162 // via the undocumented memorystatus_get_level syscall.)
163 int mAvailMemSysctl;
165 // The string representation of `mLevel`. i.e., normal, warning, or critical.
166 // Set to "unset" until a memory pressure change is reported to the process
167 // by the OS.
168 nsAutoCString mLevelStr;
170 // Timestamps for memory pressure level changes. Specifically, the Unix
171 // time in string form. Saved as Unix time to allow comparisons with
172 // the crash time.
173 nsAutoCString mNormalTimeStr;
174 nsAutoCString mWarningTimeStr;
175 nsAutoCString mCriticalTimeStr;
177 nsCOMPtr<nsITimer> mTimer; // non-null indicates the timer is active
179 // Saved pref values.
180 uint32_t mPollingInterval;
181 uint32_t mResponseMask;
184 const uint32_t nsAvailableMemoryWatcher::kResponseMaskDefault =
185 ResponseMask::eAll;
186 const uint32_t nsAvailableMemoryWatcher::kResponseMaskMax = ResponseMask::eAll;
188 // 10 seconds
189 const uint32_t nsAvailableMemoryWatcher::kPollingIntervalDefaultMS = 10'000;
190 // 10 minutes
191 const uint32_t nsAvailableMemoryWatcher::kPollingIntervalMaxMS = 600'000;
192 // 100 milliseconds
193 const uint32_t nsAvailableMemoryWatcher::kPollingIntervalMinMS = 100;
195 NS_IMPL_ISUPPORTS_INHERITED(nsAvailableMemoryWatcher,
196 nsAvailableMemoryWatcherBase, nsIObserver,
197 nsITimerCallback, nsINamed)
199 nsAvailableMemoryWatcher::nsAvailableMemoryWatcher()
200 : mInitialized(false),
201 mLevel(MacMemoryPressureLevel::Value::eUnset),
202 mResponseLevel(MacMemoryPressureLevel::Value::eCritical),
203 mLevelSysctl(0xFFFFFFFF),
204 mAvailMemSysctl(-1),
205 mLevelStr("Unset"),
206 mNormalTimeStr("Unset"),
207 mWarningTimeStr("Unset"),
208 mCriticalTimeStr("Unset"),
209 mPollingInterval(0),
210 mResponseMask(ResponseMask::eAll) {}
212 nsresult nsAvailableMemoryWatcher::Init() {
213 nsresult rv = nsAvailableMemoryWatcherBase::Init();
214 if (NS_FAILED(rv)) {
215 return rv;
218 // Users of nsAvailableMemoryWatcher should use
219 // nsAvailableMemoryWatcherBase::GetSingleton() and not call Init directly.
220 MOZ_ASSERT(!mInitialized);
221 if (mInitialized) {
222 return NS_ERROR_ALREADY_INITIALIZED;
225 // Read polling frequency pref
226 mPollingInterval =
227 Preferences::GetUint(kPollingIntervalMS, kPollingIntervalDefaultMS);
228 mPollingInterval = std::clamp(mPollingInterval, kPollingIntervalMinMS,
229 kPollingIntervalMaxMS);
231 // Read response bitmask pref which (along with the main tab unloading
232 // preference) controls whether or not tab unloading and Gecko (internal)
233 // memory pressure notifications will be sent. The main tab unloading
234 // preference must also be enabled for tab unloading to occur.
235 mResponseMask = Preferences::GetUint(kResponseMask, kResponseMaskDefault);
236 if (mResponseMask > kResponseMaskMax) {
237 mResponseMask = kResponseMaskMax;
240 // Read response level pref
241 if (Preferences::GetBool(kResponseOnWarn, kResponseLevelOnWarnDefault)) {
242 mResponseLevel = MacMemoryPressureLevel::Value::eWarning;
243 } else {
244 mResponseLevel = MacMemoryPressureLevel::Value::eCritical;
247 ReadSysctls();
248 MP_LOG("Initial memory pressure sysctl: %d", mLevelSysctl);
249 MP_LOG("Initial available memory sysctl: %d", mAvailMemSysctl);
251 // Set the initial state of all annotations for parent crash reports.
252 // Content process crash reports are set when a crash occurs and
253 // AddChildAnnotations() is called.
254 CrashReporter::AnnotateCrashReport(
255 CrashReporter::Annotation::MacMemoryPressure, mLevelStr);
256 CrashReporter::AnnotateCrashReport(
257 CrashReporter::Annotation::MacMemoryPressureNormalTime, mNormalTimeStr);
258 CrashReporter::AnnotateCrashReport(
259 CrashReporter::Annotation::MacMemoryPressureWarningTime, mWarningTimeStr);
260 CrashReporter::AnnotateCrashReport(
261 CrashReporter::Annotation::MacMemoryPressureCriticalTime,
262 mCriticalTimeStr);
263 CrashReporter::AnnotateCrashReport(
264 CrashReporter::Annotation::MacMemoryPressureSysctl, mLevelSysctl);
265 CrashReporter::AnnotateCrashReport(
266 CrashReporter::Annotation::MacAvailableMemorySysctl, mAvailMemSysctl);
268 // To support running experiments, handle pref
269 // changes without requiring a browser restart.
270 rv = Preferences::AddStrongObserver(this, kResponseMask);
271 if (NS_FAILED(rv)) {
272 NS_WARNING(
273 nsPrintfCString("Failed to add %s observer", kResponseMask).get());
275 rv = Preferences::AddStrongObserver(this, kPollingIntervalMS);
276 if (NS_FAILED(rv)) {
277 NS_WARNING(
278 nsPrintfCString("Failed to add %s observer", kPollingIntervalMS).get());
280 rv = Preferences::AddStrongObserver(this, kResponseOnWarn);
281 if (NS_FAILED(rv)) {
282 NS_WARNING(
283 nsPrintfCString("Failed to add %s observer", kResponseOnWarn).get());
286 // Use the memory pressure sysctl to initialize our memory pressure state.
287 MacMemoryPressureLevel initialLevel;
288 switch (mLevelSysctl) {
289 case kSysctlLevelNormal:
290 initialLevel = MacMemoryPressureLevel::Value::eNormal;
291 break;
292 case kSysctlLevelWarning:
293 initialLevel = MacMemoryPressureLevel::Value::eWarning;
294 break;
295 case kSysctlLevelCritical:
296 initialLevel = MacMemoryPressureLevel::Value::eCritical;
297 break;
298 default:
299 initialLevel = MacMemoryPressureLevel::Value::eUnexpected;
302 OnMemoryPressureChangedInternal(initialLevel, /* aIsInitialLevel */ true);
303 mInitialized = true;
304 return NS_OK;
307 already_AddRefed<nsAvailableMemoryWatcherBase> CreateAvailableMemoryWatcher() {
308 // Users of nsAvailableMemoryWatcher should use
309 // nsAvailableMemoryWatcherBase::GetSingleton().
310 RefPtr watcher(new nsAvailableMemoryWatcher());
311 watcher->Init();
312 return watcher.forget();
315 // Update the memory pressure level, level change timestamps, and sysctl
316 // level crash report annotations.
317 void nsAvailableMemoryWatcher::UpdateParentAnnotations() {
318 // Generate a string representation of the current Unix time.
319 time_t timeChanged = time(NULL);
320 nsAutoCString timeChangedString;
321 timeChangedString =
322 nsPrintfCString("%" PRIu64, static_cast<uint64_t>(timeChanged));
324 nsAutoCString pressureLevelString;
325 Maybe<CrashReporter::Annotation> pressureLevelKey;
327 switch (mLevel.GetValue()) {
328 case MacMemoryPressureLevel::Value::eNormal:
329 mNormalTimeStr = timeChangedString;
330 pressureLevelString = "Normal";
331 pressureLevelKey.emplace(
332 CrashReporter::Annotation::MacMemoryPressureNormalTime);
333 break;
334 case MacMemoryPressureLevel::Value::eWarning:
335 mWarningTimeStr = timeChangedString;
336 pressureLevelString = "Warning";
337 pressureLevelKey.emplace(
338 CrashReporter::Annotation::MacMemoryPressureWarningTime);
339 break;
340 case MacMemoryPressureLevel::Value::eCritical:
341 mCriticalTimeStr = timeChangedString;
342 pressureLevelString = "Critical";
343 pressureLevelKey.emplace(
344 CrashReporter::Annotation::MacMemoryPressureCriticalTime);
345 break;
346 default:
347 pressureLevelString = "Unexpected";
348 break;
351 // Save the current memory pressure level.
352 AddParentAnnotation(CrashReporter::Annotation::MacMemoryPressure,
353 pressureLevelString);
355 // Save the time we transitioned to the current memory pressure level.
356 if (pressureLevelKey.isSome()) {
357 AddParentAnnotation(pressureLevelKey.value(), timeChangedString);
360 AddParentAnnotation(CrashReporter::Annotation::MacMemoryPressureSysctl,
361 mLevelSysctl);
362 AddParentAnnotation(CrashReporter::Annotation::MacAvailableMemorySysctl,
363 mAvailMemSysctl);
366 void nsAvailableMemoryWatcher::ReadSysctls() {
367 // Pressure level
368 uint32_t level;
369 size_t size = sizeof(level);
370 if (sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &size, NULL,
371 0) == -1) {
372 MP_LOG("Failure reading memory pressure sysctl");
374 mLevelSysctl = level;
376 // Available memory percent
377 int availPercent;
378 size = sizeof(availPercent);
379 if (sysctlbyname("kern.memorystatus_level", &availPercent, &size, NULL, 0) ==
380 -1) {
381 MP_LOG("Failure reading available memory level");
383 mAvailMemSysctl = availPercent;
386 /* virtual */
387 void nsAvailableMemoryWatcher::OnMemoryPressureChanged(
388 MacMemoryPressureLevel aNewLevel) {
389 MOZ_ASSERT(mInitialized);
390 OnMemoryPressureChangedInternal(aNewLevel, /* aIsInitialLevel */ false);
393 void nsAvailableMemoryWatcher::OnMemoryPressureChangedInternal(
394 MacMemoryPressureLevel aNewLevel, bool aIsInitialLevel) {
395 MOZ_ASSERT(mInitialized || aIsInitialLevel);
396 MP_LOG("MemoryPressureChange: existing level: %s, new level: %s",
397 mLevel.ToString(), aNewLevel.ToString());
399 // If 'aNewLevel' is not one of normal, warning, or critical, ASSERT
400 // here so we can debug this scenario. For non-debug builds, ignore
401 // the unexpected value which will be logged in crash reports.
402 MOZ_ASSERT(aNewLevel.IsNormal() || aNewLevel.IsWarningOrAbove());
404 if (mLevel == aNewLevel) {
405 return;
408 // Start the memory pressure response if the new level is high enough
409 // and the existing level was not.
410 if ((mLevel < mResponseLevel) && (aNewLevel >= mResponseLevel)) {
411 UpdateLowMemoryTimeStamp();
412 LowMemoryResponse();
413 if (mResponseMask) {
414 StartPolling();
418 // End the memory pressure reponse if the new level is not high enough.
419 if ((mLevel >= mResponseLevel) && (aNewLevel < mResponseLevel)) {
421 MutexAutoLock lock(mMutex);
422 RecordTelemetryEventOnHighMemory(lock);
424 StopPolling();
425 MP_LOG("Issuing MemoryPressureState::NoPressure");
426 NS_NotifyOfMemoryPressure(MemoryPressureState::NoPressure);
429 mLevel = aNewLevel;
431 if (!aIsInitialLevel) {
432 // Sysctls are already read by ::Init().
433 ReadSysctls();
434 MP_LOG("level sysctl: %d, available memory: %d percent", mLevelSysctl,
435 mAvailMemSysctl);
437 UpdateParentAnnotations();
440 /* virtual */
441 // Add all annotations to the provided crash reporter instance.
442 void nsAvailableMemoryWatcher::AddChildAnnotations(
443 const UniquePtr<ipc::CrashReporterHost>& aCrashReporter) {
444 aCrashReporter->AddAnnotation(CrashReporter::Annotation::MacMemoryPressure,
445 mLevelStr);
446 aCrashReporter->AddAnnotation(
447 CrashReporter::Annotation::MacMemoryPressureNormalTime, mNormalTimeStr);
448 aCrashReporter->AddAnnotation(
449 CrashReporter::Annotation::MacMemoryPressureWarningTime, mWarningTimeStr);
450 aCrashReporter->AddAnnotation(
451 CrashReporter::Annotation::MacMemoryPressureCriticalTime,
452 mCriticalTimeStr);
453 aCrashReporter->AddAnnotation(
454 CrashReporter::Annotation::MacMemoryPressureSysctl, mLevelSysctl);
455 aCrashReporter->AddAnnotation(
456 CrashReporter::Annotation::MacAvailableMemorySysctl, mAvailMemSysctl);
459 void nsAvailableMemoryWatcher::LowMemoryResponse() {
460 if (mResponseMask & ResponseMask::eTabUnload) {
461 MP_LOG("Attempting tab unload");
462 mTabUnloader->UnloadTabAsync();
463 } else {
464 // Re-use OnUnloadAttemptCompleted() to issue the internal
465 // memory pressure event.
466 OnUnloadAttemptCompleted(NS_ERROR_NOT_AVAILABLE);
470 NS_IMETHODIMP
471 nsAvailableMemoryWatcher::Notify(nsITimer* aTimer) {
472 MOZ_ASSERT(NS_IsMainThread());
473 MOZ_ASSERT(mLevel >= mResponseLevel);
474 LowMemoryResponse();
475 return NS_OK;
478 // Override OnUnloadAttemptCompleted() so that we can issue Gecko memory
479 // pressure notifications only if eInternalMemoryPressure is set in
480 // mResponseMask. When called from the tab unloader, an |aResult| value of
481 // NS_OK indicates the tab unloader successfully unloaded a tab.
482 // NS_ERROR_NOT_AVAILABLE indicates the tab unloader did not unload any tabs.
483 NS_IMETHODIMP
484 nsAvailableMemoryWatcher::OnUnloadAttemptCompleted(nsresult aResult) {
485 // On MacOS we don't access these members offthread; however we do on other
486 // OSes and so they are guarded by the mutex.
487 MutexAutoLock lock(mMutex);
488 switch (aResult) {
489 // A tab was unloaded successfully.
490 case NS_OK:
491 MP_LOG("Tab unloaded");
492 ++mNumOfTabUnloading;
493 break;
495 // Either the tab unloader found no unloadable tabs OR we've been called
496 // locally to explicitly issue the internal memory pressure event because
497 // tab unloading is disabled in |mResponseMask|. In either case, attempt
498 // to reduce memory use using the internal memory pressure notification.
499 case NS_ERROR_NOT_AVAILABLE:
500 if (mResponseMask & ResponseMask::eInternalMemoryPressure) {
501 ++mNumOfMemoryPressure;
502 MP_LOG("Tab not unloaded");
503 MP_LOG("Issuing MemoryPressureState::LowMemory");
504 NS_NotifyOfEventualMemoryPressure(MemoryPressureState::LowMemory);
506 break;
508 // There was a pending task to unload a tab.
509 case NS_ERROR_ABORT:
510 break;
512 default:
513 MOZ_ASSERT_UNREACHABLE("Unexpected aResult");
514 break;
516 return NS_OK;
519 NS_IMETHODIMP
520 nsAvailableMemoryWatcher::Observe(nsISupports* aSubject, const char* aTopic,
521 const char16_t* aData) {
522 nsresult rv = nsAvailableMemoryWatcherBase::Observe(aSubject, aTopic, aData);
523 if (NS_FAILED(rv)) {
524 return rv;
527 if (strcmp(aTopic, "xpcom-shutdown") == 0) {
528 OnShutdown();
529 } else if (strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
530 OnPrefChange();
532 return NS_OK;
535 void nsAvailableMemoryWatcher::OnShutdown() {
536 StopPolling();
537 Preferences::RemoveObserver(this, kResponseMask);
538 Preferences::RemoveObserver(this, kPollingIntervalMS);
541 void nsAvailableMemoryWatcher::OnPrefChange() {
542 MP_LOG("OnPrefChange()");
543 // Handle the polling interval changing.
544 uint32_t pollingInterval = Preferences::GetUint(kPollingIntervalMS);
545 if (pollingInterval != mPollingInterval) {
546 mPollingInterval = std::clamp(pollingInterval, kPollingIntervalMinMS,
547 kPollingIntervalMaxMS);
548 RestartPolling();
551 // Handle the response mask changing.
552 uint32_t responseMask = Preferences::GetUint(kResponseMask);
553 if (mResponseMask != responseMask) {
554 mResponseMask = std::min(responseMask, kResponseMaskMax);
556 // Do we need to turn on polling?
557 if (mResponseMask && (mLevel >= mResponseLevel) && !IsPolling()) {
558 StartPolling();
561 // Do we need to turn off polling?
562 if (!mResponseMask && IsPolling()) {
563 StopPolling();
567 // Handle the response level changing.
568 MacMemoryPressureLevel newResponseLevel;
569 if (Preferences::GetBool(kResponseOnWarn, kResponseLevelOnWarnDefault)) {
570 newResponseLevel = MacMemoryPressureLevel::Value::eWarning;
571 } else {
572 newResponseLevel = MacMemoryPressureLevel::Value::eCritical;
574 if (newResponseLevel == mResponseLevel) {
575 return;
578 // Do we need to turn on polling?
579 if (mResponseMask && (newResponseLevel <= mLevel)) {
580 UpdateLowMemoryTimeStamp();
581 LowMemoryResponse();
582 StartPolling();
585 // Do we need to turn off polling?
586 if (IsPolling() && (newResponseLevel > mLevel)) {
588 MutexAutoLock lock(mMutex);
589 RecordTelemetryEventOnHighMemory(lock);
591 StopPolling();
592 MP_LOG("Issuing MemoryPressureState::NoPressure");
593 NS_NotifyOfMemoryPressure(MemoryPressureState::NoPressure);
595 mResponseLevel = newResponseLevel;
598 void nsAvailableMemoryWatcher::StartPolling() {
599 MOZ_ASSERT(NS_IsMainThread());
600 if (!mTimer) {
601 MP_LOG("Starting poller");
602 mTimer = NS_NewTimer();
603 if (mTimer) {
604 mTimer->InitWithCallback(this, mPollingInterval,
605 nsITimer::TYPE_REPEATING_SLACK);
610 void nsAvailableMemoryWatcher::StopPolling() {
611 MOZ_ASSERT(NS_IsMainThread());
612 if (mTimer) {
613 MP_LOG("Pausing poller");
614 mTimer->Cancel();
615 mTimer = nullptr;
619 void nsAvailableMemoryWatcher::RestartPolling() {
620 if (IsPolling()) {
621 StopPolling();
622 StartPolling();
623 } else {
624 MOZ_ASSERT(!mTimer);
628 NS_IMETHODIMP
629 nsAvailableMemoryWatcher::GetName(nsACString& aName) {
630 aName.AssignLiteral("nsAvailableMemoryWatcher");
631 return NS_OK;
634 } // namespace mozilla