mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	This further guards methods that weren't covered under individual OS implementations. Differential Revision: https://phabricator.services.mozilla.com/D188955
		
			
				
	
	
		
			186 lines
		
	
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			186 lines
		
	
	
	
		
			5.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- 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 "mozilla/ClearOnShutdown.h"
 | 
						|
#include "mozilla/dom/Promise.h"
 | 
						|
#include "mozilla/ErrorResult.h"
 | 
						|
#include "mozilla/RefPtr.h"
 | 
						|
#include "mozilla/Services.h"
 | 
						|
#include "mozilla/StaticPtr.h"
 | 
						|
#include "mozilla/Telemetry.h"
 | 
						|
#include "nsExceptionHandler.h"
 | 
						|
#include "nsMemoryPressure.h"
 | 
						|
#include "nsXULAppAPI.h"
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
// Use this class as the initial value of
 | 
						|
// nsAvailableMemoryWatcherBase::mCallback until RegisterCallback() is called
 | 
						|
// so that nsAvailableMemoryWatcherBase does not have to check if its callback
 | 
						|
// object is valid or not.
 | 
						|
class NullTabUnloader final : public nsITabUnloader {
 | 
						|
  ~NullTabUnloader() = default;
 | 
						|
 | 
						|
 public:
 | 
						|
  NullTabUnloader() = default;
 | 
						|
 | 
						|
  NS_DECL_ISUPPORTS
 | 
						|
  NS_DECL_NSITABUNLOADER
 | 
						|
};
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(NullTabUnloader, nsITabUnloader)
 | 
						|
 | 
						|
NS_IMETHODIMP NullTabUnloader::UnloadTabAsync() {
 | 
						|
  return NS_ERROR_NOT_IMPLEMENTED;
 | 
						|
}
 | 
						|
 | 
						|
StaticRefPtr<nsAvailableMemoryWatcherBase>
 | 
						|
    nsAvailableMemoryWatcherBase::sSingleton;
 | 
						|
 | 
						|
/*static*/
 | 
						|
already_AddRefed<nsAvailableMemoryWatcherBase>
 | 
						|
nsAvailableMemoryWatcherBase::GetSingleton() {
 | 
						|
  if (!sSingleton) {
 | 
						|
    sSingleton = CreateAvailableMemoryWatcher();
 | 
						|
    ClearOnShutdown(&sSingleton);
 | 
						|
  }
 | 
						|
 | 
						|
  return do_AddRef(sSingleton);
 | 
						|
}
 | 
						|
 | 
						|
NS_IMPL_ISUPPORTS(nsAvailableMemoryWatcherBase, nsIAvailableMemoryWatcherBase);
 | 
						|
 | 
						|
nsAvailableMemoryWatcherBase::nsAvailableMemoryWatcherBase()
 | 
						|
    : mMutex("nsAvailableMemoryWatcher mutex"),
 | 
						|
      mNumOfTabUnloading(0),
 | 
						|
      mNumOfMemoryPressure(0),
 | 
						|
      mTabUnloader(new NullTabUnloader),
 | 
						|
      mInteracting(false) {
 | 
						|
  MOZ_ASSERT(XRE_IsParentProcess(),
 | 
						|
             "Watching memory only in the main process.");
 | 
						|
}
 | 
						|
 | 
						|
const char* const nsAvailableMemoryWatcherBase::kObserverTopics[] = {
 | 
						|
    // Use this shutdown phase to make sure the instance is destroyed in GTest
 | 
						|
    "xpcom-shutdown",
 | 
						|
    "user-interaction-active",
 | 
						|
    "user-interaction-inactive",
 | 
						|
};
 | 
						|
 | 
						|
