Skip to content

Commit

Permalink
[PM] Add priority and reason to ExecutionContext and its observer
Browse files Browse the repository at this point in the history
Bug: 1077217
Change-Id: Ie0081edc73c8b8ed0669c0aacaad9ccbb2ead003
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2490800
Reviewed-by: Chris Hamilton <chrisha@chromium.org>
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Cr-Commit-Position: refs/heads/master@{#820317}
  • Loading branch information
plmonette-zz authored and Commit Bot committed Oct 23, 2020
1 parent 5b367f0 commit 2640bb4
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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_);
Expand All @@ -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(
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -94,4 +100,4 @@ class ExecutionContextRegistryImpl
} // namespace execution_context
} // 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_
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -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();
Expand All @@ -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());
Expand Down Expand Up @@ -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());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,6 +18,8 @@ class Graph;
class ProcessNode;
class WorkerNode;

using execution_context_priority::PriorityAndReason;

namespace execution_context {

class ExecutionContextObserver;
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand 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
Expand Down
22 changes: 22 additions & 0 deletions components/performance_manager/test_support/mock_graphs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
24 changes: 24 additions & 0 deletions components/performance_manager/test_support/mock_graphs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_

0 comments on commit 2640bb4

Please sign in to comment.