blob: 60bb0be30ad3bc0a4215127c6d9bdd838fd76a90 [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2020 The Chromium Authors
Sebastien Marchand1a8a708c2020-12-17 16:00:182// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/performance_manager/public/freezing/freezing.h"
6
Sebastien Marchandd7de0572021-03-09 17:10:557#include <memory>
8
Sebastien Marchand1a8a708c2020-12-17 16:00:189#include "base/bind.h"
Sebastien Marchandd7de0572021-03-09 17:10:5510#include "base/containers/contains.h"
11#include "base/containers/flat_map.h"
12#include "base/containers/flat_set.h"
13#include "base/memory/ptr_util.h"
Keishi Hattori0e45c022021-11-27 09:25:5214#include "base/memory/raw_ptr.h"
Patrick Monette490a96da2021-02-18 02:43:4615#include "base/scoped_observation.h"
Sebastien Marchand1a8a708c2020-12-17 16:00:1816#include "base/sequence_checker.h"
Patrick Monette643cdf62021-10-15 19:13:4217#include "base/task/sequenced_task_runner.h"
Sebastien Marchandd7de0572021-03-09 17:10:5518#include "base/thread_annotations.h"
Sebastien Marchand1a8a708c2020-12-17 16:00:1819#include "components/performance_manager/freezing/freezing_vote_aggregator.h"
Sebastien Marchandd7de0572021-03-09 17:10:5520#include "components/performance_manager/graph/graph_impl.h"
21#include "components/performance_manager/graph/node_attached_data_impl.h"
22#include "components/performance_manager/graph/page_node_impl.h"
Sebastien Marchand1a8a708c2020-12-17 16:00:1823#include "components/performance_manager/performance_manager_impl.h"
Sebastien Marchandd7de0572021-03-09 17:10:5524#include "components/performance_manager/public/graph/graph.h"
25#include "components/performance_manager/public/graph/graph_registered.h"
Sebastien Marchand1a8a708c2020-12-17 16:00:1826#include "components/performance_manager/public/graph/page_node.h"
27#include "components/performance_manager/public/performance_manager.h"
Sebastien Marchandd7de0572021-03-09 17:10:5528#include "content/public/browser/browser_thread.h"
Sebastien Marchand1a8a708c2020-12-17 16:00:1829#include "content/public/browser/web_contents.h"
30
31namespace performance_manager {
32
33namespace freezing {
34
35namespace {
36
Sebastien Marchandd7de0572021-03-09 17:10:5537class FreezingVoteTokenImpl;
Sebastien Marchand1a8a708c2020-12-17 16:00:1838
Sebastien Marchandd7de0572021-03-09 17:10:5539// NodeAttachedData used to store the set of FreezingVoteTokenImpl objects
40// associated with a PageNode.
41class FreezingVoteNodeData : public NodeAttachedDataImpl<FreezingVoteNodeData> {
42 public:
43 struct Traits : public NodeAttachedDataInMap<PageNodeImpl> {};
44
45 ~FreezingVoteNodeData() override = default;
46
47 void AddVote(FreezingVoteTokenImpl* token);
48 void RemoveVote(FreezingVoteTokenImpl* token);
49 bool IsEmpty() { return vote_tokens_.empty(); }
50 const base::flat_set<FreezingVoteTokenImpl*>& vote_tokens() {
51 return vote_tokens_;
52 }
53
54 private:
55 friend class ::performance_manager::NodeAttachedDataImpl<
56 FreezingVoteNodeData>;
57 explicit FreezingVoteNodeData(const PageNodeImpl* page_node) {}
58
59 // The freezing votes associated with this node.
60 base::flat_set<FreezingVoteTokenImpl*> vote_tokens_;
61};
62
63// A registry of FreezingVoteToken that lives on the PM sequence.
64//
65// There can be multiple freezing votes associated with the same page node.
66class FreezingVoteTokenPMRegistry
67 : public PageNode::ObserverDefaultImpl,
68 public GraphOwned,
69 public GraphRegisteredImpl<FreezingVoteTokenPMRegistry> {
70 public:
71 // A map that associates a voting token to a
72 // <FreezingVotingChannel, const PageNode*> pair.
73 using VotingChannelsMap =
74 base::flat_map<FreezingVoteTokenImpl*,
75 std::pair<FreezingVotingChannel, const PageNode*>>;
76
77 // Returns the FreezingVoteTokenPMRegistry graph owned instance, creates it if
78 // necessary. Can only be called from the PM sequence.
79 static FreezingVoteTokenPMRegistry* GetOrCreateInstance(Graph* graph);
80
81 FreezingVoteTokenPMRegistry(const FreezingVoteTokenPMRegistry& other) =
82 delete;
83 FreezingVoteTokenPMRegistry& operator=(const FreezingVoteTokenPMRegistry&) =
84 delete;
85
86 // Register a freezing vote for |contents|. |token| is an ID to associate with
87 // this vote, there can be only one vote associated with this ID and it as to
88 // be passed |UnregisterVote| when the vote is invalidated. This can only be
89 // called from the UI thread.
90 static void RegisterVoteForWebContents(content::WebContents* contents,
91 FreezingVoteValue vote_value,
92 const char* vote_reason,
93 FreezingVoteTokenImpl* token);
94
95 // Unregister the vote associated with |token|. This can only be called from
96 // the UI thread.
97 static void UnregisterVote(FreezingVoteTokenImpl* token);
98
99 const VotingChannelsMap& voting_channels_for_testing() {
100 return voting_channels_;
101 }
102
103 private:
104 FreezingVoteTokenPMRegistry() = default;
105
106 // Register a vote for |page_node| on the PM sequence. |token| is an ID
107 // associated with this vote that will be used when invalidating it.
108 void RegisterVoteOnPMSequence(base::WeakPtr<PageNode> page_node,
109 FreezingVote vote,
110 FreezingVoteTokenImpl* token);
111
112 // Unregister the vote associated with |token|.
113 void UnregisterVoteOnPMSequence(FreezingVoteTokenImpl* token);
Patrick Monette490a96da2021-02-18 02:43:46114
Sebastien Marchand1a8a708c2020-12-17 16:00:18115 // PageNodeObserver:
116 void OnBeforePageNodeRemoved(const PageNode* page_node) override;
117
Sebastien Marchandd7de0572021-03-09 17:10:55118 // GraphOwned:
119 void OnPassedToGraph(Graph* graph) override;
120 void OnTakenFromGraph(Graph* graph) override;
Sebastien Marchand1a8a708c2020-12-17 16:00:18121
Sebastien Marchandd7de0572021-03-09 17:10:55122 // Reset the voting channel associated with |voting_channel_iter| and remove
123 // this entry from |voting_channels_|. |voting_channel_iter| has to be a valid
124 // iterator from |voting_channels_|
125 void ResetAndRemoveVotingChannel(
126 VotingChannelsMap::iterator& voting_channel_iter,
127 const PageNode* page_node);
Patrick Monette490a96da2021-02-18 02:43:46128
Sebastien Marchandd7de0572021-03-09 17:10:55129 VotingChannelsMap voting_channels_ GUARDED_BY_CONTEXT(sequence_checker_);
Patrick Monette490a96da2021-02-18 02:43:46130
Keishi Hattori0e45c022021-11-27 09:25:52131 raw_ptr<Graph> graph_ GUARDED_BY_CONTEXT(sequence_checker_);
Sebastien Marchand1a8a708c2020-12-17 16:00:18132
133 SEQUENCE_CHECKER(sequence_checker_);
134};
135
136// Concrete implementation of a FreezingVoteToken.
137class FreezingVoteTokenImpl : public FreezingVoteToken {
138 public:
Sebastien Marchandd7de0572021-03-09 17:10:55139 FreezingVoteTokenImpl(content::WebContents* contents,
Sebastien Marchand1a8a708c2020-12-17 16:00:18140 FreezingVoteValue vote_value,
141 const char* vote_reason);
142 ~FreezingVoteTokenImpl() override;
143 FreezingVoteTokenImpl(const FreezingVoteTokenImpl& other) = delete;
144 FreezingVoteTokenImpl& operator=(const FreezingVoteTokenImpl&) = delete;
Sebastien Marchand1a8a708c2020-12-17 16:00:18145};
146
147} // namespace
148
149FreezingVoteToken::FreezingVoteToken() = default;
150FreezingVoteToken::~FreezingVoteToken() = default;
151
Sebastien Marchandd7de0572021-03-09 17:10:55152void FreezingVoteNodeData::AddVote(FreezingVoteTokenImpl* token) {
153 DCHECK(!base::Contains(vote_tokens_, token));
154 vote_tokens_.insert(token);
155}
156
157void FreezingVoteNodeData::RemoveVote(FreezingVoteTokenImpl* token) {
158 DCHECK(base::Contains(vote_tokens_, token));
159 vote_tokens_.erase(token);
160}
161
162// static
163FreezingVoteTokenPMRegistry* FreezingVoteTokenPMRegistry::GetOrCreateInstance(
164 Graph* graph) {
165 DCHECK(PerformanceManager::GetTaskRunner()->RunsTasksInCurrentSequence());
166 auto* instance = graph->GetRegisteredObjectAs<FreezingVoteTokenPMRegistry>();
167 if (!instance) {
168 auto registry = base::WrapUnique(new FreezingVoteTokenPMRegistry());
169 instance = registry.get();
170 graph->PassToGraph(std::move(registry));
171 }
172 return instance;
173}
174
175// static
176void FreezingVoteTokenPMRegistry::RegisterVoteForWebContents(
177 content::WebContents* contents,
178 FreezingVoteValue vote_value,
179 const char* vote_reason,
180 FreezingVoteTokenImpl* token) {
181 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Sebastien Marchand1a8a708c2020-12-17 16:00:18182 // Register the vote on the PM sequence.
183 PerformanceManager::CallOnGraph(
184 FROM_HERE,
185 base::BindOnce(
Sebastien Marchandd7de0572021-03-09 17:10:55186 [](base::WeakPtr<PageNode> page_node, FreezingVote vote,
187 FreezingVoteTokenImpl* token, Graph* graph) {
188 auto* registry =
189 FreezingVoteTokenPMRegistry::GetOrCreateInstance(graph);
190 registry->RegisterVoteOnPMSequence(page_node, vote, token);
191 },
Chris Hamiltonc763a552021-06-21 17:10:59192 PerformanceManager::GetPrimaryPageNodeForWebContents(contents),
Sebastien Marchandd7de0572021-03-09 17:10:55193 FreezingVote(vote_value, vote_reason), token));
Sebastien Marchand1a8a708c2020-12-17 16:00:18194}
195
Sebastien Marchandd7de0572021-03-09 17:10:55196// static
197void FreezingVoteTokenPMRegistry::UnregisterVote(FreezingVoteTokenImpl* token) {
198 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
199 // Unregister the vote on the PM sequence.
200 PerformanceManager::CallOnGraph(
201 FROM_HERE,
202 base::BindOnce(
203 [](FreezingVoteTokenImpl* token, Graph* graph) {
204 auto* registry =
205 FreezingVoteTokenPMRegistry::GetOrCreateInstance(graph);
206 registry->UnregisterVoteOnPMSequence(token);
207 },
208 token));
Patrick Monette490a96da2021-02-18 02:43:46209}
210
Sebastien Marchandd7de0572021-03-09 17:10:55211void FreezingVoteTokenPMRegistry::RegisterVoteOnPMSequence(
Patrick Monette490a96da2021-02-18 02:43:46212 base::WeakPtr<PageNode> page_node,
213 FreezingVote vote,
Sebastien Marchandd7de0572021-03-09 17:10:55214 FreezingVoteTokenImpl* token) {
Patrick Monette490a96da2021-02-18 02:43:46215 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sebastien Marchandd7de0572021-03-09 17:10:55216 DCHECK(page_node);
Patrick Monette490a96da2021-02-18 02:43:46217
Sebastien Marchandd7de0572021-03-09 17:10:55218 DCHECK(!base::Contains(voting_channels_, token));
Patrick Monette490a96da2021-02-18 02:43:46219
Sebastien Marchandd7de0572021-03-09 17:10:55220 auto* node_data = FreezingVoteNodeData::GetOrCreate(
221 PageNodeImpl::FromNode(page_node.get()));
Patrick Monette490a96da2021-02-18 02:43:46222
Sebastien Marchandd7de0572021-03-09 17:10:55223 auto voting_channel = graph_->GetRegisteredObjectAs<FreezingVoteAggregator>()
224 ->GetVotingChannel();
225 voting_channel.SubmitVote(page_node.get(), vote);
226 voting_channels_[token] =
227 std::make_pair(std::move(voting_channel), page_node.get());
228
229 node_data->AddVote(token);
Sebastien Marchand1a8a708c2020-12-17 16:00:18230}
231
Sebastien Marchandd7de0572021-03-09 17:10:55232void FreezingVoteTokenPMRegistry::UnregisterVoteOnPMSequence(
233 FreezingVoteTokenImpl* token) {
234 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
235 auto voting_channel = voting_channels_.find(token);
236 // The vote might be missing from |voting_channels_| if this gets called
237 // after the corresponding PageNode has been destroyed.
238 if (voting_channel == voting_channels_.end()) {
239 return;
240 }
241
242 auto* page_node = voting_channel->second.second;
243 ResetAndRemoveVotingChannel(voting_channel, page_node);
244
245 auto* node_data =
246 FreezingVoteNodeData::Get(PageNodeImpl::FromNode(page_node));
247 DCHECK(node_data);
248 node_data->RemoveVote(token);
249
250 // Removes the node attached data if there's no more vote associated with this
251 // node.
252 if (node_data->IsEmpty())
253 FreezingVoteNodeData::Destroy(PageNodeImpl::FromNode(page_node));
254}
255
256void FreezingVoteTokenPMRegistry::OnBeforePageNodeRemoved(
Sebastien Marchand1a8a708c2020-12-17 16:00:18257 const PageNode* page_node) {
258 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Patrick Monette490a96da2021-02-18 02:43:46259
Sebastien Marchandd7de0572021-03-09 17:10:55260 auto* node_data =
261 FreezingVoteNodeData::Get(PageNodeImpl::FromNode(page_node));
262
263 if (!node_data)
Patrick Monette490a96da2021-02-18 02:43:46264 return;
265
Sebastien Marchandd7de0572021-03-09 17:10:55266 // Invalidate the votes if its associated page node is destroyed. This can
Patrick Monette490a96da2021-02-18 02:43:46267 // happen if a freezing vote token is released after the destruction of the
268 // WebContents it's associated with.
Sebastien Marchandd7de0572021-03-09 17:10:55269 for (const auto* token_iter : node_data->vote_tokens()) {
270 auto voting_channel = voting_channels_.find(token_iter);
271 DCHECK(voting_channel != voting_channels_.end());
272 ResetAndRemoveVotingChannel(voting_channel, page_node);
273 }
Patrick Monette490a96da2021-02-18 02:43:46274}
275
Sebastien Marchandd7de0572021-03-09 17:10:55276void FreezingVoteTokenPMRegistry::OnPassedToGraph(Graph* graph) {
277 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
278 graph->RegisterObject(this);
279 graph->AddPageNodeObserver(this);
280 graph_ = graph;
Sebastien Marchand1a8a708c2020-12-17 16:00:18281}
282
Sebastien Marchandd7de0572021-03-09 17:10:55283void FreezingVoteTokenPMRegistry::OnTakenFromGraph(Graph* graph) {
284 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
285 graph->UnregisterObject(this);
286 graph->RemovePageNodeObserver(this);
287 graph_ = nullptr;
288}
289
290void FreezingVoteTokenPMRegistry::ResetAndRemoveVotingChannel(
291 VotingChannelsMap::iterator& voting_channel_iter,
292 const PageNode* page_node) {
293 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
294 voting_channel_iter->second.first.InvalidateVote(page_node);
295 voting_channel_iter->second.first.Reset();
296 voting_channels_.erase(voting_channel_iter);
297}
298
299FreezingVoteTokenImpl::FreezingVoteTokenImpl(content::WebContents* contents,
Sebastien Marchand1a8a708c2020-12-17 16:00:18300 FreezingVoteValue vote_value,
Sebastien Marchandd7de0572021-03-09 17:10:55301 const char* vote_reason) {
302 FreezingVoteTokenPMRegistry::RegisterVoteForWebContents(contents, vote_value,
303 vote_reason, this);
Sebastien Marchand1a8a708c2020-12-17 16:00:18304}
305
Sebastien Marchandd7de0572021-03-09 17:10:55306FreezingVoteTokenImpl::~FreezingVoteTokenImpl() {
307 FreezingVoteTokenPMRegistry::UnregisterVote(this);
308}
Sebastien Marchand1a8a708c2020-12-17 16:00:18309
310std::unique_ptr<FreezingVoteToken> EmitFreezingVoteForWebContents(
Sebastien Marchandd7de0572021-03-09 17:10:55311 content::WebContents* contents,
Sebastien Marchand1a8a708c2020-12-17 16:00:18312 FreezingVoteValue vote_value,
313 const char* vote_reason) {
Sebastien Marchandd7de0572021-03-09 17:10:55314 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
315 return std::make_unique<FreezingVoteTokenImpl>(contents, vote_value,
Sebastien Marchand1a8a708c2020-12-17 16:00:18316 vote_reason);
317}
318
Sebastien Marchand24ac6202020-12-17 16:13:51319const char* FreezingVoteValueToString(FreezingVoteValue freezing_vote_value) {
320 if (freezing_vote_value == freezing::FreezingVoteValue::kCanFreeze) {
321 return "kCanFreeze";
322 } else {
323 return "kCannotFreeze";
324 }
325}
326
Sebastien Marchandd7de0572021-03-09 17:10:55327size_t FreezingVoteCountForPageOnPMForTesting(PageNode* page_node) {
328 DCHECK(PerformanceManager::GetTaskRunner()->RunsTasksInCurrentSequence());
329
330 auto* node_data =
331 FreezingVoteNodeData::Get(PageNodeImpl::FromNode(page_node));
332
333 if (!node_data)
334 return 0;
335
336 return node_data->vote_tokens().size();
337}
338
339size_t TotalFreezingVoteCountOnPMForTesting(Graph* graph) {
340 DCHECK(PerformanceManager::GetTaskRunner()->RunsTasksInCurrentSequence());
341
342 auto* registry = FreezingVoteTokenPMRegistry::GetOrCreateInstance(graph);
343
344 size_t registry_size = registry->voting_channels_for_testing().size();
345 size_t page_nodes_vote_count = 0U;
346
347 for (const PageNode* page_node : graph->GetAllPageNodes()) {
348 auto* node_data =
349 FreezingVoteNodeData::Get(PageNodeImpl::FromNode(page_node));
350 if (node_data)
351 page_nodes_vote_count += node_data->vote_tokens().size();
352 }
353
354 DCHECK_EQ(registry_size, page_nodes_vote_count);
355
356 return registry_size;
357}
358
Sebastien Marchand1a8a708c2020-12-17 16:00:18359} // namespace freezing
Patrick Monette490a96da2021-02-18 02:43:46360} // namespace performance_manager