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