blob: abe85f39b25f9181cce836aa4696ded8fdc4c10d [file] [log] [blame]
// Copyright (c) 2024 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quiche/quic/core/qpack/new_qpack_blocking_manager.h"
#include <cstdint>
#include <initializer_list>
#include <limits>
#include <memory>
#include <utility>
#include "absl/container/flat_hash_set.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
#include "quiche/common/platform/api/quiche_logging.h"
namespace quic {
NewQpackBlockingManager::IndexSet::IndexSet(
std::initializer_list<uint64_t> indices) {
for (const uint64_t index : indices) {
insert(index);
}
}
void NewQpackBlockingManager::IndexSet::insert(uint64_t index) {
if (index > max_index_) {
max_index_ = index;
}
if (index < min_index_) {
min_index_ = index;
}
}
uint64_t NewQpackBlockingManager::IndexSet::RequiredInsertCount() const {
if (empty()) {
QUIC_BUG(qpack_blocking_manager_required_insert_count_on_empty_set)
<< "RequiredInsertCount called on an empty IndexSet.";
return 0;
}
return max_index_ + 1;
}
uint64_t NewQpackBlockingManager::StreamRecord::MaxRequiredInsertCount() const {
uint64_t result = 0;
for (const IndexSet& header_block : header_blocks) {
uint64_t required_insert_count = header_block.RequiredInsertCount();
if (required_insert_count > result) {
result = required_insert_count;
}
}
return result;
}
bool NewQpackBlockingManager::OnHeaderAcknowledgement(QuicStreamId stream_id) {
auto it = stream_map_.find(stream_id);
if (it == stream_map_.end()) {
return false;
}
if (it->second->header_blocks.empty()) {
QUIC_BUG(qpack_blocking_manager_no_unacked_header_blocks_in_stream)
<< "OnHeaderAcknowledgement is called on a stream with no "
"unacked header blocks. stream_id:"
<< stream_id;
return false;
}
{
// Scoped to prevent accidental access to |acked_header_block| after
// it is erased right after the scope.
const IndexSet& acked_header_block = it->second->header_blocks.front();
if (known_received_count_ < acked_header_block.RequiredInsertCount()) {
IncreaseKnownReceivedCount(acked_header_block.RequiredInsertCount());
}
DecMinIndexReferenceCounts(acked_header_block.min_index());
}
it->second->header_blocks.erase(it->second->header_blocks.begin());
bool ok = true;
if (it->second->header_blocks.empty()) {
if (blocked_streams_.is_linked(it->second.get())) {
// header_blocks.empty() means all header blocks in the stream are acked,
// thus the stream should not be blocked.
QUIC_BUG(qpack_blocking_manager_stream_blocked_unexpectedly)
<< "Stream is blocked unexpectedly. stream_id:" << stream_id;
ok = false;
UpdateBlockedListForStream(*it->second);
}
stream_map_.erase(it);
}
return ok;
}
void NewQpackBlockingManager::IncreaseKnownReceivedCount(
uint64_t new_known_received_count) {
if (new_known_received_count <= known_received_count_) {
QUIC_BUG(qpack_blocking_manager_known_received_count_not_increased)
<< "new_known_received_count:" << new_known_received_count
<< ", known_received_count_:" << known_received_count_;
return;
}
known_received_count_ = new_known_received_count;
// Go through blocked streams and remove those that are no longer blocked.
for (auto it = blocked_streams_.begin(); it != blocked_streams_.end();) {
if (it->MaxRequiredInsertCount() > known_received_count_) {
// Stream is still blocked.
++it;
continue;
}
// Stream is no longer blocked.
it = blocked_streams_.erase(it);
num_blocked_streams_--;
}
}
void NewQpackBlockingManager::OnStreamCancellation(QuicStreamId stream_id) {
auto it = stream_map_.find(stream_id);
if (it == stream_map_.end()) {
return;
}
for (const IndexSet& header_block : it->second->header_blocks) {
DecMinIndexReferenceCounts(header_block.min_index());
}
// header_blocks.clear() cause StreamRecord.MaxRequiredInsertCount() to return
// zero, thus UpdateBlockedListForStream will remove it from blocked_streams_.
it->second->header_blocks.clear();
UpdateBlockedListForStream(*it->second);
stream_map_.erase(it);
}
bool NewQpackBlockingManager::OnInsertCountIncrement(uint64_t increment) {
if (increment >
std::numeric_limits<uint64_t>::max() - known_received_count_) {
return false;
}
IncreaseKnownReceivedCount(known_received_count_ + increment);
return true;
}
void NewQpackBlockingManager::OnHeaderBlockSent(
QuicStreamId stream_id, IndexSet indices, uint64_t required_insert_count) {
if (indices.empty()) {
QUIC_BUG(qpack_blocking_manager_empty_indices)
<< "OnHeaderBlockSent must not be called with empty indices. stream_id:"
<< stream_id;
return;
}
IncMinIndexReferenceCounts(indices.min_index());
QUICHE_DCHECK_EQ(required_insert_count, indices.RequiredInsertCount());
auto it = stream_map_.find(stream_id);
if (it == stream_map_.end()) {
it =
stream_map_.insert({stream_id, std::make_unique<StreamRecord>()}).first;
}
it->second->header_blocks.push_back(std::move(indices));
UpdateBlockedListForStream(*it->second);
}
void NewQpackBlockingManager::UpdateBlockedListForStream(
StreamRecord& stream_record) {
if (stream_record.MaxRequiredInsertCount() > known_received_count_) {
// Stream is blocked.
if (!blocked_streams_.is_linked(&stream_record)) {
blocked_streams_.push_back(&stream_record);
num_blocked_streams_++;
}
} else {
// Stream is not blocked.
if (blocked_streams_.is_linked(&stream_record)) {
blocked_streams_.erase(&stream_record);
num_blocked_streams_--;
}
}
}
bool NewQpackBlockingManager::stream_is_blocked(QuicStreamId stream_id) const {
auto it = stream_map_.find(stream_id);
return it != stream_map_.end() &&
blocked_streams_.is_linked(it->second.get());
}
bool NewQpackBlockingManager::blocking_allowed_on_stream(
QuicStreamId stream_id, uint64_t maximum_blocked_streams) const {
if (num_blocked_streams_ < maximum_blocked_streams) {
// Whether |stream_id| is currently blocked or not, blocking on it will not
// exceed |maximum_blocked_streams|.
return true;
}
// We've reached |maximum_blocked_streams| so no _new_ blocked streams are
// allowed. Return true iff |stream_id| is already blocked.
return stream_is_blocked(stream_id);
}
uint64_t NewQpackBlockingManager::smallest_blocking_index() const {
return min_index_reference_counts_.empty()
? std::numeric_limits<uint64_t>::max()
: min_index_reference_counts_.begin()->first;
}
// static
uint64_t NewQpackBlockingManager::RequiredInsertCount(const IndexSet& indices) {
return indices.RequiredInsertCount();
}
void NewQpackBlockingManager::IncMinIndexReferenceCounts(uint64_t min_index) {
min_index_reference_counts_[min_index]++;
}
void NewQpackBlockingManager::DecMinIndexReferenceCounts(uint64_t min_index) {
auto it = min_index_reference_counts_.find(min_index);
if (it == min_index_reference_counts_.end()) {
QUIC_BUG(qpack_blocking_manager_removing_non_existent_min_index)
<< "Removing min index:" << min_index
<< " which do not exist in min_index_reference_counts_.";
return;
}
if (it->second == 1) {
min_index_reference_counts_.erase(it);
} else {
it->second--;
}
}
} // namespace quic