Commit 5d43115f authored by Csoregi Natalia's avatar Csoregi Natalia
Browse files

Backed out changeset 44b7971a0893 (bug 1532955) for bustage on...

Backed out changeset 44b7971a0893 (bug 1532955) for bustage on AvailableMemoryWatcher.cpp and other failures. CLOSED TREE
parent 59f02209
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ MWQThread
MediaCache
MediaTelemetry
MediaTrackGrph
MemoryPoller
mtransport
NamedPipeSrv
Netlink Monitor
+1 −11
Original line number Diff line number Diff line
@@ -1180,7 +1180,7 @@
  value: 16777216
  mirror: always

#if defined(XP_WIN) || defined(XP_LINUX)
#ifdef XP_WIN
  # Notify TabUnloader or send the memory pressure if the memory resource
  # notification is signaled AND the available commit space is lower than
  # this value.
@@ -1190,16 +1190,6 @@
    mirror: always
#endif

#ifdef XP_LINUX
  # On Linux we also check available memory in comparison to total memory,
  # and use this percent value (out of 100) to determine if we are in a
  # low memory scenario.
-   name: browser.low_commit_space_threshold_percent
    type: RelaxedAtomicUint32
    value: 5
    mirror: always
#endif

# Render animations and videos as a solid color
- name: browser.measurement.render_anims_and_video_solid
  type: RelaxedAtomicBool
