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