Commit f3beb68e authored by Chris Hamilton's avatar Chris Hamilton Committed by Commit Bot

[PM] Create ExecutionContextRegistry.

This creates a registry of all FrameNodes and WorkersNodes, and a
wrapper that allows them to be accessed from a common ExecutionContext
type. This will be used in various upcoming per-ExecutionContext
tracking CLs.

BUG=1085129

Change-Id: Ibe6467ee07c9e143c788f6a42dcdd955966a0fd2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2274543
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarJoe Mason <joenotcharles@chromium.org>
Cr-Commit-Position: refs/heads/master@{#785994}
parent 147ed45a
......@@ -20,6 +20,10 @@ static_library("performance_manager") {
"embedder/binders.h",
"embedder/performance_manager_lifetime.h",
"embedder/performance_manager_registry.h",
"execution_context/execution_context_impl.cc",
"execution_context/execution_context_impl.h",
"execution_context/execution_context_registry_impl.cc",
"execution_context/execution_context_registry_impl.h",
"features.cc",
"frame_priority/boosting_vote_aggregator.cc",
"frame_priority/frame_priority.cc",
......@@ -86,6 +90,9 @@ static_library("performance_manager") {
"public/decorators/page_load_tracker_decorator_helper.h",
"public/decorators/tab_properties_decorator.h",
"public/decorators/v8_per_frame_memory_decorator.h",
"public/execution_context/execution_context.h",
"public/execution_context/execution_context_registry.h",
"public/execution_context/execution_context_token.h",
"public/features.h",
"public/frame_priority/boosting_vote_aggregator.h",
"public/frame_priority/frame_priority.h",
......@@ -187,6 +194,7 @@ source_set("unit_tests") {
"decorators/page_load_tracker_decorator_unittest.cc",
"decorators/tab_properties_decorator_unittest.cc",
"decorators/v8_per_frame_memory_decorator_unittest.cc",
"execution_context/execution_context_registry_impl_unittest.cc",
"frame_priority/boosting_vote_aggregator_unittest.cc",
"frame_priority/frame_priority_unittest.cc",
"frame_priority/max_vote_aggregator_unittest.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/performance_manager/execution_context/execution_context_impl.h"
#include "base/sequence_checker.h"
#include "base/util/type_safety/pass_key.h"
#include "components/performance_manager/graph/frame_node_impl.h"
#include "components/performance_manager/graph/node_attached_data_impl.h"
#include "components/performance_manager/graph/worker_node_impl.h"
#include "components/performance_manager/public/execution_context/execution_context.h"
namespace performance_manager {
namespace execution_context {
// Allows accessing internal NodeAttachedData storage for ExecutionContext
// implementations.
class ExecutionContextAccess {
public:
using PassKey = util::PassKey<ExecutionContextAccess>;
template <typename NodeImplType>
static std::unique_ptr<NodeAttachedData>* GetExecutionAccessStorage(
NodeImplType* node) {
return node->GetExecutionContextStorage(PassKey());
}
};
namespace {
// Templated partial implementation of ExecutionContext. Implements the common
// bits of ExecutionContext, except for GetToken().
template <typename DerivedType,
typename NodeImplType,
ExecutionContextType kExecutionContextType>
class ExecutionContextImpl : public ExecutionContext,
public NodeAttachedDataImpl<DerivedType> {
public:
using ImplType = ExecutionContextImpl;
struct Traits : public NodeAttachedDataImpl<DerivedType>::
template NodeAttachedDataOwnedByNodeType<NodeImplType> {};
ExecutionContextImpl(const ExecutionContextImpl&) = delete;
ExecutionContextImpl& operator=(const ExecutionContextImpl&) = delete;
~ExecutionContextImpl() override {}
// ExecutionContext implementation:
ExecutionContextType GetType() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return kExecutionContextType;
}
const GURL& GetUrl() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return node_->url();
}
const FrameNode* GetFrameNode() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (std::is_same<FrameNodeImpl, NodeImplType>::value)
return reinterpret_cast<const FrameNode*>(node_);
return nullptr;
}
const WorkerNode* GetWorkerNode() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (std::is_same<WorkerNodeImpl, NodeImplType>::value)
return reinterpret_cast<const WorkerNodeImpl*>(node_);
return nullptr;
}
protected:
explicit ExecutionContextImpl(const NodeImplType* node) : node_(node) {
DCHECK(node);
}
static std::unique_ptr<NodeAttachedData>* GetUniquePtrStorage(
NodeImplType* node) {
return ExecutionContextAccess::GetExecutionAccessStorage(node);
}
SEQUENCE_CHECKER(sequence_checker_);
const NodeImplType* node_ = nullptr;
};
// An ExecutionContext implementation that wraps a FrameNodeImpl.
class FrameExecutionContext
: public ExecutionContextImpl<FrameExecutionContext,
FrameNodeImpl,
ExecutionContextType::kFrameNode> {
public:
FrameExecutionContext(const FrameExecutionContext&) = delete;
FrameExecutionContext& operator=(const FrameExecutionContext&) = delete;
~FrameExecutionContext() override = default;
// Remaining ExecutionContext implementation not provided by
// ExecutionContextImpl:
const ExecutionContextToken& GetToken() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The casting is safe because ExecutionContext guarantees it has the same
// layout as base::UnguessableToken.
return *reinterpret_cast<const ExecutionContextToken*>(
&node_->frame_token().value());
}
protected:
friend class NodeAttachedDataImpl<FrameExecutionContext>;
explicit FrameExecutionContext(const FrameNodeImpl* frame_node)
: ImplType(frame_node) {}
};
// An ExecutionContext implementation that wraps a WorkerNodeImpl.
class WorkerExecutionContext
: public ExecutionContextImpl<WorkerExecutionContext,
WorkerNodeImpl,
ExecutionContextType::kWorkerNode> {
public:
WorkerExecutionContext(const WorkerExecutionContext&) = delete;
WorkerExecutionContext& operator=(const WorkerExecutionContext&) = delete;
~WorkerExecutionContext() override = default;
// Remaining ExecutionContext implementation not provided by
// ExecutionContextImpl:
const ExecutionContextToken& GetToken() const override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The casting is safe because ExecutionContext guarantees it has the same
// layout as base::UnguessableToken.
return *reinterpret_cast<const ExecutionContextToken*>(
&node_->dev_tools_token());
}
protected:
friend class NodeAttachedDataImpl<WorkerExecutionContext>;
explicit WorkerExecutionContext(const WorkerNodeImpl* worker_node)
: ImplType(worker_node) {}
};
} // namespace
const ExecutionContext* GetOrCreateExecutionContextForFrameNode(
const FrameNode* frame_node) {
DCHECK(frame_node);
DCHECK_ON_GRAPH_SEQUENCE(frame_node->GetGraph());
return FrameExecutionContext::GetOrCreate(
FrameNodeImpl::FromNode(frame_node));
}
const ExecutionContext* GetOrCreateExecutionContextForWorkerNode(
const WorkerNode* worker_node) {
DCHECK(worker_node);
DCHECK_ON_GRAPH_SEQUENCE(worker_node->GetGraph());
return WorkerExecutionContext::GetOrCreate(
WorkerNodeImpl::FromNode(worker_node));
}
} // namespace execution_context
} // namespace performance_manager
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_EXECUTION_CONTEXT_EXECUTION_CONTEXT_IMPL_H_
#define COMPONENTS_PERFORMANCE_MANAGER_EXECUTION_CONTEXT_EXECUTION_CONTEXT_IMPL_H_
namespace performance_manager {
class FrameNode;
class WorkerNode;
namespace execution_context {
class ExecutionContext;
// Constructs ExecutionContext wrappers (implemented as NodeAttachedData) for
// FrameNodes and WorkerNodes. Once created the objects will live until the
// underlying node disappears. These should only be called from the graph
// sequence, like the underlying objects they wrap. The public interface of
// this is via ExecutionContextRegistry::GetExecutionContextFor*Node().
const ExecutionContext* GetOrCreateExecutionContextForFrameNode(
const FrameNode* frame_node);
const ExecutionContext* GetOrCreateExecutionContextForWorkerNode(
const WorkerNode* worker_node);
} // namespace execution_context
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_EXECUTION_CONTEXT_EXECUTION_CONTEXT_IMPL_H_
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/performance_manager/execution_context/execution_context_registry_impl.h"
#include "base/check.h"
#include "components/performance_manager/execution_context/execution_context_impl.h"
#include "components/performance_manager/public/execution_context/execution_context.h"
#include "url/gurl.h"
namespace performance_manager {
namespace execution_context {
namespace {
// std::unordered_set doesn't support transparent keys until C++20, so we use
// a custom ExecutionContext wrapper for the time being.
class DummyExecutionContextForLookup : public ExecutionContext {
public:
explicit DummyExecutionContextForLookup(const ExecutionContextToken& token)
: token_(token) {}
DummyExecutionContextForLookup(const DummyExecutionContextForLookup&) =
delete;
DummyExecutionContextForLookup& operator=(
const DummyExecutionContextForLookup&) = delete;
~DummyExecutionContextForLookup() override = default;
// ExecutionContext implementation:
ExecutionContextType GetType() const override {
NOTREACHED();
return ExecutionContextType::kFrameNode;
}
const ExecutionContextToken& GetToken() const override { return token_; }
const GURL& GetUrl() const override {
NOTREACHED();
static const GURL kUrl;
return kUrl;
}
const FrameNode* GetFrameNode() const override {
NOTREACHED();
return nullptr;
}
const WorkerNode* GetWorkerNode() const override {
NOTREACHED();
return nullptr;
}
private:
const ExecutionContextToken& token_;
};
} // namespace
////////////////////////////////////////////////////////////////////////////////
// ExecutionContextRegistry
ExecutionContextRegistry::ExecutionContextRegistry() = default;
ExecutionContextRegistry::~ExecutionContextRegistry() = default;
// static
ExecutionContextRegistry* ExecutionContextRegistry::GetFromGraph(Graph* graph) {
return GraphRegisteredImpl<ExecutionContextRegistryImpl>::GetFromGraph(graph);
}
////////////////////////////////////////////////////////////////////////////////
// ExecutionContextRegistryImpl
ExecutionContextRegistryImpl::ExecutionContextRegistryImpl() = default;
ExecutionContextRegistryImpl::~ExecutionContextRegistryImpl() = default;
void ExecutionContextRegistryImpl::AddObserver(
ExecutionContextObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observers_.AddObserver(observer);
}
void ExecutionContextRegistryImpl::RemoveObserver(
ExecutionContextObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(observers_.HasObserver(observer));
observers_.RemoveObserver(observer);
}
const ExecutionContext*
ExecutionContextRegistryImpl::GetExecutionContextByToken(
const ExecutionContextToken& token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (token.value().is_empty())
return nullptr;
DummyExecutionContextForLookup key(token);
auto it = execution_contexts_.find(&key);
if (it == execution_contexts_.end())
return nullptr;
return *it;
}
const FrameNode* ExecutionContextRegistryImpl::GetFrameNodeByFrameToken(
const FrameToken& token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The casting is safe because ExecutionContextToken guarantees it has the
// same layout as base::UnguessableToken.
auto* ec = GetExecutionContextByToken(
*reinterpret_cast<const ExecutionContextToken*>(&token.value()));
if (!ec)
return nullptr;
return ec->GetFrameNode();
}
const WorkerNode* ExecutionContextRegistryImpl::GetWorkerNodeByDevToolsToken(
const base::UnguessableToken& token) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The casting is safe because ExecutionContext guarantees it has the same
// layout as base::UnguessableToken.
auto* ec = GetExecutionContextByToken(
*reinterpret_cast<const ExecutionContextToken*>(&token));
if (!ec)
return nullptr;
return ec->GetWorkerNode();
}
const ExecutionContext*
ExecutionContextRegistryImpl::GetExecutionContextForFrameNode(
const FrameNode* frame_node) {
return GetOrCreateExecutionContextForFrameNode(frame_node);
}
const ExecutionContext*
ExecutionContextRegistryImpl::GetExecutionContextForWorkerNode(
const WorkerNode* worker_node) {
return GetOrCreateExecutionContextForWorkerNode(worker_node);
}
void ExecutionContextRegistryImpl::OnFrameNodeAdded(
const FrameNode* frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto* ec = GetOrCreateExecutionContextForFrameNode(frame_node);
DCHECK(ec);
auto result = execution_contexts_.insert(ec);
DCHECK(result.second); // Inserted.
for (auto& observer : observers_)
observer.OnExecutionContextAdded(ec);
}
void ExecutionContextRegistryImpl::OnBeforeFrameNodeRemoved(
const FrameNode* frame_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto* ec = GetOrCreateExecutionContextForFrameNode(frame_node);
DCHECK(ec);
for (auto& observer : observers_)
observer.OnBeforeExecutionContextRemoved(ec);
size_t erased = execution_contexts_.erase(ec);
DCHECK_EQ(1u, erased);
}
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::OnWorkerNodeAdded(
const WorkerNode* worker_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto* ec = GetOrCreateExecutionContextForWorkerNode(worker_node);
DCHECK(ec);
// Unfortunately, in tests workers do not always have a valid dev tools token.
// TODO: Transition to using custom worker tokens (like FrameToken), and
// get rid of this conditional.
if (!ec->GetToken().value().is_empty()) {
auto result = execution_contexts_.insert(ec);
DCHECK(result.second); // Inserted.
}
for (auto& observer : observers_)
observer.OnExecutionContextAdded(ec);
}
void ExecutionContextRegistryImpl::OnBeforeWorkerNodeRemoved(
const WorkerNode* worker_node) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto* ec = GetOrCreateExecutionContextForWorkerNode(worker_node);
DCHECK(ec);
for (auto& observer : observers_)
observer.OnBeforeExecutionContextRemoved(ec);
// Unfortunately, in tests workers do not always have a valid dev tools token.
// TODO: Transition to using custom worker tokens (like FrameToken), and
// get rid of this conditional.
if (!ec->GetToken().value().is_empty()) {
size_t erased = execution_contexts_.erase(ec);
DCHECK_EQ(1u, erased);
}
}
////////////////////////////////////////////////////////////////////////////////
// ExecutionContextRegistryImpl::ExecutionContextHash
size_t ExecutionContextRegistryImpl::ExecutionContextHash::operator()(
const ExecutionContext* ec) const {
return base::UnguessableTokenHash()(ec->GetToken().value());
}
////////////////////////////////////////////////////////////////////////////////
// ExecutionContextRegistryImpl::ExecutionContextKeyEqual
bool ExecutionContextRegistryImpl::ExecutionContextKeyEqual::operator()(
const ExecutionContext* ec1,
const ExecutionContext* ec2) const {
return ec1->GetToken() == ec2->GetToken();
}
} // namespace execution_context
} // namespace performance_manager
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_IMPL_H_
#define COMPONENTS_PERFORMANCE_MANAGER_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_IMPL_H_
#include "base/observer_list.h"
#include "base/sequence_checker.h"
#include "components/performance_manager/public/execution_context/execution_context_registry.h"
#include "components/performance_manager/public/execution_context/execution_context_token.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/graph_registered.h"
#include "components/performance_manager/public/graph/worker_node.h"
namespace base {
class UnguessableToken;
} // namespace base
namespace performance_manager {
namespace execution_context {
class ExecutionContext;
// The ExecutionContextRegistry is a GraphRegistered class that allows for
// observers to be registered, and for ExecutionContexts to be looked up by
// their tokens. An instance of the registry must be passed to the Graph prior
// to any nodes being created.
class ExecutionContextRegistryImpl
: public ExecutionContextRegistry,
public FrameNode::ObserverDefaultImpl,
public GraphOwned,
public GraphRegisteredImpl<ExecutionContextRegistryImpl>,
public WorkerNode::ObserverDefaultImpl {
public:
ExecutionContextRegistryImpl();
ExecutionContextRegistryImpl(const ExecutionContextRegistryImpl&) = delete;
ExecutionContextRegistryImpl& operator=(const ExecutionContextRegistryImpl&) =
delete;
~ExecutionContextRegistryImpl() override;
// ExecutionContextRegistry implementation:
void AddObserver(ExecutionContextObserver* observer) override;
void RemoveObserver(ExecutionContextObserver* observer) override;
const ExecutionContext* GetExecutionContextByToken(
const ExecutionContextToken& token) override;
const FrameNode* GetFrameNodeByFrameToken(const FrameToken& token) override;
const WorkerNode* GetWorkerNodeByDevToolsToken(
const base::UnguessableToken& token) override;
const ExecutionContext* GetExecutionContextForFrameNode(
const FrameNode* frame_node) override;
const ExecutionContext* GetExecutionContextForWorkerNode(
const WorkerNode* worker_node) override;
size_t GetExecutionContextCountForTesting() const {
return execution_contexts_.size();
}
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;
// WorkerNode::ObserverDefaultImpl implementation:
void OnWorkerNodeAdded(const WorkerNode* worker_node) override;
void OnBeforeWorkerNodeRemoved(const WorkerNode* worker_node) 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
// tracked. This is stored as a hash set keyed by the token, so that the
// token itself doesn't have to be duplicated as would be the case with a map.
// TODO: Whenever C++20 is supported, make these transparent!
struct ExecutionContextHash {
size_t operator()(const ExecutionContext* ec) const;
};
struct ExecutionContextKeyEqual {
bool operator()(const ExecutionContext* ec1,
const ExecutionContext* ec2) const;
};
std::unordered_set<const ExecutionContext*,
ExecutionContextHash,
ExecutionContextKeyEqual>
execution_contexts_;
base::ObserverList<ExecutionContextObserver, /* check_empty = */ true>
observers_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace execution_context
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_H_
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/performance_manager/execution_context/execution_context_registry_impl.h"
#include <memory>
#include "base/test/gtest_util.h"
#include "components/performance_manager/execution_context/execution_context_impl.h"
#include "components/performance_manager/public/execution_context/execution_context.h"
#include "components/performance_manager/test_support/graph_test_harness.h"
#include "components/performance_manager/test_support/mock_graphs.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace performance_manager {
namespace execution_context {
namespace {
class LenientMockExecutionContextObserver : public ExecutionContextObserver {
public:
LenientMockExecutionContextObserver() = default;
LenientMockExecutionContextObserver(
const LenientMockExecutionContextObserver&) = delete;
LenientMockExecutionContextObserver& operator=(
const LenientMockExecutionContextObserver&) = delete;
~LenientMockExecutionContextObserver() override = default;
// ExecutionContextObserver implementation:
MOCK_METHOD(void, OnExecutionContextAdded, (const ExecutionContext*), ());
MOCK_METHOD(void,
OnBeforeExecutionContextRemoved,
(const ExecutionContext*),
());
};
using MockExecutionContextObserver =
testing::StrictMock<LenientMockExecutionContextObserver>;
class ExecutionContextRegistryImplTest : public GraphTestHarness {
public:
using Super = GraphTestHarness;
ExecutionContextRegistryImplTest() = default;
ExecutionContextRegistryImplTest(const ExecutionContextRegistryImplTest&) =
delete;
ExecutionContextRegistryImplTest& operator=(
const ExecutionContextRegistryImplTest&) = delete;
~ExecutionContextRegistryImplTest() override = default;
void SetUp() override {
Super::SetUp();
graph()->PassToGraph(std::make_unique<ExecutionContextRegistryImpl>());
registry_ = GraphRegisteredImpl<ExecutionContextRegistryImpl>::GetFromGraph(
graph());
ASSERT_TRUE(registry_);
}
protected:
ExecutionContextRegistryImpl* registry_ = nullptr;
};
using ExecutionContextRegistryImplDeathTest = ExecutionContextRegistryImplTest;
} // namespace
TEST_F(ExecutionContextRegistryImplTest, RegistryWorks) {
// Ensure that the public getter works.
EXPECT_EQ(registry_, ExecutionContextRegistry::GetFromGraph(graph()));
// Create an observer.
MockExecutionContextObserver obs;
registry_->AddObserver(&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();
auto* frame2 = mock_graph.other_frame.get();
auto* worker = worker_node.get();
// Get the execution contexts for each node directly.
auto* frame1_ec = GetOrCreateExecutionContextForFrameNode(frame1);
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());
EXPECT_EQ(frame1->url(), frame1_ec->GetUrl());
EXPECT_EQ(frame1, frame1_ec->GetFrameNode());
EXPECT_FALSE(frame1_ec->GetWorkerNode());
// Expect the WorkerExecutionContext implementation to work.
EXPECT_EQ(ExecutionContextType::kWorkerNode, worker_ec->GetType());
EXPECT_EQ(worker->dev_tools_token(), worker_ec->GetToken().value());
EXPECT_EQ(worker->url(), worker_ec->GetUrl());
EXPECT_FALSE(worker_ec->GetFrameNode());
EXPECT_EQ(worker, worker_ec->GetWorkerNode());
// Getting ExecutionContexts for a node should work.
EXPECT_EQ(frame1_ec, registry_->GetExecutionContextForFrameNode(frame1));
EXPECT_EQ(frame2_ec, registry_->GetExecutionContextForFrameNode(frame2));
EXPECT_EQ(worker_ec, registry_->GetExecutionContextForWorkerNode(worker));
// Lookup by ExecutionContextToken should work.
EXPECT_EQ(frame1_ec,
registry_->GetExecutionContextByToken(frame1_ec->GetToken()));
EXPECT_EQ(frame2_ec,
registry_->GetExecutionContextByToken(frame2_ec->GetToken()));
EXPECT_EQ(worker_ec,
registry_->GetExecutionContextByToken(worker_ec->GetToken()));
// Lookup by typed tokens should work.
EXPECT_EQ(frame1, registry_->GetFrameNodeByFrameToken(frame1->frame_token()));
EXPECT_EQ(frame2, registry_->GetFrameNodeByFrameToken(frame2->frame_token()));
EXPECT_EQ(worker,
registry_->GetWorkerNodeByDevToolsToken(worker->dev_tools_token()));
// Querying an invalid token or a random token should fail.
EXPECT_FALSE(registry_->GetExecutionContextByToken(
ExecutionContextToken(base::UnguessableToken::Null())));
EXPECT_FALSE(registry_->GetExecutionContextByToken(
ExecutionContextToken(base::UnguessableToken::Create())));
EXPECT_FALSE(registry_->GetFrameNodeByFrameToken(
FrameToken(base::UnguessableToken::Null())));
EXPECT_FALSE(registry_->GetFrameNodeByFrameToken(
FrameToken(base::UnguessableToken::Create())));
EXPECT_FALSE(
registry_->GetWorkerNodeByDevToolsToken(base::UnguessableToken::Null()));
EXPECT_FALSE(registry_->GetWorkerNodeByDevToolsToken(
base::UnguessableToken::Create()));
// 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();
EXPECT_EQ(1u, registry_->GetExecutionContextCountForTesting());
EXPECT_CALL(obs, OnBeforeExecutionContextRemoved(frame1_ec));
mock_graph.frame.reset();
EXPECT_EQ(0u, registry_->GetExecutionContextCountForTesting());
// Unregister the observer so that the registry doesn't explode when it is
// torn down.
registry_->RemoveObserver(&obs);
}
TEST_F(ExecutionContextRegistryImplDeathTest, EnforceObserversRemoved) {
// Create an observer.
MockExecutionContextObserver obs;
registry_->AddObserver(&obs);
// The registry should explode if we kill it without unregistering observers.
EXPECT_DCHECK_DEATH(graph()->TakeFromGraph(registry_));
// Unregister the observer so that the registry doesn't explode when it is
// torn down.
registry_->RemoveObserver(&obs);
}
} // namespace execution_context
} // namespace performance_manager
......@@ -14,6 +14,7 @@
#include "base/util/type_safety/pass_key.h"
#include "components/performance_manager/graph/node_base.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/node_attached_data.h"
#include "components/performance_manager/public/render_frame_host_proxy.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
......@@ -26,6 +27,10 @@ class PageNodeImpl;
class ProcessNodeImpl;
class WorkerNodeImpl;
namespace execution_context {
class ExecutionContextAccess;
} // namespace execution_context
// Frame nodes form a tree structure, each FrameNode at most has one parent that
// is a FrameNode. Conceptually, a frame corresponds to a
// content::RenderFrameHost in the browser, and a content::RenderFrameImpl /
......@@ -139,8 +144,7 @@ class FrameNodeImpl
SeverOpenedPagesAndMaybeReparent();
}
protected:
friend class PageNodeImpl;
// Implementation details below this point.
// Invoked by opened pages when this frame is set/cleared as their opener.
// See PageNodeImpl::(Set|Clear)OpenerFrameNodeAndOpenedType.
......@@ -148,6 +152,12 @@ class FrameNodeImpl
void RemoveOpenedPage(util::PassKey<PageNodeImpl> key,
PageNodeImpl* page_node);
// Used by the ExecutionContextRegistry mechanism.
std::unique_ptr<NodeAttachedData>* GetExecutionContextStorage(
util::PassKey<execution_context::ExecutionContextAccess> key) {
return &execution_context_;
}
private:
friend class FrameNodeImplDescriber;
friend class FramePriorityAccess;
......@@ -328,6 +338,9 @@ class FrameNodeImpl
// Inline storage for FramePriorityDecorator data.
frame_priority::AcceptedVote accepted_vote_;
// Inline storage for ExecutionContext.
std::unique_ptr<NodeAttachedData> execution_context_;
base::WeakPtrFactory<FrameNodeImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(FrameNodeImpl);
......
......@@ -4,6 +4,8 @@
#include "components/performance_manager/public/graph/graph.h"
#include "base/dcheck_is_on.h"
namespace performance_manager {
Graph::Graph() = default;
......
......@@ -325,6 +325,12 @@ const void* GraphImpl::GetImpl() const {
return this;
}
#if DCHECK_IS_ON()
bool GraphImpl::IsOnGraphSequence() const {
return sequence_checker_.CalledOnValidSequence();
}
#endif
GraphRegistered* GraphImpl::GetRegisteredObject(uintptr_t type_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return registered_objects_.GetRegisteredObject(type_id);
......
......@@ -82,6 +82,9 @@ class GraphImpl : public Graph {
NodeDataDescriberRegistry* GetNodeDataDescriberRegistry() const override;
uintptr_t GetImplType() const override;
const void* GetImpl() const override;
#if DCHECK_IS_ON()
bool IsOnGraphSequence() const override;
#endif
GraphRegistered* GetRegisteredObject(uintptr_t type_id) override;
// Helper function for safely downcasting to the implementation. This also
......
......@@ -12,6 +12,7 @@
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/unguessable_token.h"
#include "base/util/type_safety/pass_key.h"
#include "components/performance_manager/graph/node_base.h"
#include "components/performance_manager/public/graph/worker_node.h"
#include "url/gurl.h"
......@@ -21,6 +22,10 @@ namespace performance_manager {
class FrameNodeImpl;
class ProcessNodeImpl;
namespace execution_context {
class ExecutionContextAccess;
} // namespace execution_context
class WorkerNodeImpl
: public PublicNodeImpl<WorkerNodeImpl, WorkerNode>,
public TypedNodeBase<WorkerNodeImpl, WorkerNode, WorkerNodeObserver> {
......@@ -57,6 +62,14 @@ class WorkerNodeImpl
const base::flat_set<WorkerNodeImpl*>& client_workers() const;
const base::flat_set<WorkerNodeImpl*>& child_workers() const;
// Implementation details below this point.
// Used by the ExecutionContextRegistry mechanism.
std::unique_ptr<NodeAttachedData>* GetExecutionContextStorage(
util::PassKey<execution_context::ExecutionContextAccess> key) {
return &execution_context_;
}
private:
void OnJoiningGraph() override;
void OnBeforeLeavingGraph() override;
......@@ -106,6 +119,9 @@ class WorkerNodeImpl
// distinction between client workers and child workers.
base::flat_set<WorkerNodeImpl*> child_workers_;
// Used by ExecutionContextRegistry mechanism.
std::unique_ptr<NodeAttachedData> execution_context_;
DISALLOW_COPY_AND_ASSIGN(WorkerNodeImpl);
};
......
......@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "components/performance_manager/decorators/page_load_tracker_decorator.h"
#include "components/performance_manager/execution_context/execution_context_registry_impl.h"
#include "components/performance_manager/graph/frame_node_impl_describer.h"
#include "components/performance_manager/graph/page_node_impl_describer.h"
#include "components/performance_manager/graph/process_node_impl_describer.h"
......@@ -22,6 +23,8 @@ namespace {
void DefaultGraphCreatedCallback(
GraphCreatedCallback external_graph_created_callback,
GraphImpl* graph) {
graph->PassToGraph(
std::make_unique<execution_context::ExecutionContextRegistryImpl>());
graph->PassToGraph(std::make_unique<FrameNodeImplDescriber>());
graph->PassToGraph(std::make_unique<PageLiveStateDecorator>());
graph->PassToGraph(std::make_unique<PageLoadTrackerDecorator>());
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef 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 "components/performance_manager/public/execution_context/execution_context_token.h"
class GURL;
namespace performance_manager {
class FrameNode;
class WorkerNode;
namespace execution_context {
class ExecutionContextObserver;
class ExecutionContextObserverDefaultImpl;
// The types of ExecutionContexts that are defined.
enum class ExecutionContextType {
kFrameNode,
kWorkerNode,
};
// An ExecutionContext is a concept from Blink, which denotes a context in
// which Javascript can be executed. Roughly speaking, both FrameNodes and
// WorkerNodes correspond to ExecutionContexts in the PM world-view. This class
// allows external observers to track ExecutionContexts abstractly, without
// having to deal with multiple types at runtime, and duplicating lots of code.
//
// A decorator is responsible for augmenting each instance of a WorkerNode or a
// FrameNode with an instance of ExecutionContext which wraps it, and provides
// accessors for concepts that are common between these two node types. It also
// provides an observer which adapts FrameNodeObserver and WorkerNodeObserver
// behind the scenes, so you don't have to.
//
// Instances of this object are managed by the ExecutionContextRegistry. Their
// lifetimes are the same as those of the underlying nodes they wrap.
class ExecutionContext {
public:
using Observer = ExecutionContextObserver;
using ObserverDefaultImpl = ExecutionContextObserverDefaultImpl;
ExecutionContext() = default;
ExecutionContext(const ExecutionContext&) = delete;
ExecutionContext& operator=(const ExecutionContext&) = delete;
virtual ~ExecutionContext() = default;
// Returns the type of this ExecutionContext.
virtual ExecutionContextType GetType() const = 0;
// Returns the unique token associated with this ExecutionContext. This is a
// constant over the lifetime of the context. Tokens are unique for all time
// and should never be reused.
virtual const ExecutionContextToken& GetToken() const = 0;
// Returns the final post-redirect committed URL associated with this
// ExecutionContext. This is the URL of the HTML document (not the javascript)
// in the case of a FrameNode, or the URL of the worker javascript in the case
// of a WorkerNode.
virtual const GURL& GetUrl() const = 0;
// Returns the underlying FrameNode, if this context is a FrameNode, or
// nullptr otherwise.
virtual const FrameNode* GetFrameNode() const = 0;
// Returns the underlying WorkerNode, if this context is a WorkerNode, or
// nullptr otherwise.
virtual const WorkerNode* GetWorkerNode() const = 0;
};
// An observer for ExecutionContexts.
class ExecutionContextObserver : public base::CheckedObserver {
public:
ExecutionContextObserver() = default;
ExecutionContextObserver(const ExecutionContextObserver&) = delete;
ExecutionContextObserver& operator=(const ExecutionContextObserver&) = delete;
~ExecutionContextObserver() override = default;
// Called when an ExecutionContext is added. The provided pointer is valid
// until a call to OnBeforeExecutionContextRemoved().
virtual void OnExecutionContextAdded(const ExecutionContext* ec) = 0;
// 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;
};
// A default implementation of ExecutionContextObserver with empty stubs for all
// functions.
class ExecutionContextObserverDefaultImpl : public ExecutionContextObserver {
public:
ExecutionContextObserverDefaultImpl() = default;
ExecutionContextObserverDefaultImpl(
const ExecutionContextObserverDefaultImpl&) = delete;
ExecutionContextObserverDefaultImpl& operator=(
const ExecutionContextObserverDefaultImpl&) = delete;
~ExecutionContextObserverDefaultImpl() override = default;
// ExecutionContextObserver implementation:
void OnExecutionContextAdded(const ExecutionContext* ec) override {}
void OnBeforeExecutionContextRemoved(const ExecutionContext* ec) override {}
};
} // namespace execution_context
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_H_
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_H_
#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_H_
#include "components/performance_manager/public/execution_context/execution_context_token.h"
#include "components/performance_manager/public/graph/frame_node.h"
namespace base {
class UnguessableToken;
} // namespace base
namespace performance_manager {
class Graph;
class WorkerNode;
namespace execution_context {
class ExecutionContext;
class ExecutionContextObserver;
// The ExecutionContextRegistry is a per-graph class that allows for observers
// to be registered, and for ExecutionContexts to be looked up by their tokens.
// An instance of the registry must be passed to the Graph prior to any nodes
// being created.
class ExecutionContextRegistry {
public:
ExecutionContextRegistry();
ExecutionContextRegistry(const ExecutionContextRegistry&) = delete;
ExecutionContextRegistry& operator=(const ExecutionContextRegistry&) = delete;
virtual ~ExecutionContextRegistry();
// Returns the instance of the ExecutionContextRegistry for the given
// Graph.
static ExecutionContextRegistry* GetFromGraph(Graph* graph);
// Adds an observer to the registry. The observer needs to be removed before
// the registry is torn down.
virtual void AddObserver(ExecutionContextObserver* observer) = 0;
// Removes an observer from the registry.
virtual void RemoveObserver(ExecutionContextObserver* observer) = 0;
// Looks up an ExecutionContext by token. Returns nullptr if no such context
// exists.
virtual const ExecutionContext* GetExecutionContextByToken(
const ExecutionContextToken& token) = 0;
// Does a typed lookup of a FrameNode ExecutionContext by FrameToken, returns
// nullptr if no such FrameNode exists.
virtual const FrameNode* GetFrameNodeByFrameToken(
const FrameToken& token) = 0;
// Does a typed lookup of a WorkerNode ExecutionContext by its DevToolsToken,
// returns nullptr if no such WorkerNode exists.
virtual const WorkerNode* GetWorkerNodeByDevToolsToken(
const base::UnguessableToken& token) = 0;
// Returns the ExecutionContext associated with a node.
virtual const ExecutionContext* GetExecutionContextForFrameNode(
const FrameNode* frame_node) = 0;
virtual const ExecutionContext* GetExecutionContextForWorkerNode(
const WorkerNode* worker_node) = 0;
};
} // namespace execution_context
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_REGISTRY_H_
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_TOKEN_H_
#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_TOKEN_H_
#include "base/unguessable_token.h"
#include "base/util/type_safety/strong_alias.h"
namespace performance_manager {
namespace execution_context {
using ExecutionContextToken =
util::StrongAlias<class ExecutionContextTokenTag, base::UnguessableToken>;
// These objects should have the identical size and layout; the StrongAlias
// wrapper is simply there to provide type safety. This allows us to
// safely reinterpret_cast behind the scenes, so we can continue to return
// references and pointers.
static_assert(sizeof(ExecutionContextToken) == sizeof(base::UnguessableToken),
"StrongAlias should not change object layout");
} // namespace execution_context
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_EXECUTION_CONTEXT_EXECUTION_CONTEXT_TOKEN_H_
......@@ -9,6 +9,7 @@
#include <memory>
#include <vector>
#include "base/dcheck_is_on.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
......@@ -121,6 +122,13 @@ class Graph {
virtual uintptr_t GetImplType() const = 0;
virtual const void* GetImpl() const = 0;
// Allows code that is not explicitly aware of the Graph sequence to determine
// if they are in fact on the right sequence. Prefer to use the
// DCHECK_ON_GRAPH_SEQUENCE macro.
#if DCHECK_IS_ON()
virtual bool IsOnGraphSequence() const = 0;
#endif
private:
// Retrieves the object with the given |type_id|, returning nullptr if none
// exists. Clients must use the GetRegisteredObjectAs wrapper instead.
......@@ -129,6 +137,13 @@ class Graph {
DISALLOW_COPY_AND_ASSIGN(Graph);
};
#if DCHECK_IS_ON()
#define DCHECK_ON_GRAPH_SEQUENCE(graph) DCHECK(graph->IsOnGraphSequence())
#else
// Compiles to a nop, and will eat ostream input.
#define DCHECK_ON_GRAPH_SEQUENCE(graph) DCHECK(true)
#endif
// Observer interface for the graph.
class GraphObserver {
public:
......
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