Track unacknowledged references on the encoder stream in QpackBlockingManager. gfe-relnote: n/a, change to QUIC v99-only code. Protected by existing disabled gfe2_reloadable_flag_quic_enable_version_99. PiperOrigin-RevId: 263116461 Change-Id: I928d3b06dfbc8de7ad8995b739f49c65856237b2
diff --git a/quic/core/qpack/qpack_blocking_manager.cc b/quic/core/qpack/qpack_blocking_manager.cc index e8e4f87..04d824f 100644 --- a/quic/core/qpack/qpack_blocking_manager.cc +++ b/quic/core/qpack/qpack_blocking_manager.cc
@@ -23,7 +23,7 @@ const uint64_t required_index_count = RequiredInsertCount(indices); if (known_received_count_ < required_index_count) { - known_received_count_ = required_index_count; + IncreaseKnownReceivedCountTo(required_index_count); } DecreaseReferenceCounts(indices); @@ -50,7 +50,7 @@ } void QpackBlockingManager::OnInsertCountIncrement(uint64_t increment) { - known_received_count_ += increment; + IncreaseKnownReceivedCountTo(known_received_count_ + increment); } void QpackBlockingManager::OnHeaderBlockSent(QuicStreamId stream_id, @@ -61,6 +61,16 @@ header_blocks_[stream_id].push_back(std::move(indices)); } +void QpackBlockingManager::OnReferenceSentOnEncoderStream( + uint64_t inserted_index, + uint64_t referred_index) { + auto result = unacked_encoder_stream_references_.insert( + {inserted_index, referred_index}); + // Each dynamic table entry can refer to at most one |referred_index|. + DCHECK(result.second); + IncreaseReferenceCounts({referred_index}); +} + uint64_t QpackBlockingManager::blocked_stream_count() const { uint64_t blocked_stream_count = 0; for (const auto& header_blocks_for_stream : header_blocks_) { @@ -86,6 +96,26 @@ return *indices.rbegin() + 1; } +void QpackBlockingManager::IncreaseKnownReceivedCountTo( + uint64_t new_known_received_count) { + DCHECK_GT(new_known_received_count, known_received_count_); + + known_received_count_ = new_known_received_count; + + // Remove referred indices with key less than new Known Received Count from + // |unacked_encoder_stream_references_| and |entry_reference_counts_|. + IndexSet acknowledged_references; + auto it = unacked_encoder_stream_references_.begin(); + while (it != unacked_encoder_stream_references_.end() && + it->first < known_received_count_) { + acknowledged_references.insert(it->second); + ++it; + } + unacked_encoder_stream_references_.erase( + unacked_encoder_stream_references_.begin(), it); + DecreaseReferenceCounts(acknowledged_references); +} + void QpackBlockingManager::IncreaseReferenceCounts(const IndexSet& indices) { for (const uint64_t index : indices) { auto it = entry_reference_counts_.lower_bound(index);
diff --git a/quic/core/qpack/qpack_blocking_manager.h b/quic/core/qpack/qpack_blocking_manager.h index 00669af..7acfd5a 100644 --- a/quic/core/qpack/qpack_blocking_manager.h +++ b/quic/core/qpack/qpack_blocking_manager.h
@@ -41,6 +41,12 @@ // entries with |indices|. |indices| must not be empty. void OnHeaderBlockSent(QuicStreamId stream_id, IndexSet indices); + // Called when sending Insert With Name Reference or Duplicate instruction on + // encoder stream, inserting entry |inserted_index| referring to + // |referred_index|. + void OnReferenceSentOnEncoderStream(uint64_t inserted_index, + uint64_t referred_index); + // Returns the number of blocked streams. uint64_t blocked_stream_count() const; @@ -64,6 +70,11 @@ using HeaderBlocksForStream = std::list<IndexSet>; using HeaderBlocks = QuicUnorderedMap<QuicStreamId, HeaderBlocksForStream>; + // Increases |known_received_count_| to |new_known_received_count|, which must + // me larger than |known_received_count_|. Removes acknowledged references + // from |unacked_encoder_stream_references_|. + void IncreaseKnownReceivedCountTo(uint64_t new_known_received_count); + // Increase or decrease the reference count for each index in |indices|. void IncreaseReferenceCounts(const IndexSet& indices); void DecreaseReferenceCounts(const IndexSet& indices); @@ -72,7 +83,13 @@ // Must not contain a stream id with an empty queue. HeaderBlocks header_blocks_; - // Number of references in |header_blocks_| for each entry index. + // Unacknowledged references on the encoder stream. + // The key is the absolute index of the inserted entry, + // the mapped value is the absolute index of the entry referred. + std::map<uint64_t, uint64_t> unacked_encoder_stream_references_; + + // Number of references in |header_blocks_| and + // |unacked_encoder_stream_references_| for each entry index. std::map<uint64_t, uint64_t> entry_reference_counts_; uint64_t known_received_count_;
diff --git a/quic/core/qpack/qpack_blocking_manager_test.cc b/quic/core/qpack/qpack_blocking_manager_test.cc index a7cba73..6f78d87 100644 --- a/quic/core/qpack/qpack_blocking_manager_test.cc +++ b/quic/core/qpack/qpack_blocking_manager_test.cc
@@ -206,6 +206,88 @@ manager_.smallest_blocking_index()); } +TEST_F(QpackBlockingManagerTest, + ReferenceOnEncoderStreamUnblockedByInsertCountIncrement) { + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); + + // Entry 1 refers to entry 0. + manager_.OnReferenceSentOnEncoderStream(1, 0); + // Entry 2 also refers to entry 0. + manager_.OnReferenceSentOnEncoderStream(2, 0); + + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Acknowledging entry 1 still leaves one unacknowledged reference to entry 0. + manager_.OnInsertCountIncrement(2); + + EXPECT_EQ(2u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Entry 3 also refers to entry 2. + manager_.OnReferenceSentOnEncoderStream(3, 2); + + EXPECT_EQ(2u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Acknowledging entry 2 removes last reference to entry 0. + manager_.OnInsertCountIncrement(1); + + EXPECT_EQ(3u, manager_.known_received_count()); + EXPECT_EQ(2u, manager_.smallest_blocking_index()); + + // Acknowledging entry 4 (and implicitly 3) removes reference to entry 2. + manager_.OnInsertCountIncrement(2); + + EXPECT_EQ(5u, manager_.known_received_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); +} + +TEST_F(QpackBlockingManagerTest, + ReferenceOnEncoderStreamUnblockedByHeaderAcknowledgement) { + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); + + // Entry 1 refers to entry 0. + manager_.OnReferenceSentOnEncoderStream(1, 0); + // Entry 2 also refers to entry 0. + manager_.OnReferenceSentOnEncoderStream(2, 0); + + EXPECT_EQ(0u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Acknowledging a header block with entries up to 1 still leave one + // unacknowledged reference to entry 0. + manager_.OnHeaderBlockSent(/* stream_id = */ 0, {0, 1}); + manager_.OnHeaderAcknowledgement(/* stream_id = */ 0); + + EXPECT_EQ(2u, manager_.known_received_count()); + EXPECT_EQ(0u, manager_.smallest_blocking_index()); + + // Entry 3 also refers to entry 2. + manager_.OnReferenceSentOnEncoderStream(3, 2); + + // Acknowledging a header block with entries up to 2 removes last reference to + // entry 0. + manager_.OnHeaderBlockSent(/* stream_id = */ 0, {2, 0, 2}); + manager_.OnHeaderAcknowledgement(/* stream_id = */ 0); + + EXPECT_EQ(3u, manager_.known_received_count()); + EXPECT_EQ(2u, manager_.smallest_blocking_index()); + + // Acknowledging entry 4 (and implicitly 3) removes reference to entry 2. + manager_.OnHeaderBlockSent(/* stream_id = */ 0, {1, 4, 2, 0}); + manager_.OnHeaderAcknowledgement(/* stream_id = */ 0); + + EXPECT_EQ(5u, manager_.known_received_count()); + EXPECT_EQ(std::numeric_limits<uint64_t>::max(), + manager_.smallest_blocking_index()); +} + } // namespace } // namespace test } // namespace quic