Blocked decoding part 1: QpackHeaderTable::Observer.

gfe-relnote: n/a, QUIC v99-only change.
PiperOrigin-RevId: 257104083
Change-Id: I6209b6f2be59b32fba1790765a00abee7b75bfe2
diff --git a/quic/core/qpack/qpack_header_table.cc b/quic/core/qpack/qpack_header_table.cc
index 0194330..9522666 100644
--- a/quic/core/qpack/qpack_header_table.cc
+++ b/quic/core/qpack/qpack_header_table.cc
@@ -140,6 +140,14 @@
     CHECK(result.second);
   }
 
+  // Notify and deregister observers whose threshold is met, if any.
+  while (!observers_.empty() &&
+         observers_.top().required_insert_count <= inserted_entry_count()) {
+    Observer* observer = observers_.top().observer;
+    observers_.pop();
+    observer->OnInsertCountReachedThreshold();
+  }
+
   return new_entry;
 }
 
@@ -169,6 +177,17 @@
   max_entries_ = maximum_dynamic_table_capacity / 32;
 }
 
+void QpackHeaderTable::RegisterObserver(Observer* observer,
+                                        uint64_t required_insert_count) {
+  DCHECK_GT(required_insert_count, 0u);
+  observers_.push({observer, required_insert_count});
+}
+
+bool QpackHeaderTable::ObserverWithThreshold::operator>(
+    const ObserverWithThreshold& other) const {
+  return required_insert_count > other.required_insert_count;
+}
+
 void QpackHeaderTable::EvictDownToCurrentCapacity() {
   while (dynamic_table_size_ > dynamic_table_capacity_) {
     DCHECK(!dynamic_entries_.empty());
diff --git a/quic/core/qpack/qpack_header_table.h b/quic/core/qpack/qpack_header_table.h
index eda7ab5..39d7326 100644
--- a/quic/core/qpack/qpack_header_table.h
+++ b/quic/core/qpack/qpack_header_table.h
@@ -6,6 +6,9 @@
 #define QUICHE_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
 
 #include <cstdint>
+#include <functional>
+#include <queue>
+#include <vector>
 
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
@@ -30,6 +33,17 @@
   // Result of header table lookup.
   enum class MatchType { kNameAndValue, kName, kNoMatch };
 
+  // Observer interface for dynamic table insertion.
+  class Observer {
+   public:
+    virtual ~Observer() = default;
+
+    // Called when inserted_entry_count() reaches the threshold the Observer was
+    // registered with.  After this call the Observer automatically gets
+    // deregistered.
+    virtual void OnInsertCountReachedThreshold() = 0;
+  };
+
   QpackHeaderTable();
   QpackHeaderTable(const QpackHeaderTable&) = delete;
   QpackHeaderTable& operator=(const QpackHeaderTable&) = delete;
@@ -68,6 +82,11 @@
   // This method must only be called at most once.
   void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity);
 
+  // Register an observer to be notified when inserted_entry_count() reaches
+  // |required_insert_count|.  After the notification, |observer| automatically
+  // gets unregistered.
+  void RegisterObserver(Observer* observer, uint64_t required_insert_count);
+
   // Used on request streams to encode and decode Required Insert Count.
   uint64_t max_entries() const { return max_entries_; }
 
@@ -137,6 +156,22 @@
 
   // The number of entries dropped from the dynamic table.
   uint64_t dropped_entry_count_;
+
+  // Data structure to hold an Observer and its threshold.
+  struct ObserverWithThreshold {
+    Observer* observer;
+    uint64_t required_insert_count;
+    bool operator>(const ObserverWithThreshold& other) const;
+  };
+
+  // Use std::greater so that entry with smallest |required_insert_count|
+  // is on top.
+  using ObserverHeap = std::priority_queue<ObserverWithThreshold,
+                                           std::vector<ObserverWithThreshold>,
+                                           std::greater<ObserverWithThreshold>>;
+
+  // Observers waiting to be notified.
+  ObserverHeap observers_;
 };
 
 }  // namespace quic
diff --git a/quic/core/qpack/qpack_header_table_test.cc b/quic/core/qpack/qpack_header_table_test.cc
index 6f9009d..ff7ef48 100644
--- a/quic/core/qpack/qpack_header_table_test.cc
+++ b/quic/core/qpack/qpack_header_table_test.cc
@@ -9,12 +9,22 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
 
+using ::testing::Mock;
+using ::testing::StrictMock;
+
 namespace quic {
 namespace test {
 namespace {
 
 const uint64_t kMaximumDynamicTableCapacityForTesting = 1024 * 1024;
 
+class MockObserver : public QpackHeaderTable::Observer {
+ public:
+  ~MockObserver() override = default;
+
+  MOCK_METHOD0(OnInsertCountReachedThreshold, void());
+};
+
 class QpackHeaderTableTest : public QuicTest {
  protected:
   QpackHeaderTableTest() {
@@ -78,6 +88,11 @@
     return table_.SetDynamicTableCapacity(capacity);
   }
 
+  void RegisterObserver(QpackHeaderTable::Observer* observer,
+                        uint64_t required_insert_count) {
+    table_.RegisterObserver(observer, required_insert_count);
+  }
+
   uint64_t max_entries() const { return table_.max_entries(); }
   uint64_t inserted_entry_count() const {
     return table_.inserted_entry_count();
@@ -350,6 +365,45 @@
               /* expected_is_static = */ false, 2u);
 }
 
+TEST_F(QpackHeaderTableTest, Observer) {
+  StrictMock<MockObserver> observer1;
+  RegisterObserver(&observer1, 1);
+  EXPECT_CALL(observer1, OnInsertCountReachedThreshold);
+  InsertEntry("foo", "bar");
+  EXPECT_EQ(1u, inserted_entry_count());
+  Mock::VerifyAndClearExpectations(&observer1);
+
+  // Registration order does not matter.
+  StrictMock<MockObserver> observer2;
+  StrictMock<MockObserver> observer3;
+  RegisterObserver(&observer3, 3);
+  RegisterObserver(&observer2, 2);
+
+  EXPECT_CALL(observer2, OnInsertCountReachedThreshold);
+  InsertEntry("foo", "bar");
+  EXPECT_EQ(2u, inserted_entry_count());
+  Mock::VerifyAndClearExpectations(&observer3);
+
+  EXPECT_CALL(observer3, OnInsertCountReachedThreshold);
+  InsertEntry("foo", "bar");
+  EXPECT_EQ(3u, inserted_entry_count());
+  Mock::VerifyAndClearExpectations(&observer2);
+
+  // Multiple observers with identical |required_insert_count| should all be
+  // notified.
+  StrictMock<MockObserver> observer4;
+  StrictMock<MockObserver> observer5;
+  RegisterObserver(&observer4, 4);
+  RegisterObserver(&observer5, 4);
+
+  EXPECT_CALL(observer4, OnInsertCountReachedThreshold);
+  EXPECT_CALL(observer5, OnInsertCountReachedThreshold);
+  InsertEntry("foo", "bar");
+  EXPECT_EQ(4u, inserted_entry_count());
+  Mock::VerifyAndClearExpectations(&observer4);
+  Mock::VerifyAndClearExpectations(&observer5);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic