// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/memory_pressure/multi_source_memory_pressure_monitor.h"

#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/time/time.h"
#include "base/trace_event/memory_pressure_level_proto.h"
#include "base/trace_event/trace_event.h"
#include "base/tracing_buildflags.h"
#include "build/build_config.h"
#include "components/memory_pressure/system_memory_pressure_evaluator.h"

namespace memory_pressure {

namespace {

MultiSourceMemoryPressureMonitor* g_monitor = nullptr;

}  // namespace

MultiSourceMemoryPressureMonitor::MultiSourceMemoryPressureMonitor()
    : current_pressure_level_(base::MEMORY_PRESSURE_LEVEL_NONE),
      dispatch_callback_(base::BindRepeating(
          &base::MemoryPressureListener::NotifyMemoryPressure)),
      aggregator_(this),
      level_reporter_(current_pressure_level_,
                      "Memory.PressureLevel2",
#if BUILDFLAG(IS_MAC)
                      std::nullopt
#else
                      "Memory.PressureWindowDuration."
#endif
      ) {
  CHECK(!g_monitor);
  g_monitor = this;
}

MultiSourceMemoryPressureMonitor::~MultiSourceMemoryPressureMonitor() {
  // Destroy system evaluator early while the remaining members of this class
  // still exist. MultiSourceMemoryPressureMonitor implements
  // MemoryPressureVoteAggregator::Delegate, and
  // delegate_->OnMemoryPressureLevelChanged() gets indirectly called during
  // ~SystemMemoryPressureEvaluator().
  system_evaluator_.reset();

  CHECK_EQ(g_monitor, this);
  g_monitor = nullptr;
}

// static
MultiSourceMemoryPressureMonitor* MultiSourceMemoryPressureMonitor::Get() {
  return g_monitor;
}

void MultiSourceMemoryPressureMonitor::MaybeStartPlatformVoter() {
  system_evaluator_ =
      SystemMemoryPressureEvaluator::CreateDefaultSystemEvaluator(this);
}

std::unique_ptr<MemoryPressureVoter>
MultiSourceMemoryPressureMonitor::CreateVoter() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  return aggregator_.CreateVoter();
}

void MultiSourceMemoryPressureMonitor::UpdateDiskPressureState(
    bool new_is_disk_pressure,
    base::MemoryPressureLevel new_os_pressure_level) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  level_reporter_.UpdateDiskPressureState(new_is_disk_pressure,
                                          new_os_pressure_level);
}

void MultiSourceMemoryPressureMonitor::OnMemoryPressureLevelChanged(
    base::MemoryPressureLevel level) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK_NE(current_pressure_level_, level);

  level_reporter_.OnMemoryPressureLevelChanged(level);

  TRACE_EVENT_INSTANT(
      "base", "MultiSourceMemoryPressureMonitor::OnMemoryPressureLevelChanged",
      [&](perfetto::EventContext ctx) {
        auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
        auto* data = event->set_chrome_memory_pressure_notification();
        data->set_level(
            base::trace_event::MemoryPressureLevelToTraceEnum(level));
      });

  current_pressure_level_ = level;
}

void MultiSourceMemoryPressureMonitor::OnNotifyListenersRequested() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  dispatch_callback_.Run(current_pressure_level_);
}

void MultiSourceMemoryPressureMonitor::SetSystemEvaluator(
    std::unique_ptr<SystemMemoryPressureEvaluator> evaluator) {
  DCHECK(!system_evaluator_);
  system_evaluator_ = std::move(evaluator);
}

void MultiSourceMemoryPressureMonitor::SetDispatchCallbackForTesting(
    const DispatchCallback& callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  // Must be called before `Start()`.
  DCHECK(!system_evaluator_);
  dispatch_callback_ = callback;
}

}  // namespace memory_pressure
