[PM] Create GraphOwned helper class.

This provides functionality that allows external clients to tie lifetimes
of their objects directly to that of a graph instance. Can be used in a
way similar to PerformanceManager::RegisterObserver, but more generically.
This will ease the transition of legacy observers to the new public
interfaces.

BUG=910288

Change-Id: I22cd06ba1a6b8a19c4bd76b05c0523c2f76ee421
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1665548
Commit-Queue: Chris Hamilton <[email protected]>
Reviewed-by: Sigurður Ásgeirsson <[email protected]>
Cr-Commit-Position: refs/heads/master@{#673114}
diff --git a/chrome/browser/performance_manager/graph/graph.cc b/chrome/browser/performance_manager/graph/graph.cc
index 27a6cf9e..ce08169 100644
--- a/chrome/browser/performance_manager/graph/graph.cc
+++ b/chrome/browser/performance_manager/graph/graph.cc
@@ -12,4 +12,10 @@
 GraphObserver::GraphObserver() = default;
 GraphObserver::~GraphObserver() = default;
 
+GraphOwned::GraphOwned() = default;
+GraphOwned::~GraphOwned() = default;
+
+GraphOwnedDefaultImpl::GraphOwnedDefaultImpl() = default;
+GraphOwnedDefaultImpl::~GraphOwnedDefaultImpl() = default;
+
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/graph_impl.cc b/chrome/browser/performance_manager/graph/graph_impl.cc
index 8c397fdb..858951e4 100644
--- a/chrome/browser/performance_manager/graph/graph_impl.cc
+++ b/chrome/browser/performance_manager/graph/graph_impl.cc
@@ -61,6 +61,8 @@
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
+// TODO(chrisha|siggi): Move this to a TearDown, which is invoked by the
+// performance manager before the graph destructor.
 GraphImpl::~GraphImpl() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -68,6 +70,18 @@
   for (auto* observer : graph_observers_)
     observer->OnBeforeGraphDestroyed(this);
 
+  // Clean up graph owned objects. This causes their TakeFromGraph callbacks to
+  // be invoked, and ideally they clean up any observers they may have, etc.
+  while (!graph_owned_.empty())
+    auto object = TakeFromGraph(graph_owned_.begin()->first);
+
+  // At this point, all typed observers should be empty.
+  DCHECK(graph_observers_.empty());
+  DCHECK(frame_node_observers_.empty());
+  DCHECK(page_node_observers_.empty());
+  DCHECK(process_node_observers_.empty());
+  DCHECK(system_node_observers_.empty());
+
   // All observers should have been removed before the graph is deleted.
   // TODO(chrisha): This will disappear, as new observers are allowed to stay
   // attached at graph death.
@@ -131,6 +145,26 @@
   RemoveObserverImpl(&system_node_observers_, observer);
 }
 
