blob: 00ee4f768523e932347c7ea538555e998e7cd853 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_GRAPH_NODE_INLINE_DATA_H_
#define COMPONENTS_PERFORMANCE_MANAGER_GRAPH_NODE_INLINE_DATA_H_
#include <tuple>
#include <utility>
#include "base/types/pass_key.h"
#include "components/performance_manager/graph/node_inline_data_impl.h"
namespace performance_manager {
// Helper classes for defining a user data class that is associated with nodes
// in the graph. At most one instance of each type of data may exist per node
// in the graph.
//
// An instance of node inline data has to be specifically permitted to be
// associated with individual node types, with this being enforced at compile
// time via the type system. A storage type must be specified which may be one
// of the following:
//
// - NodeInlineData<T>: Directly stored as a member in the node.
//
// - SparseNodeInlineData<T>: Stored in a unique_ptr<> as a member in the node.
//
// If the data is needed for most instances of a node type and for effectively
// the entire lifetime of the node, use NodeInlineData<T>.
//
// If the data is needed for most instances of a node type, but only for a
// relatively small portion of the node's lifetime, use
// SparseNodeInlineData<T>. The type will be stored as a member on the node, and
// as such it will be slightly faster to access but will always occupy its
// memory footprint, whether it is created or not.
//
// If the data is *not* needed for most instances of a node type, use
// NodeAttachedData<T> (See node_attached_data.h). The type will be stored in a
// unique_ptr, and will thus less memory is allocated when the type is not
// created.
//
// The functionality of this file is intended to be used as follows:
//
// First, add your data type to the node type (or types) that you want to
// associate your data with.
//
// For example:
//
// -- process_node_impl.h --
//
// class ProcessNodeImpl : (...)
// (...)
// public SupportsNodeInlineData<DataType1,
// DataType2,
// ----> YourDataType> <----
//
// -- process_node_impl.h --
//
// Second, derive your data type from one of NodeInlineData<T> or
// SparseNodeInlineData<T>:
//
// -- your_data_type.h --
//
// class YourDataType : public SparseNodeInlineData<YourDataType> {
// (...)
// };
//
// -- your_data_type.h --
//
// Once defined, your type instance can be created like so:
//
// YourDataType& your_data_type = YourDataType::Create(node_impl, ..args..);
//
// If the instance already exists, you can access it using Get():
//
// YourDataType& your_data_type = YourDataType::Get(node_impl);
//
// Optionally, you can preemptively destroy the instance once it is no longer
// needed:
//
// YourDataType::Destroy(node_impl);
template <class T>
class NodeInlineData {
public:
using PassKey = base::PassKey<NodeInlineData>;
// Returns true if the instance exists for this type.
template <class NodeImplClass>
static bool Exists(NodeImplClass* node);
// Retrieves the instance associated with `node`. Asserts that it exists.
// `Create()` must have been called before calling this.
template <class NodeImplClass>
static T& Get(NodeImplClass* node);
// Retrieves the instance associated with `node`. Asserts that it exists.
// `Create()` must have been called before calling this.
template <class NodeImplClass>
static const T& Get(const NodeImplClass* node);
// Creates the instance. `args` is passed as arguments to the constructor of
// `T`. If `T`'s first argument is the node type, `node` is also passed as an
// argument to the constructor.
template <class NodeImplClass, class... Args>
requires std::is_constructible_v<T, Args...>
static T& Create(NodeImplClass* node, Args&&... args);
template <class NodeImplClass, class... Args>
requires std::is_constructible_v<T, NodeImplClass*, Args...>
static T& Create(NodeImplClass* node, Args&&... args);
// Destroys the instance associated with `node`. Asserts that it exists. Note
// that calling this is optional. The instance will get cleaned up
// automatically when the node is destroyed.
template <class NodeImplClass>
static void Destroy(NodeImplClass* node);
};
template <class T>
class SparseNodeInlineData : public NodeInlineData<T> {};
// Derived by node implementation classes to provide access to inline data.
// Private through the use of base::PassKey so that only the relevant
// NodeInlineData<T> can use it.
template <class... Ts>
class SupportsNodeInlineData {
public:
template <class T>
bool NodeDataExists(base::PassKey<NodeInlineData<T>>);
template <class T>
T& GetNodeData(base::PassKey<NodeInlineData<T>>);
template <class T>
const T& GetNodeData(base::PassKey<NodeInlineData<T>>) const;
template <class T, class... Args>
T& CreateNodeData(base::PassKey<NodeInlineData<T>>, Args&&... args);
template <class T>
void DestroyNodeData(base::PassKey<NodeInlineData<T>>);
protected:
virtual ~SupportsNodeInlineData() = default;
void DestroyNodeInlineDataStorage();
private:
template <class T>
internal::Storage<T>& GetStorage();
template <class T>
const internal::Storage<T>& GetStorage() const;
using UnderlyingStorage = std::tuple<internal::Storage<Ts>...>;
UnderlyingStorage storage_;
};
///////////////////////////////////////////////////////////
// Everything below this point is implementation detail! //
///////////////////////////////////////////////////////////
// Implementation of NodeInlineData<T>:
// static
template <class T>
template <class NodeImplClass>
bool NodeInlineData<T>::Exists(NodeImplClass* node) {
return node->NodeDataExists(PassKey());
}
// static
template <class T>
template <class NodeImplClass>
T& NodeInlineData<T>::Get(NodeImplClass* node) {
return node->GetNodeData(PassKey());
}
// static
template <class T>
template <class NodeImplClass>
const T& NodeInlineData<T>::Get(const NodeImplClass* node) {
return node->GetNodeData(PassKey());
}
// static
template <class T>
template <class NodeImplClass, class... Args>
requires std::is_constructible_v<T, Args...>
T& NodeInlineData<T>::Create(NodeImplClass* node, Args&&... args) {
return node->CreateNodeData(PassKey(), std::forward<Args>(args)...);
}
// static
template <class T>
template <class NodeImplClass, class... Args>
requires std::is_constructible_v<T, NodeImplClass*, Args...>
T& NodeInlineData<T>::Create(NodeImplClass* node, Args&&... args) {
return node->CreateNodeData(PassKey(), node, std::forward<Args>(args)...);
}
// static
template <class T>
template <class NodeImplClass>
void NodeInlineData<T>::Destroy(NodeImplClass* node) {
node->DestroyNodeData(PassKey());
}
// Implementation of SupportsNodeInlineData<Ts...>:
template <class... Ts>
template <class T>
bool SupportsNodeInlineData<Ts...>::NodeDataExists(
base::PassKey<NodeInlineData<T>>) {
return GetStorage<T>().Exists();
}
template <class... Ts>
template <class T>
T& SupportsNodeInlineData<Ts...>::GetNodeData(
base::PassKey<NodeInlineData<T>>) {
return GetStorage<T>().Get();
}
template <class... Ts>
template <class T>
const T& SupportsNodeInlineData<Ts...>::GetNodeData(
base::PassKey<NodeInlineData<T>>) const {
return GetStorage<T>().Get();
}
template <class... Ts>
template <class T, class... Args>
T& SupportsNodeInlineData<Ts...>::CreateNodeData(
base::PassKey<NodeInlineData<T>>,
Args&&... args) {
return GetStorage<T>().Create(std::forward<Args>(args)...);
}
template <class... Ts>
template <class T>
void SupportsNodeInlineData<Ts...>::DestroyNodeData(
base::PassKey<NodeInlineData<T>>) {
GetStorage<T>().Destroy();
}
template <class... Ts>
void SupportsNodeInlineData<Ts...>::DestroyNodeInlineDataStorage() {
storage_ = UnderlyingStorage();
}
template <class... Ts>
template <class T>
internal::Storage<T>& SupportsNodeInlineData<Ts...>::GetStorage() {
return std::get<internal::Storage<T>>(storage_);
}
template <class... Ts>
template <class T>
const internal::Storage<T>& SupportsNodeInlineData<Ts...>::GetStorage() const {
return std::get<internal::Storage<T>>(storage_);
}
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_GRAPH_NODE_INLINE_DATA_H_