blob: f2729e7770b4130dcbf999639dc50884adec533a [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/compute_pressure/compute_pressure_manager.h"
#include <tuple>
#include <utility>
#include "base/bind.h"
#include "base/check_op.h"
#include "base/feature_list.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "content/browser/compute_pressure/compute_pressure_host.h"
#include "content/browser/compute_pressure/compute_pressure_sample.h"
#include "content/browser/compute_pressure/compute_pressure_sampler.h"
#include "content/browser/compute_pressure/cpu_probe.h"
#include "content/public/browser/global_routing_id.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/compute_pressure/compute_pressure.mojom.h"
namespace content {
constexpr base::TimeDelta ComputePressureManager::kDefaultSamplingInterval;
// static
std::unique_ptr<ComputePressureManager> ComputePressureManager::Create() {
return std::make_unique<ComputePressureManager>(
CpuProbe::Create(), ComputePressureManager::kDefaultSamplingInterval,
ComputePressureHost::kDefaultVisibleObserverRateLimit,
base::PassKey<ComputePressureManager>());
}
// static
std::unique_ptr<ComputePressureManager>
ComputePressureManager::CreateForTesting(
std::unique_ptr<CpuProbe> cpu_probe,
base::TimeDelta sampling_interval,
base::TimeDelta visible_observer_rate_limit) {
return std::make_unique<ComputePressureManager>(
std::move(cpu_probe), sampling_interval, visible_observer_rate_limit,
base::PassKey<ComputePressureManager>());
}
ComputePressureManager::ComputePressureManager(
std::unique_ptr<CpuProbe> cpu_probe,
base::TimeDelta sampling_interval,
base::TimeDelta visible_observer_rate_limit,
base::PassKey<ComputePressureManager>)
// base::Unretained usage is safe here because the callback is only run
// while `sampler_` is alive, and `sampler_` is owned by this instance.
: sampler_(std::move(cpu_probe),
sampling_interval,
base::BindRepeating(&ComputePressureManager::UpdateObservers,
base::Unretained(this))),
visible_observer_rate_limit_(visible_observer_rate_limit) {}
ComputePressureManager::~ComputePressureManager() = default;
void ComputePressureManager::BindReceiver(
url::Origin origin,
GlobalFrameRoutingId frame_id,
mojo::PendingReceiver<blink::mojom::ComputePressureHost> receiver) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(frame_id);
if (!base::FeatureList::IsEnabled(blink::features::kComputePressure)) {
mojo::ReportBadMessage("Compute Pressure not enabled");
return;
}
if (!network::IsOriginPotentiallyTrustworthy(origin)) {
mojo::ReportBadMessage("Compute Pressure access from an insecure origin");
return;
}
auto it = hosts_by_origin_.lower_bound(origin);
if (it == hosts_by_origin_.end() || it->first != origin) {
// base::Unretained use is safe here because the callback will only be Run()
// while the ComputePressureHost instance is alive, and the
// ComputePressureHost instance is owned by this ComputePressureManager
// indirectly, via `hosts_by_origin_`.
it = hosts_by_origin_.emplace_hint(
it, std::piecewise_construct, std::forward_as_tuple(origin),
std::forward_as_tuple(
origin, sampler_.has_probe(), visible_observer_rate_limit_,
base::BindRepeating(
&ComputePressureManager::DidHostConnectionsChange,
base::Unretained(this))));
}
ComputePressureHost& host = it->second;
DCHECK_EQ(origin, host.origin());
host.BindReceiver(frame_id, std::move(receiver));
}
void ComputePressureManager::UpdateObservers(ComputePressureSample sample) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::Time sample_time = base::Time::Now();
for (auto& origin_and_host : hosts_by_origin_)
origin_and_host.second.UpdateObservers(sample, sample_time);
}
void ComputePressureManager::DidHostConnectionsChange(
ComputePressureHost* host) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(host);
DCHECK(hosts_by_origin_.count(host->origin()));
DCHECK_EQ(&hosts_by_origin_.at(host->origin()), host);
if (host->has_observers()) {
hosts_with_observers_.insert(host->origin());
sampler_.EnsureStarted();
} else {
hosts_with_observers_.erase(host->origin());
if (hosts_with_observers_.empty()) {
// If there are no observers left, we can save CPU cycles (and therefore
// power) by stopping the sampler.
sampler_.Stop();
}
if (!host->has_receivers()) {
// `host` is no longer valid after the erase.
hosts_by_origin_.erase(host->origin());
}
}
}
} // namespace content