+void GraphImpl::PassToGraph(std::unique_ptr<GraphOwned> graph_owned) {
+  auto* raw = graph_owned.get();
+  DCHECK(!base::Contains(graph_owned_, raw));
+  graph_owned_.insert(std::make_pair(raw, std::move(graph_owned)));
+  raw->OnPassedToGraph(this);
+}
+
+std::unique_ptr<GraphOwned> GraphImpl::TakeFromGraph(GraphOwned* graph_owned) {
+  std::unique_ptr<GraphOwned> object;
+  auto it = graph_owned_.find(graph_owned);
+  if (it != graph_owned_.end()) {
+    DCHECK_EQ(graph_owned, it->first);
+    DCHECK_EQ(graph_owned, it->second.get());
+    object = std::move(it->second);
+    graph_owned_.erase(it);
+    object->OnTakenFromGraph(this);
+  }
+  return object;
+}
+
 void GraphImpl::RegisterObserver(GraphImplObserver* observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   observer->SetGraph(this);
diff --git a/chrome/browser/performance_manager/graph/graph_impl.h b/chrome/browser/performance_manager/graph/graph_impl.h
index 780ef83..b7bf449 100644
--- a/chrome/browser/performance_manager/graph/graph_impl.h
+++ b/chrome/browser/performance_manager/graph/graph_impl.h
@@ -13,6 +13,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/process/process_handle.h"
 #include "base/sequence_checker.h"
@@ -57,6 +58,8 @@
   void RemovePageNodeObserver(PageNodeObserver* observer) override;
   void RemoveProcessNodeObserver(ProcessNodeObserver* observer) override;
   void RemoveSystemNodeObserver(SystemNodeObserver* observer) override;
+  void PassToGraph(std::unique_ptr<GraphOwned> graph_owned) override;
+  std::unique_ptr<GraphOwned> TakeFromGraph(GraphOwned* graph_owned) override;
   uintptr_t GetImplType() const override;
   const void* GetImpl() const override;
 
@@ -106,6 +109,9 @@
   // Allows explicitly invoking SystemNode destruction for testing.
   void ReleaseSystemNodeForTesting() { ReleaseSystemNode(); }
 
+  // Returns the number of objects in the |graph_owned_| map, for testing.
+  size_t GraphOwnedCountForTesting() const { return graph_owned_.size(); }
+
  protected:
   friend class NodeBase;
 
@@ -149,6 +155,10 @@
   std::vector<ProcessNodeObserver*> process_node_observers_;
   std::vector<SystemNodeObserver*> system_node_observers_;
 
+  // Graph-owned objects. For now we only expect O(10) clients, hence the
+  // flat_map.
+  base::flat_map<GraphOwned*, std::unique_ptr<GraphOwned>> graph_owned_;
+
   // User data storage for the graph.
   friend class NodeAttachedDataMapHelper;
   using NodeAttachedDataKey = std::pair<const Node*, const void*>;
diff --git a/chrome/browser/performance_manager/graph/graph_impl_unittest.cc b/chrome/browser/performance_manager/graph/graph_impl_unittest.cc
index dc62b1a..3e7d222 100644
--- a/chrome/browser/performance_manager/graph/graph_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/graph_impl_unittest.cc
@@ -138,7 +138,7 @@
   LenientMockObserver() {}
   ~LenientMockObserver() override {}
 
-  MOCK_METHOD1(OnBeforeGraphDestroyed, void(const Graph*));
+  MOCK_METHOD1(OnBeforeGraphDestroyed, void(Graph*));
 };
 
 using MockObserver = ::testing::StrictMock<LenientMockObserver>;
@@ -150,15 +150,79 @@
 
 TEST(GraphImplTest, ObserverWorks) {
   std::unique_ptr<GraphImpl> graph = base::WrapUnique(new GraphImpl());
-  const Graph* raw_graph = graph.get();
+  Graph* raw_graph = graph.get();
 
   MockObserver obs;
   graph->AddGraphObserver(&obs);
   graph->RemoveGraphObserver(&obs);
   graph->AddGraphObserver(&obs);
 
-  EXPECT_CALL(obs, OnBeforeGraphDestroyed(raw_graph));
+  // Expect the graph teardown callback to be invoked. We have to unregister our
+  // observer in order to maintain graph invariants.
+  EXPECT_CALL(obs, OnBeforeGraphDestroyed(raw_graph))
+      .WillOnce(testing::Invoke(
+          [&obs](Graph* graph) { graph->RemoveGraphObserver(&obs); }));
   graph.reset();
 }
 
+namespace {
+
+class Foo : public GraphOwned {
+ public:
+  explicit Foo(int* destructor_count) : destructor_count_(destructor_count) {}
+
+  ~Foo() override { (*destructor_count_)++; }
+
+  // GraphOwned implementation:
+  void OnPassedToGraph(Graph* graph) override { passed_to_called_ = true; }
+  void OnTakenFromGraph(Graph* graph) override { taken_from_called_ = true; }
+
+  bool passed_to_called() const { return passed_to_called_; }
+  bool taken_from_called() const { return taken_from_called_; }
+
+ private:
+  bool passed_to_called_ = false;
+  bool taken_from_called_ = false;
+  int* destructor_count_ = nullptr;
+};
+
+}  // namespace
+
+TEST(GraphImplTest, GraphOwned) {
+  int destructor_count = 0;
+
+  std::unique_ptr<Foo> foo1 = base::WrapUnique(new Foo(&destructor_count));
+  std::unique_ptr<Foo> foo2 = base::WrapUnique(new Foo(&destructor_count));
+  auto* raw1 = foo1.get();
+  auto* raw2 = foo2.get();
+
+  // Pass both objects to the graph.
+  std::unique_ptr<GraphImpl> graph = base::WrapUnique(new GraphImpl());
+  EXPECT_EQ(0u, graph->GraphOwnedCountForTesting());
+  EXPECT_FALSE(raw1->passed_to_called());
+  graph->PassToGraph(std::move(foo1));
+  EXPECT_TRUE(raw1->passed_to_called());
+  EXPECT_EQ(1u, graph->GraphOwnedCountForTesting());
+  EXPECT_FALSE(raw2->passed_to_called());
+  graph->PassToGraph(std::move(foo2));
+  EXPECT_TRUE(raw2->passed_to_called());
+  EXPECT_EQ(2u, graph->GraphOwnedCountForTesting());
+
+  // Take one back.
+  EXPECT_FALSE(raw1->taken_from_called());
+  foo1 = graph->TakeFromGraphAs<Foo>(raw1);
+  EXPECT_TRUE(raw1->taken_from_called());
+  EXPECT_EQ(1u, graph->GraphOwnedCountForTesting());
+
+  // Destroy that object and expect its destructor to have been invoked.
+  EXPECT_EQ(0, destructor_count);
+  foo1.reset();
+  EXPECT_EQ(1, destructor_count);
+
+  // Now destroy the graph and expect the other object to have been torn down
+  // too.
+  graph.reset();
+  EXPECT_EQ(2, destructor_count);
+}
+
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/public/graph/graph.h b/chrome/browser/performance_manager/public/graph/graph.h
index 5bceb6c0..0cc76500 100644
--- a/chrome/browser/performance_manager/public/graph/graph.h
+++ b/chrome/browser/performance_manager/public/graph/graph.h
@@ -6,12 +6,15 @@
 #define CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_H_
 
 #include <cstdint>