nsresult nsAvailableMemoryWatcherBase::Init() {
 | 
						|
  MOZ_ASSERT(NS_IsMainThread(),
 | 
						|
             "nsAvailableMemoryWatcherBase needs to be initialized "
 | 
						|
             "in the main thread.");
 | 
						|
 | 
						|
  if (mObserverSvc) {
 | 
						|
    return NS_ERROR_ALREADY_INITIALIZED;
 | 
						|
  }
 | 
						|
 | 
						|
  mObserverSvc = services::GetObserverService();
 | 
						|
  MOZ_ASSERT(mObserverSvc);
 | 
						|
 | 
						|
  for (auto topic : kObserverTopics) {
 | 
						|
    nsresult rv = mObserverSvc->AddObserver(this, topic,
 | 
						|
                                            /* ownsWeak */ false);
 | 
						|
    NS_ENSURE_SUCCESS(rv, rv);
 | 
						|
  }
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void nsAvailableMemoryWatcherBase::Shutdown() {
 | 
						|
  for (auto topic : kObserverTopics) {
 | 
						|
    mObserverSvc->RemoveObserver(this, topic);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
nsAvailableMemoryWatcherBase::Observe(nsISupports* aSubject, const char* aTopic,
 | 
						|
                                      const char16_t* aData) {
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
 | 
						|
  if (strcmp(aTopic, "xpcom-shutdown") == 0) {
 | 
						|
    Shutdown();
 | 
						|
  } else if (strcmp(aTopic, "user-interaction-inactive") == 0) {
 | 
						|
    mInteracting = false;
 | 
						|
#ifdef MOZ_CRASHREPORTER
 | 
						|
    CrashReporter::SetInactiveStateStart();
 | 
						|
#endif
 | 
						|
  } else if (strcmp(aTopic, "user-interaction-active") == 0) {
 | 
						|
    mInteracting = true;
 | 
						|
#ifdef MOZ_CRASHREPORTER
 | 
						|
    CrashReporter::ClearInactiveStateStart();
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
nsresult nsAvailableMemoryWatcherBase::RegisterTabUnloader(
 | 
						|
    nsITabUnloader* aTabUnloader) {
 | 
						|
  mTabUnloader = aTabUnloader;
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
// This method is called as a part of UnloadTabAsync(). Like Notify(), if we
 | 
						|
// call this method synchronously without releasing the lock first we can lead
 | 
						|
// to deadlock.
 | 
						|
nsresult nsAvailableMemoryWatcherBase::OnUnloadAttemptCompleted(
 | 
						|
    nsresult aResult) {
 | 
						|
  MutexAutoLock lock(mMutex);
 | 
						|
  switch (aResult) {
 | 
						|
    // A tab was unloaded successfully.
 | 
						|
    case NS_OK:
 | 
						|
      ++mNumOfTabUnloading;
 | 
						|
      break;
 | 
						|
 | 
						|
    // There was no unloadable tab.
 | 
						|
    case NS_ERROR_NOT_AVAILABLE:
 | 
						|
      ++mNumOfMemoryPressure;
 | 
						|
      NS_NotifyOfEventualMemoryPressure(MemoryPressureState::LowMemory);
 | 
						|
      break;
 | 
						|
 | 
						|
    // There was a pending task to unload a tab.
 | 
						|
    case NS_ERROR_ABORT:
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unexpected aResult");
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void nsAvailableMemoryWatcherBase::UpdateLowMemoryTimeStamp() {
 | 
						|
  if (mLowMemoryStart.IsNull()) {
 | 
						|
    mLowMemoryStart = TimeStamp::NowLoRes();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void nsAvailableMemoryWatcherBase::RecordTelemetryEventOnHighMemory(
 | 
						|
    const MutexAutoLock&) {
 | 
						|
  Telemetry::SetEventRecordingEnabled("memory_watcher"_ns, true);
 | 
						|
  Telemetry::RecordEvent(
 | 
						|
      Telemetry::EventID::Memory_watcher_OnHighMemory_Stats,
 | 
						|
      Some(nsPrintfCString(
 | 
						|
          "%u,%u,%f", mNumOfTabUnloading, mNumOfMemoryPressure,
 | 
						|
          (TimeStamp::NowLoRes() - mLowMemoryStart).ToSeconds())),
 | 
						|
      Nothing());
 | 
						|
  mNumOfTabUnloading = mNumOfMemoryPressure = 0;
 | 
						|
  mLowMemoryStart = TimeStamp();
 | 
						|
}
 | 
						|
 | 
						|
// Define the fallback method for a platform for which a platform-specific
 | 
						|
// CreateAvailableMemoryWatcher() is not defined.
 | 
						|
#if defined(ANDROID) || \
 | 
						|
    !defined(XP_WIN) && !defined(XP_DARWIN) && !defined(XP_LINUX)
 | 
						|
already_AddRefed<nsAvailableMemoryWatcherBase> CreateAvailableMemoryWatcher() {
 | 
						|
  RefPtr instance(new nsAvailableMemoryWatcherBase);
 | 
						|
  return do_AddRef(instance);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
}  // namespace mozilla
 |