1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/performance_manager/policies/urgent_page_discarding_policy.h"
#include <memory>
#include "base/feature_list.h"
#include "base/time/time.h"
#include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
#include "chrome/browser/performance_manager/policies/policy_features.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "base/metrics/histogram_macros.h"
#include "chromeos/ash/components/memory/pressure/system_memory_pressure_evaluator.h"
#endif
namespace performance_manager::policies {
namespace {
bool g_disabled_for_testing = false;
#if BUILDFLAG(IS_CHROMEOS)
std::optional<memory_pressure::ReclaimTarget> GetReclaimTarget() {
std::optional<memory_pressure::ReclaimTarget> reclaim_target = std::nullopt;
auto* evaluator = ash::memory::SystemMemoryPressureEvaluator::Get();
if (evaluator) {
reclaim_target = evaluator->GetCachedReclaimTarget();
}
return reclaim_target;
}
#endif // BUILDFLAG(IS_CHROMEOS)
} // namespace
UrgentPageDiscardingPolicy::UrgentPageDiscardingPolicy() = default;
UrgentPageDiscardingPolicy::~UrgentPageDiscardingPolicy() = default;
void UrgentPageDiscardingPolicy::OnPassedToGraph(Graph* graph) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!handling_memory_pressure_notification_);
graph->AddSystemNodeObserver(this);
DCHECK(PageDiscardingHelper::GetFromGraph(graph))
<< "A PageDiscardingHelper instance should be registered against the "
"graph in order to use this policy.";
}
void UrgentPageDiscardingPolicy::OnTakenFromGraph(Graph* graph) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
graph->RemoveSystemNodeObserver(this);
}
#if BUILDFLAG(IS_CHROMEOS)
void UrgentPageDiscardingPolicy::OnReclaimTarget(
base::TimeTicks on_memory_pressure_at,
std::optional<memory_pressure::ReclaimTarget> reclaim_target) {
bool discard_protected_pages = true;
std::optional<base::TimeTicks> origin_time = std::nullopt;
if (reclaim_target) {
discard_protected_pages = reclaim_target->discard_protected;
origin_time = reclaim_target->origin_time;
}
std::optional<base::TimeTicks> first_discarded_at =
PageDiscardingHelper::GetFromGraph(GetOwningGraph())
->DiscardMultiplePages(
reclaim_target, discard_protected_pages,
DiscardEligibilityPolicy::DiscardReason::URGENT);
DCHECK(handling_memory_pressure_notification_);
handling_memory_pressure_notification_ = false;
if (origin_time && first_discarded_at) {
base::TimeDelta reclaim_arrival_duration =
on_memory_pressure_at - *origin_time;
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES("Discarding.ReclaimArrivalLatency",
reclaim_arrival_duration);
base::TimeDelta discard_duration =
*first_discarded_at - on_memory_pressure_at;
DEPRECATED_UMA_HISTOGRAM_MEDIUM_TIMES("Discarding.DiscardLatency",
discard_duration);
}
}
#endif // BUILDFLAG(IS_CHROMEOS)
void UrgentPageDiscardingPolicy::DisableForTesting() {
g_disabled_for_testing = true;
}
void UrgentPageDiscardingPolicy::OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel new_level) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (g_disabled_for_testing) {
return;
}
// The Memory Pressure Monitor will send notifications at regular interval,
// |handling_memory_pressure_notification_| prevents this class from trying to
// reply to multiple notifications at the same time.
if (handling_memory_pressure_notification_ ||
new_level != base::MemoryPressureListener::MemoryPressureLevel::
MEMORY_PRESSURE_LEVEL_CRITICAL) {
return;
}
// Don't discard a page if urgent discarding is disabled. The feature state is
// checked here instead of at policy creation time so that only clients that
// experience memory pressure are enrolled in the experiment.
if (!base::FeatureList::IsEnabled(
performance_manager::features::kUrgentPageDiscarding)) {
return;
}
handling_memory_pressure_notification_ = true;
#if BUILDFLAG(IS_CHROMEOS)
base::TimeTicks on_memory_pressure_at = base::TimeTicks::Now();
// Chrome OS memory pressure evaluator provides the memory reclaim target to
// leave critical memory pressure. When Chrome OS is under heavy memory
// pressure, discards multiple tabs to meet the memory reclaim target.
content::GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(GetReclaimTarget),
base::BindOnce(&UrgentPageDiscardingPolicy::OnReclaimTarget,
base::Unretained(this), on_memory_pressure_at));
#else
PageDiscardingHelper::GetFromGraph(GetOwningGraph())
->DiscardAPage(DiscardEligibilityPolicy::DiscardReason::URGENT);
DCHECK(handling_memory_pressure_notification_);
handling_memory_pressure_notification_ = false;
#endif // BUILDFLAG(IS_CHROMEOS)
}
} // namespace performance_manager::policies
|