+#include <memory>
 
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 
 namespace performance_manager {
 
 class GraphObserver;
+class GraphOwned;
 class FrameNodeObserver;
 class PageNodeObserver;
 class ProcessNodeObserver;
@@ -42,6 +45,21 @@
   virtual void RemoveProcessNodeObserver(ProcessNodeObserver* observer) = 0;
   virtual void RemoveSystemNodeObserver(SystemNodeObserver* observer) = 0;
 
+  // For convenience, allows you to pass ownership of an object to the graph.
+  // Useful for attaching observers that will live with the graph until it dies.
+  // If you can name the object you can also take it back via "TakeFromGraph".
+  virtual void PassToGraph(std::unique_ptr<GraphOwned> graph_owned) = 0;
+  virtual std::unique_ptr<GraphOwned> TakeFromGraph(
+      GraphOwned* graph_owned) = 0;
+
+  // A TakeFromGraph helper that casts to a derived type. It is up to the caller
+  // to ensure that the cast is safe.
+  template <typename DerivedType>
+  std::unique_ptr<DerivedType> TakeFromGraphAs(GraphOwned* graph_owned) {
+    return base::WrapUnique(
+        static_cast<DerivedType*>(TakeFromGraph(graph_owned).release()));
+  }
+
   // The following functions are implementation detail and should not need to be
   // used by external clients. They provide the ability to safely downcast to
   // the underlying implementation.
@@ -59,18 +77,48 @@
   virtual ~GraphObserver();
 
   // Called before the |graph| associated with this observer disappears. This
-  // allows the observer to do any necessary cleanup work. Note that the graph
-  // is in its destructor while this is being called, so the observer should
-  // refrain from uselessly modifying the graph. This is intended to be used to
-  // facilitate lifetime management of observers.
-  // TODO(chrisha): Make this run before the constructor!
+  // allows the observer to do any necessary cleanup work. Note that the
+  // observer should remove itself from observing the graph using this
+  // callback.
+  // TODO(chrisha): Make this run before the destructor!
   // crbug.com/966840
-  virtual void OnBeforeGraphDestroyed(const Graph* graph) = 0;
+  virtual void OnBeforeGraphDestroyed(Graph* graph) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(GraphObserver);
 };
 
+// Helper class for passing ownership of objects to a graph.
+class GraphOwned {
+ public:
+  GraphOwned();
+  virtual ~GraphOwned();
+
+  // Called when the object is passed into the graph.
+  virtual void OnPassedToGraph(Graph* graph) = 0;
+
+  // Called when the object is removed from the graph, either via an explicit
+  // call to Graph::TakeFromGraph, or prior to the Graph being destroyed.
+  virtual void OnTakenFromGraph(Graph* graph) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GraphOwned);
+};
+
+// A default implementation of GraphOwned.
+class GraphOwnedDefaultImpl : public GraphOwned {
+ public:
+  GraphOwnedDefaultImpl();
+  ~GraphOwnedDefaultImpl() override;
+
+  // GraphOwned implementation:
+  void OnPassedToGraph(Graph* graph) override {}
+  void OnTakenFromGraph(Graph* graph) override {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GraphOwnedDefaultImpl);
+};
+
 }  // namespace performance_manager
 
 #endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_PUBLIC_GRAPH_GRAPH_H_