Use BuilderRecord::waiting_map_

Replace four pointer sets with a simple { ptr -> bool[4] }
map. These are used to track the dependents that are
waiting for a given record to change state.

This is a small preparation step for another CL that will
change how state change notifications are propagated during
resolution.

Benchmarking shows -1.5% PEAK RAM usage, `gn gen` performance
is unaffected.

Change-Id: I233c4515ce12a98a201461b8823f5af760bde41b
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/23140
Commit-Queue: David Turner <digit@google.com>
Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/src/gn/builder_record.cc b/src/gn/builder_record.cc
index 5c0b84d..8896925 100644
--- a/src/gn/builder_record.cc
+++ b/src/gn/builder_record.cc
@@ -71,10 +71,14 @@
 void BuilderRecord::AddDep(BuilderRecord* dep) {
   DCHECK(state_ == STATE_DEFINED);
   all_deps_.add(dep);
+  WaitInfo* info = nullptr;
   if (!dep->is_resolved()) {
     // This cannot be resolved yet if any of its standard dependencies is not
     // resolved.
-    if (dep->waiting_on_resolution_.add(this)) {
+    if (!info)
+      info = &dep->waiting_map_[this];
+    if (!info->wait_resolved) {
+      info->wait_resolved = true;
       unresolved_count_++;
       DEBUG_BUILDER_RECORD_LOG("-- AddDep waiting_on_resolution %s -> %s\n",
                                dep->ToDebugString().c_str(),
@@ -84,7 +88,10 @@
   if (!dep->is_finalized()) {
     // Finalization of the current record is blocked until the
     // dependency is finalized.
-    if (dep->waiting_on_finalization_.add(this)) {
+    if (!info)
+      info = &dep->waiting_map_[this];
+    if (!info->wait_finalized) {
+      info->wait_finalized = true;
       unfinalized_count_++;
       DEBUG_BUILDER_RECORD_LOG("-- AddDep waiting_on_finalization %s -> %s\n",
                                dep->ToDebugString().c_str(),
@@ -109,10 +116,14 @@
   //    where we might write the ninja file before the validation output path
   //    is computed.
   all_deps_.add(dep);
+  WaitInfo* info = nullptr;
   if (!dep->is_defined()) {
     // The record cannot be resolved yet if any of its validation dependencies
     // is not defined.
-    if (dep->waiting_on_validation_definition_.add(this)) {
+    if (!info)
+      info = &dep->waiting_map_[this];
+    if (!info->wait_validation_defined) {
+      info->wait_validation_defined = true;
       unresolved_count_++;
       DEBUG_BUILDER_RECORD_LOG(
           "-- AddValidationDep waiting_on_validation_definition %s -> %s\n",
@@ -122,7 +133,10 @@
   if (!dep->is_resolved()) {
     // This record cannot be finalized if any of its validation deps is not
     // resolved.
-    if (dep->waiting_on_validation_resolution_.add(this)) {
+    if (!info)
+      info = &dep->waiting_map_[this];
+    if (!info->wait_validation_resolved) {
+      info->wait_validation_resolved = true;
       unfinalized_count_++;
       DEBUG_BUILDER_RECORD_LOG(
           "-- AddValidationDep waiting_on_validation_resolution %s -> %s\n",
@@ -185,12 +199,14 @@
     const {
   std::vector<const BuilderRecord*> result;
   for (auto it = all_deps_.begin(); it.valid(); ++it) {
-    BuilderRecord* dep = *it;
-    if (dep->waiting_on_resolution_.contains(
-            const_cast<BuilderRecord*>(this)) ||
-        dep->waiting_on_validation_definition_.contains(
-            const_cast<BuilderRecord*>(this)))
+    const BuilderRecord* dep = *it;
+    auto wait_it = dep->waiting_map_.find(this);
+    if (wait_it == dep->waiting_map_.end())
+      continue;
+    if (wait_it->second.wait_resolved ||
+        wait_it->second.wait_validation_defined) {
       result.push_back(dep);
+    }
   }
   std::sort(result.begin(), result.end(), LabelCompare);
   return result;
@@ -224,4 +240,4 @@
   result += ")";
   return result;
 }
-#endif  // DEBUG_BUILDER_RECORD
\ No newline at end of file
+#endif  // DEBUG_BUILDER_RECORD
diff --git a/src/gn/builder_record.h b/src/gn/builder_record.h
index 9c3f0be..fd05fd8 100644
--- a/src/gn/builder_record.h
+++ b/src/gn/builder_record.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/containers/flat_map.h"
 #include "gn/item.h"
 #include "gn/location.h"
 #include "gn/pointer_set.h"
@@ -187,12 +188,13 @@
   // false, stop and return false.
   template <typename Func>
   bool NotifyDependentsWaitingOnValidationDefinition(Func&& func) {
-    BuilderRecordSet waiting_deps =
-        std::move(waiting_on_validation_definition_);
-    for (auto it = waiting_deps.begin(); it.valid(); ++it) {
-      BuilderRecord* waiting = *it;
-      if (waiting->OnDefinedValidationDep(this) && !func(waiting))
-        return false;
+    for (auto& pair : waiting_map_) {
+      if (pair.second.wait_validation_defined) {
+        pair.second.wait_validation_defined = false;
+        BuilderRecord* waiting = pair.first;
+        if (waiting->OnDefinedValidationDep(this) && !func(waiting))
+          return false;
+      }
     }
     return true;
   }
@@ -221,11 +223,13 @@
   // stop and return false.
   template <typename Func>
   bool NotifyDependentsWaitingOnResolution(Func&& func) {
-    BuilderRecordSet waiting_deps = std::move(waiting_on_resolution_);
-    for (auto it = waiting_deps.begin(); it.valid(); ++it) {
-      BuilderRecord* waiting = *it;
-      if (waiting->OnResolvedDep(this) && !func(waiting))
-        return false;
+    for (auto& pair : waiting_map_) {
+      if (pair.second.wait_resolved) {
+        pair.second.wait_resolved = false;
+        BuilderRecord* waiting = pair.first;
+        if (waiting->OnResolvedDep(this) && !func(waiting))
+          return false;
+      }
     }
     return true;
   }
@@ -235,12 +239,13 @@
   // |func| returns false, stop and return false.
   template <typename Func>
   bool NotifyDependentsWaitingOnValidationResolution(Func&& func) {
-    BuilderRecordSet waiting_deps =
-        std::move(waiting_on_validation_resolution_);
-    for (auto it = waiting_deps.begin(); it.valid(); ++it) {
-      BuilderRecord* waiting = *it;
-      if (waiting->OnResolvedValidationDep(this) && !func(waiting))
-        return false;
+    for (auto& pair : waiting_map_) {
+      if (pair.second.wait_validation_resolved) {
+        pair.second.wait_validation_resolved = false;
+        BuilderRecord* waiting = pair.first;
+        if (waiting->OnResolvedValidationDep(this) && !func(waiting))
+          return false;
+      }
     }
     return true;
   }
@@ -263,11 +268,13 @@
   // stop and return false.
   template <typename Func>
   bool NotifyDependentsWaitingOnFinalization(Func&& func) {
-    BuilderRecordSet waiting_deps = std::move(waiting_on_finalization_);
-    for (auto it = waiting_deps.begin(); it.valid(); ++it) {
-      BuilderRecord* waiting = *it;
-      if (waiting->OnFinalizedDep(this) && !func(waiting))
-        return false;
+    for (auto& pair : waiting_map_) {
+      if (pair.second.wait_finalized) {
+        pair.second.wait_finalized = false;
+        BuilderRecord* waiting = pair.first;
+        if (waiting->OnFinalizedDep(this) && !func(waiting))
+          return false;
+      }
     }
     return true;
   }
@@ -281,30 +288,40 @@
   // as a list sorted by label.
   std::vector<const BuilderRecord*> GetSortedUnresolvedDeps() const;
 
+  // Used by unit-tests.
+
   // Records that are waiting on this one to be defined. This is used for
   // "validations" dependencies which don't require the target to be fully
   // resolved, only defined.
-  BuilderRecordSet& waiting_on_validation_definition() {
-    return waiting_on_validation_definition_;
-  }
-  const BuilderRecordSet& waiting_on_validation_definition() const {
-    return waiting_on_validation_definition_;
+  BuilderRecordSet waiting_on_validation_definition() const {
+    BuilderRecordSet result;
+    for (const auto& pair : waiting_map_) {
+      if (pair.second.wait_validation_defined)
+        result.add(pair.first);
+    }
+    return result;
   }
 
   // Records that are waiting on this one to be resolved. This is the other
   // end of the "unresolved deps" arrow for standard dependencies.
-  BuilderRecordSet& waiting_on_resolution() { return waiting_on_resolution_; }
-  const BuilderRecordSet& waiting_on_resolution() const {
-    return waiting_on_resolution_;
+  BuilderRecordSet waiting_on_resolution() const {
+    BuilderRecordSet result;
+    for (const auto& pair : waiting_map_) {
+      if (pair.second.wait_resolved)
+        result.add(pair.first);
+    }
+    return result;
   }
 
   // Records that are waiting on this one to be resolved before they can be
-  // written to the ninja file. This is used for "validations" dependencies.
-  BuilderRecordSet& waiting_on_validation_resolution() {
-    return waiting_on_validation_resolution_;
-  }
-  const BuilderRecordSet& waiting_on_validation_resolution() const {
-    return waiting_on_validation_resolution_;
+  // written to the Ninja file. This is used for "validations" dependencies.
+  BuilderRecordSet waiting_on_validation_resolution() const {
+    BuilderRecordSet result;
+    for (const auto& pair : waiting_map_) {
+      if (pair.second.wait_validation_resolved)
+        result.add(pair.first);
+    }
+    return result;
   }
 
   // Comparator function used to sort records from their label.
@@ -357,10 +374,16 @@
   size_t unfinalized_count_ = 0;
 
   BuilderRecordSet all_deps_;
-  BuilderRecordSet waiting_on_resolution_;
-  BuilderRecordSet waiting_on_finalization_;
-  BuilderRecordSet waiting_on_validation_definition_;
-  BuilderRecordSet waiting_on_validation_resolution_;
+
+  // A specialized { dependent -> wait_info } map used to track
+  // which state changes dependents of this record are waiting for.
+  struct WaitInfo {
+    bool wait_resolved = false;
+    bool wait_finalized = false;
+    bool wait_validation_defined = false;
+    bool wait_validation_resolved = false;
+  };
+  base::flat_map<BuilderRecord*, WaitInfo> waiting_map_;
 
   BuilderRecord(const BuilderRecord&) = delete;
   BuilderRecord& operator=(const BuilderRecord&) = delete;