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, ...@@ -68,6 +68,13 @@ class ExecutionContextImpl : public ExecutionContext,
return node_->process_node(); 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 { const FrameNode* GetFrameNode() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (std::is_same<FrameNodeImpl, NodeImplType>::value) if (std::is_same<FrameNodeImpl, NodeImplType>::value)
......
...@@ -52,6 +52,12 @@ class DummyExecutionContextForLookup : public ExecutionContext { ...@@ -52,6 +52,12 @@ class DummyExecutionContextForLookup : public ExecutionContext {
return nullptr; return nullptr;
} }
const PriorityAndReason& GetPriorityAndReason() const override {
NOTREACHED();
static const PriorityAndReason kPriorityAndReason;
return kPriorityAndReason;
}
const FrameNode* GetFrameNode() const override { const FrameNode* GetFrameNode() const override {
NOTREACHED(); NOTREACHED();
return nullptr; return nullptr;
...@@ -149,6 +155,21 @@ ExecutionContextRegistryImpl::GetExecutionContextForWorkerNode( ...@@ -149,6 +155,21 @@ ExecutionContextRegistryImpl::GetExecutionContextForWorkerNode(
return GetOrCreateExecutionContextForWorkerNode(worker_node); 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( void ExecutionContextRegistryImpl::OnFrameNodeAdded(
const FrameNode* frame_node) { const FrameNode* frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
...@@ -171,19 +192,14 @@ void ExecutionContextRegistryImpl::OnBeforeFrameNodeRemoved( ...@@ -171,19 +192,14 @@ void ExecutionContextRegistryImpl::OnBeforeFrameNodeRemoved(
DCHECK_EQ(1u, erased); 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_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(graph->IsEmpty()); auto* ec = GetOrCreateExecutionContextForFrameNode(frame_node);
graph->RegisterObject(this); DCHECK(ec);
graph->AddFrameNodeObserver(this); for (auto& observer : observers_)
graph->AddWorkerNodeObserver(this); observer.OnPriorityAndReasonChanged(ec, previous_value);
}
void ExecutionContextRegistryImpl::OnTakenFromGraph(Graph* graph) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
graph->RemoveWorkerNodeObserver(this);
graph->RemoveFrameNodeObserver(this);
graph->UnregisterObject(this);
} }
void ExecutionContextRegistryImpl::OnWorkerNodeAdded( void ExecutionContextRegistryImpl::OnWorkerNodeAdded(
...@@ -211,6 +227,16 @@ void ExecutionContextRegistryImpl::OnBeforeWorkerNodeRemoved( ...@@ -211,6 +227,16 @@ void ExecutionContextRegistryImpl::OnBeforeWorkerNodeRemoved(
DCHECK_EQ(1u, erased); 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 // ExecutionContextRegistryImpl::ExecutionContextHash
......
...@@ -25,9 +25,9 @@ class ExecutionContext; ...@@ -25,9 +25,9 @@ class ExecutionContext;
// to any nodes being created. // to any nodes being created.
class ExecutionContextRegistryImpl class ExecutionContextRegistryImpl
: public ExecutionContextRegistry, : public ExecutionContextRegistry,
public FrameNode::ObserverDefaultImpl,
public GraphOwned, public GraphOwned,
public GraphRegisteredImpl<ExecutionContextRegistryImpl>, public GraphRegisteredImpl<ExecutionContextRegistryImpl>,
public FrameNode::ObserverDefaultImpl,
public WorkerNode::ObserverDefaultImpl { public WorkerNode::ObserverDefaultImpl {
public: public:
ExecutionContextRegistryImpl(); ExecutionContextRegistryImpl();
...@@ -56,17 +56,23 @@ class ExecutionContextRegistryImpl ...@@ -56,17 +56,23 @@ class ExecutionContextRegistryImpl
} }
private: private:
// FrameNode::ObserverDefaultImpl implementation:
void OnFrameNodeAdded(const FrameNode* frame_node) override;
void OnBeforeFrameNodeRemoved(const FrameNode* frame_node) override;
// GraphOwned implementation: // GraphOwned implementation:
void OnPassedToGraph(Graph* graph) override; void OnPassedToGraph(Graph* graph) override;
void OnTakenFromGraph(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: // WorkerNode::ObserverDefaultImpl implementation:
void OnWorkerNodeAdded(const WorkerNode* worker_node) override; void OnWorkerNodeAdded(const WorkerNode* worker_node) override;
void OnBeforeWorkerNodeRemoved(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 // 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 // Graph. It is expected that there are O(100s) to O(1000s) of these being
...@@ -94,4 +100,4 @@ class ExecutionContextRegistryImpl ...@@ -94,4 +100,4 @@ class ExecutionContextRegistryImpl
} // namespace execution_context } // namespace execution_context
} // namespace performance_manager } // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_H_ #endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_H_
\ No newline at end of file
...@@ -35,6 +35,11 @@ class LenientMockExecutionContextObserver : public ExecutionContextObserver { ...@@ -35,6 +35,11 @@ class LenientMockExecutionContextObserver : public ExecutionContextObserver {
OnBeforeExecutionContextRemoved, OnBeforeExecutionContextRemoved,
(const ExecutionContext*), (const ExecutionContext*),
()); ());
MOCK_METHOD(void,
OnPriorityAndReasonChanged,
(const ExecutionContext*,
const PriorityAndReason& previous_value),
());
}; };
using MockExecutionContextObserver = using MockExecutionContextObserver =
testing::StrictMock<LenientMockExecutionContextObserver>; testing::StrictMock<LenientMockExecutionContextObserver>;
...@@ -70,31 +75,16 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) { ...@@ -70,31 +75,16 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) {
// Ensure that the public getter works. // Ensure that the public getter works.
EXPECT_EQ(registry_, ExecutionContextRegistry::GetFromGraph(graph())); 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 // Create some mock nodes. This creates a graph with 1 page containing 2
// frames in 1 process. // 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()); MockMultiplePagesInSingleProcessGraph mock_graph(graph());
// Only the frames are in the map at this point. // Only the frames are in the map at this point.
EXPECT_EQ(2u, ecs.size());
EXPECT_EQ(2u, registry_->GetExecutionContextCountForTesting()); EXPECT_EQ(2u, registry_->GetExecutionContextCountForTesting());
// Creating a worker should create another entry in the map. // 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>( auto worker_node = CreateNode<WorkerNodeImpl>(
WorkerNode::WorkerType::kDedicated, mock_graph.process.get()); WorkerNode::WorkerType::kDedicated, mock_graph.process.get());
EXPECT_EQ(3u, ecs.size());
EXPECT_EQ(3u, registry_->GetExecutionContextCountForTesting()); EXPECT_EQ(3u, registry_->GetExecutionContextCountForTesting());
auto* frame1 = mock_graph.frame.get(); auto* frame1 = mock_graph.frame.get();
...@@ -106,11 +96,6 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) { ...@@ -106,11 +96,6 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) {
auto* frame2_ec = GetOrCreateExecutionContextForFrameNode(frame2); auto* frame2_ec = GetOrCreateExecutionContextForFrameNode(frame2);
auto* worker_ec = GetOrCreateExecutionContextForWorkerNode(worker); 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 the FrameExecutionContext implementation to work.
EXPECT_EQ(ExecutionContextType::kFrameNode, frame1_ec->GetType()); EXPECT_EQ(ExecutionContextType::kFrameNode, frame1_ec->GetType());
EXPECT_EQ(frame1->frame_token().value(), frame1_ec->GetToken().value()); EXPECT_EQ(frame1->frame_token().value(), frame1_ec->GetToken().value());
...@@ -154,15 +139,45 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) { ...@@ -154,15 +139,45 @@ TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) {
registry_->GetExecutionContextByToken(blink::ExecutionContextToken())); registry_->GetExecutionContextByToken(blink::ExecutionContextToken()));
EXPECT_FALSE(registry_->GetFrameNodeByFrameToken(blink::LocalFrameToken())); EXPECT_FALSE(registry_->GetFrameNodeByFrameToken(blink::LocalFrameToken()));
EXPECT_FALSE(registry_->GetWorkerNodeByWorkerToken(blink::WorkerToken())); 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. // Destroy nodes one by one and expect observer notifications.
EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(worker_ec)); EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(worker_ec));
worker_node.reset(); mock_graph.DeleteWorker();
EXPECT_EQ(2u, registry_->GetExecutionContextCountForTesting());
EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(frame2_ec));
mock_graph.other_frame.reset();
EXPECT_EQ(1u, registry_->GetExecutionContextCountForTesting()); EXPECT_EQ(1u, registry_->GetExecutionContextCountForTesting());
EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(frame1_ec)); EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(frame_ec));
mock_graph.frame.reset(); mock_graph.frame.reset();
EXPECT_EQ(0u, registry_->GetExecutionContextCountForTesting()); EXPECT_EQ(0u, registry_->GetExecutionContextCountForTesting());
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_H_ #define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_H_
#include "base/observer_list_types.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" #include "third_party/blink/public/common/tokens/tokens.h"
class GURL; class GURL;
...@@ -17,6 +18,8 @@ class Graph; ...@@ -17,6 +18,8 @@ class Graph;
class ProcessNode; class ProcessNode;
class WorkerNode; class WorkerNode;
using execution_context_priority::PriorityAndReason;
namespace execution_context { namespace execution_context {
class ExecutionContextObserver; class ExecutionContextObserver;
...@@ -73,6 +76,10 @@ class ExecutionContext { ...@@ -73,6 +76,10 @@ class ExecutionContext {
// ExecutionContext is hosted. This will never return nullptr. // ExecutionContext is hosted. This will never return nullptr.
virtual const ProcessNode* GetProcessNode() const = 0; 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 // Returns the underlying FrameNode, if this context is a FrameNode, or
// nullptr otherwise. // nullptr otherwise.
virtual const FrameNode* GetFrameNode() const = 0; virtual const FrameNode* GetFrameNode() const = 0;
...@@ -97,6 +104,11 @@ class ExecutionContextObserver : public base::CheckedObserver { ...@@ -97,6 +104,11 @@ class ExecutionContextObserver : public base::CheckedObserver {
// Called when an ExecutionContext is about to be removed. The pointer |ec| // Called when an ExecutionContext is about to be removed. The pointer |ec|
// becomes invalid immediately after this returns. // becomes invalid immediately after this returns.
virtual void OnBeforeExecutionContextRemoved(const ExecutionContext* ec) = 0; 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 // A default implementation of ExecutionContextObserver with empty stubs for all
...@@ -113,6 +125,9 @@ class ExecutionContextObserverDefaultImpl : public ExecutionContextObserver { ...@@ -113,6 +125,9 @@ class ExecutionContextObserverDefaultImpl : public ExecutionContextObserver {
// ExecutionContextObserver implementation: // ExecutionContextObserver implementation:
void OnExecutionContextAdded(const ExecutionContext* ec) override {} void OnExecutionContextAdded(const ExecutionContext* ec) override {}
void OnBeforeExecutionContextRemoved(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 // Helper function for converting from a WorkerToken to an
......
...@@ -87,4 +87,26 @@ MockMultiplePagesWithMultipleProcessesGraph:: ...@@ -87,4 +87,26 @@ MockMultiplePagesWithMultipleProcessesGraph::
MockMultiplePagesWithMultipleProcessesGraph:: MockMultiplePagesWithMultipleProcessesGraph::
~MockMultiplePagesWithMultipleProcessesGraph() = default; ~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 } // namespace performance_manager
...@@ -116,6 +116,30 @@ struct MockMultiplePagesWithMultipleProcessesGraph ...@@ -116,6 +116,30 @@ struct MockMultiplePagesWithMultipleProcessesGraph
TestNodeWrapper<FrameNodeImpl> child_frame; 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 } // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_TEST_SUPPORT_MOCK_GRAPHS_H_ #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