+0 −2
Original line number Diff line number Diff line
@@ -44,8 +44,6 @@ skip-if = os != 'win'
[TestMacroForEach]
[TestMathAlgorithms]
[TestMaybe]
[TestMemoryPressureWatcherLinux]
skip-if = os != 'linux'
[TestMMPolicy]
skip-if = os != 'win'
[TestNativeNt]
+1 −1
Original line number Diff line number Diff line
@@ -169,7 +169,7 @@ void nsAvailableMemoryWatcherBase::RecordTelemetryEventOnHighMemory() {

// Define the fallback method for a platform for which a platform-specific
// CreateAvailableMemoryWatcher() is not defined.
#if !defined(XP_WIN) && !defined(XP_MACOSX) && !defined(XP_LINUX)
#if !defined(XP_WIN) && !defined(XP_MACOSX)
already_AddRefed<nsAvailableMemoryWatcherBase> CreateAvailableMemoryWatcher() {
  RefPtr instance(new nsAvailableMemoryWatcherBase);
  return do_AddRef(instance);
+0 −252
Original line number Diff line number Diff line
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AvailableMemoryWatcher.h"
#include "AvailableMemoryWatcherUtils.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/Unused.h"
#include "nsIObserverService.h"
#include "nsISupports.h"
#include "nsITimer.h"
#include "nsIThread.h"
#include "nsMemoryPressure.h"

namespace mozilla {

// Linux has no native low memory detection. This class creates a timer that
// polls for low memory and sends a low memory notification if it notices a
// memory pressure event.
class nsAvailableMemoryWatcher final : public nsITimerCallback,
                                       public nsINamed,
                                       public nsAvailableMemoryWatcherBase {
 public:
  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_NSITIMERCALLBACK
  NS_DECL_NSIOBSERVER
  NS_DECL_NSINAMED

  nsresult Init() override;
  nsAvailableMemoryWatcher();

  void HandleLowMemory();
  void MaybeHandleHighMemory();

 private:
  ~nsAvailableMemoryWatcher() = default;
  void StartPolling(const MutexAutoLock&);
  void StopPolling(const MutexAutoLock&);
  void ShutDown(const MutexAutoLock&);
  static bool IsMemoryLow();

  nsCOMPtr<nsITimer> mTimer;
  nsCOMPtr<nsIThread> mThread;

  bool mPolling;
  bool mUnderMemoryPressure;

  // We might tell polling to start/stop from our polling thread
  // or from the main thread during ::Observe().
  Mutex mMutex;

  // Polling interval to check for low memory. In high memory scenarios,
  // default to 5000 ms between each check.
  static const uint32_t kHighMemoryPollingIntervalMS = 5000;

  // Polling interval to check for low memory. Default to 1000 ms between each
  // check. Use this interval when memory is low,
  static const uint32_t kLowMemoryPollingIntervalMS = 1000;
};

// A modern version of linux should keep memory information in the
// /proc/meminfo path.
static const char* kMeminfoPath = "/proc/meminfo";

nsAvailableMemoryWatcher::nsAvailableMemoryWatcher()
    : mPolling(false),
      mUnderMemoryPressure(false),
      mMutex("Memory Poller mutex") {}

nsresult nsAvailableMemoryWatcher::Init() {
  nsresult rv = nsAvailableMemoryWatcherBase::Init();
  if (NS_FAILED(rv)) {
    return rv;
  }
  mTimer = NS_NewTimer();
  nsCOMPtr<nsIThread> thread;
  // We have to make our own thread here instead of using the background pool,
  // because some low memory scenarios can cause the background pool to fill.
  rv = NS_NewNamedThread("MemoryPoller", getter_AddRefs(thread));
  if (NS_FAILED(rv)) {
    NS_WARNING("Couldn't make a thread for nsAvailableMemoryWatcher.");
    // In this scenario we can't poll for low memory, since we can't dispatch
    // to our memory watcher thread.
    return rv;
  }
  mThread = thread;

  MutexAutoLock lock(mMutex);
  StartPolling(lock);

  return NS_OK;
}

already_AddRefed<nsAvailableMemoryWatcherBase> CreateAvailableMemoryWatcher() {
  RefPtr watcher(new nsAvailableMemoryWatcher);

  if (NS_FAILED(watcher->Init())) {
    return do_AddRef(new nsAvailableMemoryWatcherBase);
  }

  return watcher.forget();
}

NS_IMPL_ISUPPORTS(nsAvailableMemoryWatcher, nsITimerCallback, nsIObserver);

void nsAvailableMemoryWatcher::StopPolling(const MutexAutoLock&) {
  if (mPolling && mTimer) {
    // stop dispatching memory checks to the thread.
    mTimer->Cancel();
    mPolling = false;
  }
}

// Check /proc/meminfo for low memory. Largely C method for reading
// /proc/meminfo.
/* static */
bool nsAvailableMemoryWatcher::IsMemoryLow() {
  MemoryInfo memInfo{0, 0};
  bool aResult = false;

  nsresult rv = ReadMemoryFile(kMeminfoPath, memInfo);

  if (NS_FAILED(rv) || memInfo.memAvailable == 0) {
    // If memAvailable cannot be found, then we are using an older system.
    // We can't accurately poll on this.
    return aResult;
  }
  unsigned long memoryAsPercentage =
      (memInfo.memAvailable * 100) / memInfo.memTotal;

  if (memoryAsPercentage <=
          StaticPrefs::browser_low_commit_space_threshold_percent() ||
      memInfo.memAvailable <
          StaticPrefs::browser_low_commit_space_threshold_mb() * 1024) {
    aResult = true;
  }

  return aResult;
}

void nsAvailableMemoryWatcher::ShutDown(const MutexAutoLock&) {
  if (mTimer) {
    mTimer->Cancel();
  }

  if (mThread) {
    mThread->Shutdown();
  }
}

// We will use this to poll for low memory.
NS_IMETHODIMP
nsAvailableMemoryWatcher::Notify(nsITimer* aTimer) {
  MutexAutoLock lock(mMutex);
  if (!mThread) {
    // If we've made it this far and there's no  |mThread|,
    // we might have failed to dispatch it for some reason.
    MOZ_ASSERT(mThread);
    return NS_ERROR_FAILURE;
  }
  nsresult rv = mThread->Dispatch(
      NS_NewRunnableFunction("MemoryPoller", [self = RefPtr{this}]() {
        if (self->IsMemoryLow()) {
          self->HandleLowMemory();
        } else {
          self->MaybeHandleHighMemory();
        }
      }));

  if NS_FAILED (rv) {
    NS_WARNING("Cannot dispatch memory polling event.");
  }
  return NS_OK;
}

void nsAvailableMemoryWatcher::HandleLowMemory() {
  MutexAutoLock lock(mMutex);
  if (!mUnderMemoryPressure) {
    mUnderMemoryPressure = true;
    // Poll more frequently under memory pressure.
    StartPolling(lock);
  }
  UpdateLowMemoryTimeStamp();
  // We handle low memory offthread, but we want to unload
  // tabs only from the main thread, so we will dispatch this
  // back to the main thread.
  NS_DispatchToMainThread(NS_NewRunnableFunction(
      "nsAvailableMemoryWatcher::OnLowMemory",
      [self = RefPtr{this}]() { self->mTabUnloader->UnloadTabAsync(); }));
}

// If memory is not low, we may need to dispatch an
// event for it if we have been under memory pressure.
// We can also adjust our polling interval.
void nsAvailableMemoryWatcher::MaybeHandleHighMemory() {
  MutexAutoLock lock(mMutex);
  if (mUnderMemoryPressure) {
    RecordTelemetryEventOnHighMemory();
    NS_NotifyOfEventualMemoryPressure(MemoryPressureState::NoPressure);
    mUnderMemoryPressure = false;
  }
  StartPolling(lock);
}

// When we change the polling interval, we will need to restart the timer
// on the new interval.
void nsAvailableMemoryWatcher::StartPolling(const MutexAutoLock& aLock) {
  uint32_t pollingInterval = mUnderMemoryPressure
                                 ? kLowMemoryPollingIntervalMS
                                 : kHighMemoryPollingIntervalMS;
  if (!mPolling) {
    // Restart the timer with the new interval if it has stopped.
    // For testing, use a small polling interval.
    if (NS_SUCCEEDED(
            mTimer->InitWithCallback(this, gIsGtest ? 10 : pollingInterval,
                                     nsITimer::TYPE_REPEATING_SLACK))) {
      mPolling = true;
    }
  } else {
    mTimer->SetDelay(gIsGtest ? 10 : pollingInterval);
  }
}

// Observe events for shutting down and starting/stopping the timer.
NS_IMETHODIMP
nsAvailableMemoryWatcher::Observe(nsISupports* aSubject, const char* aTopic,
                                  const char16_t* aData) {
  nsresult rv = nsAvailableMemoryWatcherBase::Observe(aSubject, aTopic, aData);
  if (NS_FAILED(rv)) {
    return rv;
  }

  MutexAutoLock lock(mMutex);
  if (strcmp(aTopic, "xpcom-shutdown") == 0) {
    ShutDown(lock);
  } else if (strcmp(aTopic, "user-interaction-active") == 0) {
    StartPolling(lock);
  } else if (strcmp(aTopic, "user-interaction-inactive") == 0) {
    StopPolling(lock);
  }

  return NS_OK;
}

NS_IMETHODIMP nsAvailableMemoryWatcher::GetName(nsACString& aName) {
  aName.AssignLiteral("nsAvailableMemoryWatcher");
  return NS_OK;
}

}  // namespace mozilla
Loading