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