Commit 2640bb42 authored by Patrick Monette's avatar Patrick Monette Committed by Commit Bot

[PM] Add priority and reason to ExecutionContext and its observer

Bug: 1077217
Change-Id: Ie0081edc73c8b8ed0669c0aacaad9ccbb2ead003
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2490800Reviewed-by: default avatarChris Hamilton <chrisha@chromium.org>
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Cr-Commit-Position: refs/heads/master@{#820317}
parent 5b367f02
......@@ -68,6 +68,13 @@ class ExecutionContextImpl : public ExecutionContext,
return node_->process_node();
}
// Returns the current priority of the execution context, and the reason for
// the execution context having that particular priority.
const PriorityAndReason& GetPriorityAndReason() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return node_->priority_and_reason();
}
const FrameNode* GetFrameNode() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (std::is_same<FrameNodeImpl, NodeImplType>::value)
......
......@@ -52,6 +52,12 @@ class DummyExecutionContextForLookup : public ExecutionContext {
return nullptr;
}
const PriorityAndReason& GetPriorityAndReason() const override {
NOTREACHED();
static const PriorityAndReason kPriorityAndReason;
return kPriorityAndReason;
}
const FrameNode* GetFrameNode() const override {
NOTREACHED();
return nullptr;
......@@ -149,6 +155,21 @@ ExecutionContextRegistryImpl::GetExecutionContextForWorkerNode(
return GetOrCreateExecutionContextForWorkerNode(worker_node);
}
void ExecutionContextRegistryImpl::OnPassedToGraph(Graph* graph) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(graph->IsEmpty());
graph->RegisterObject(this);
graph->AddFrameNodeObserver(this);
graph->AddWorkerNodeObserver(this);
}
void ExecutionContextRegistryImpl::OnTakenFromGraph(Graph* graph) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
graph->RemoveWorkerNodeObserver(this);
graph->RemoveFrameNodeObserver(this);
graph->UnregisterObject(this);
}
void ExecutionContextRegistryImpl::OnFrameNodeAdded(
const FrameNode* frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -171,19 +192,14 @@ void ExecutionContextRegistryImpl::OnBeforeFrameNodeRemoved(
DCHECK_EQ(1u, erased);
}
void ExecutionContextRegistryImpl::OnPassedToGraph(Graph* graph) {
void ExecutionContextRegistryImpl::OnPriorityAndReasonChanged(
const FrameNode* frame_node,
const PriorityAndReason& previous_value) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(graph->IsEmpty());
graph->RegisterObject(this);
graph->AddFrameNodeObserver(this);
graph->AddWorkerNodeObserver(this);
}
void ExecutionContextRegistryImpl::OnTakenFromGraph(Graph* graph) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
graph->RemoveWorkerNodeObserver(this);
graph->RemoveFrameNodeObserver(this);
graph->UnregisterObject(this);
auto* ec = GetOrCreateExecutionContextForFrameNode(frame_node);
DCHECK(ec);
for (auto& observer : observers_)
observer.OnPriorityAndReasonChanged(ec, previous_value);
}
void ExecutionContextRegistryImpl::OnWorkerNodeAdded(
......@@ -211,6 +227,16 @@ void ExecutionContextRegistryImpl::OnBeforeWorkerNodeRemoved(
DCHECK_EQ(1u, erased);
}
void ExecutionContextRegistryImpl::OnPriorityAndReasonChanged(
const WorkerNode* worker_node,
const PriorityAndReason& previous_value) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto* ec = GetOrCreateExecutionContextForWorkerNode(worker_node);
DCHECK(ec);
for (auto& observer : observers_)
observer.OnPriorityAndReasonChanged(ec, previous_value);
}
////////////////////////////////////////////////////////////////////////////////
// ExecutionContextRegistryImpl::ExecutionContextHash
......
......@@ -25,9 +25,9 @@ class ExecutionContext;
// to any nodes being created.
class ExecutionContextRegistryImpl
: public ExecutionContextRegistry,
public FrameNode::ObserverDefaultImpl,
public GraphOwned,
public GraphRegisteredImpl<ExecutionContextRegistryImpl>,
public FrameNode::ObserverDefaultImpl,
public WorkerNode::ObserverDefaultImpl {
public:
ExecutionContextRegistryImpl();
......@@ -56,17 +56,23 @@ class ExecutionContextRegistryImpl
}
private:
// FrameNode::ObserverDefaultImpl implementation:
void OnFrameNodeAdded(const FrameNode* frame_node) override;
void OnBeforeFrameNodeRemoved(const FrameNode* frame_node) override;
// GraphOwned implementation:
void OnPassedToGraph(Graph* graph) override;
void OnTakenFromGraph(Graph* graph) override;
// FrameNode::ObserverDefaultImpl implementation:
void OnFrameNodeAdded(const FrameNode* frame_node) override;
void OnBeforeFrameNodeRemoved(const FrameNode* frame_node) override;
void OnPriorityAndReasonChanged(
const FrameNode* frame_node,
const PriorityAndReason& previous_value) override;
// WorkerNode::ObserverDefaultImpl implementation:
void OnWorkerNodeAdded(const WorkerNode* worker_node) override;
void OnBeforeWorkerNodeRemoved(const WorkerNode* worker_node) override;
void OnPriorityAndReasonChanged(
const WorkerNode* worker_node,
const PriorityAndReason& previous_value) override;
// Maintains the collection of all currently known ExecutionContexts in the
// Graph. It is expected that there are O(100s) to O(1000s) of these being
......@@ -94,4 +100,4 @@ class ExecutionContextRegistryImpl
} // namespace execution_context
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_H_
\ No newline at end of file
#endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_H_
......@@ -35,6 +35,11 @@ class LenientMockExecutionContextObserver : public ExecutionContextObserver {
OnBeforeExecutionContextRemoved,
(const ExecutionContext*),
());
MOCK_METHOD(void,
OnPriorityAndReasonChanged,
(const ExecutionContext*,
const PriorityAndReason& previous_value),
());
};
using MockExecutionContextObserver =
testing::StrictMock<LenientMockExecutionContextObserver>;
......@@ -70,31 +75,16 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) {
// Ensure that the public getter works.
EXPECT_EQ(registry_, ExecutionContextRegistry::GetFromGraph(graph()));
// Create an observer.
MockExecutionContextObserver obs;
EXPECT_FALSE(registry_->HasObserver(&obs));
registry_->AddObserver(&obs);
EXPECT_TRUE(registry_->HasObserver(&obs));
// Create some mock nodes. This creates a graph with 1 page containing 2
// frames in 1 process.
std::vector<const ExecutionContext*> ecs;
EXPECT_CALL(obs, OnExecutionContextAdded(testing::_))
.Times(2)
.WillRepeatedly(
[&ecs](const ExecutionContext* ec) { ecs.push_back(ec); });
MockMultiplePagesInSingleProcessGraph mock_graph(graph());
// Only the frames are in the map at this point.
EXPECT_EQ(2u, ecs.size());
EXPECT_EQ(2u, registry_->GetExecutionContextCountForTesting());
// Creating a worker should create another entry in the map.
EXPECT_CALL(obs, OnExecutionContextAdded(testing::_))
.WillOnce([&ecs](const ExecutionContext* ec) { ecs.push_back(ec); });
auto worker_node = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, mock_graph.process.get());
EXPECT_EQ(3u, ecs.size());
EXPECT_EQ(3u, registry_->GetExecutionContextCountForTesting());
auto* frame1 = mock_graph.frame.get();
......@@ -106,11 +96,6 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) {
auto* frame2_ec = GetOrCreateExecutionContextForFrameNode(frame2);
auto* worker_ec = GetOrCreateExecutionContextForWorkerNode(worker);
// Expect them to match those that were seen by the observer.
EXPECT_EQ(ecs[0], frame1_ec);
EXPECT_EQ(ecs[1], frame2_ec);
EXPECT_EQ(ecs[2], worker_ec);
// Expect the FrameExecutionContext implementation to work.
EXPECT_EQ(ExecutionContextType::kFrameNode, frame1_ec->GetType());
EXPECT_EQ(frame1->frame_token().value(), frame1_ec->GetToken().value());
......@@ -154,15 +139,45 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) {
registry_->GetExecutionContextByToken(blink::ExecutionContextToken()));
EXPECT_FALSE(registry_->GetFrameNodeByFrameToken(blink::LocalFrameToken()));
EXPECT_FALSE(registry_->GetWorkerNodeByWorkerToken(blink::WorkerToken()));
}
TEST_F(ExecutionContextRegistryImplTest, Observers) {
// Create an observer.
MockExecutionContextObserver obs;
EXPECT_FALSE(registry_->HasObserver(&obs));
registry_->AddObserver(&obs);
EXPECT_TRUE(registry_->HasObserver(&obs));
// Create some mock nodes. This creates a graph with 1 page containing 1 frame
// and 1 worker in a single process.
EXPECT_CALL(obs, OnExecutionContextAdded(testing::_)).Times(2);
MockSinglePageWithFrameAndWorkerInSingleProcessGraph mock_graph(graph());
// The registry has 2 entries: the frame and the worker.
EXPECT_EQ(2u, registry_->GetExecutionContextCountForTesting());
auto* frame = mock_graph.frame.get();
auto* worker = mock_graph.worker.get();
// Get the execution contexts for each node directly.
auto* frame_ec = GetOrCreateExecutionContextForFrameNode(frame);
auto* worker_ec = GetOrCreateExecutionContextForWorkerNode(worker);
// Set the priority and reason of the frame and expect a notification.
EXPECT_CALL(obs, OnPriorityAndReasonChanged(frame_ec, testing::_));
frame->SetPriorityAndReason(
PriorityAndReason(base::TaskPriority::HIGHEST, "frame reason"));
// Set the priority and reason of the worker and expect a notification.
EXPECT_CALL(obs, OnPriorityAndReasonChanged(worker_ec, testing::_));
worker->SetPriorityAndReason(
PriorityAndReason(base::TaskPriority::HIGHEST, "worker reason"));
// Destroy nodes one by one and expect observer notifications.
EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(worker_ec));
worker_node.reset();
EXPECT_EQ(2u, registry_->GetExecutionContextCountForTesting());
EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(frame2_ec));
mock_graph.other_frame.reset();
mock_graph.DeleteWorker();
EXPECT_EQ(1u, registry_->GetExecutionContextCountForTesting());
EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(frame1_ec));
EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(frame_ec));
mock_graph.frame.reset();
EXPECT_EQ(0u, registry_->GetExecutionContextCountForTesting());
......
......@@ -6,6 +6,7 @@
#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_H_
#include "base/observer_list_types.h"
#include "components/performance_manager/public/execution_context_priority/execution_context_priority.h"
#include "third_party/blink/public/common/tokens/tokens.h"
class GURL;
......@@ -17,6 +18,8 @@ class Graph;
class ProcessNode;
class WorkerNode;
using execution_context_priority::PriorityAndReason;
namespace execution_context {
class ExecutionContextObserver;
......@@ -73,6 +76,10 @@ class ExecutionContext {
// ExecutionContext is hosted. This will never return nullptr.
virtual const ProcessNode* GetProcessNode() const = 0;
// Returns the current priority of the execution context, and the reason for
// the execution context having that particular priority.
virtual const PriorityAndReason& GetPriorityAndReason() const = 0;
// Returns the underlying FrameNode, if this context is a FrameNode, or
// nullptr otherwise.
virtual const FrameNode* GetFrameNode() const = 0;
......@@ -97,6 +104,11 @@ class ExecutionContextObserver : public base::CheckedObserver {
// Called when an ExecutionContext is about to be removed. The pointer |ec|
// becomes invalid immediately after this returns.
virtual void OnBeforeExecutionContextRemoved(const ExecutionContext* ec) = 0;
// Invoked when the execution context priority and reason changes.
virtual void OnPriorityAndReasonChanged(
const ExecutionContext* ec,
const PriorityAndReason& previous_value) = 0;
};
// A default implementation of ExecutionContextObserver with empty stubs for all
......@@ -113,6 +125,9 @@ class ExecutionContextObserverDefaultImpl : public ExecutionContextObserver {
// ExecutionContextObserver implementation:
void OnExecutionContextAdded(const ExecutionContext* ec) override {}
void OnBeforeExecutionContextRemoved(const ExecutionContext* ec) override {}
void OnPriorityAndReasonChanged(
const ExecutionContext* ec,
const PriorityAndReason& previous_value) override {}
};
// Helper function for converting from a WorkerToken to an
......
......@@ -87,4 +87,26 @@ MockMultiplePagesWithMultipleProcessesGraph::
MockMultiplePagesWithMultipleProcessesGraph::
~MockMultiplePagesWithMultipleProcessesGraph() = default;
MockSinglePageWithFrameAndWorkerInSingleProcessGraph::
MockSinglePageWithFrameAndWorkerInSingleProcessGraph(TestGraphImpl* graph)
: MockSinglePageInSingleProcessGraph(graph),
worker(TestNodeWrapper<WorkerNodeImpl>::Create(
graph,
WorkerNode::WorkerType::kDedicated,
process.get())) {
worker->AddClientFrame(frame.get());
}
MockSinglePageWithFrameAndWorkerInSingleProcessGraph::
~MockSinglePageWithFrameAndWorkerInSingleProcessGraph() {
if (worker.get())
worker->RemoveClientFrame(frame.get());
}
void MockSinglePageWithFrameAndWorkerInSingleProcessGraph::DeleteWorker() {
DCHECK(worker.get());
worker->RemoveClientFrame(frame.get());
worker.reset();
}
} // namespace performance_manager
......@@ -116,6 +116,30 @@ struct MockMultiplePagesWithMultipleProcessesGraph
TestNodeWrapper<FrameNodeImpl> child_frame;
};
// The following graph topology is created to emulate a scenario where a page
// contains a single frame that creates a single dedicated worker.
//
// Pg Pr_
// \ / |
// F |
// \ |
// W__|
//
// Where:
// Pg: page
// F: frame(frame_tree_id:0)
// W: worker
// Pr: process(pid:1)
struct MockSinglePageWithFrameAndWorkerInSingleProcessGraph
: public MockSinglePageInSingleProcessGraph {
explicit MockSinglePageWithFrameAndWorkerInSingleProcessGraph(
TestGraphImpl* graph);
~MockSinglePageWithFrameAndWorkerInSingleProcessGraph();
TestNodeWrapper<WorkerNodeImpl> worker;
void DeleteWorker();
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_TEST_SUPPORT_MOCK_GRAPHS_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment