diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc b/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc index 17dd0a4d2ebdf..fc4f261afb017 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc @@ -20,8 +20,9 @@ #include "src/core/ext/transport/chttp2/transport/hpack_parser_table.h" -#include -#include +#include +#include +#include #include "absl/strings/str_format.h" @@ -38,17 +39,52 @@ extern grpc_core::TraceFlag grpc_http_trace; namespace grpc_core { +void MementoRingBuffer::Put(Memento m) { + GPR_ASSERT(num_entries_ < max_entries_); + if (entries_.size() < max_entries_) { + ++num_entries_; + return entries_.push_back(std::move(m)); + } + size_t index = (first_entry_ + num_entries_) % max_entries_; + entries_[index] = std::move(m); + ++num_entries_; +} + +auto MementoRingBuffer::PopOne() -> Memento { + GPR_ASSERT(num_entries_ > 0); + size_t index = first_entry_ % max_entries_; + ++first_entry_; + --num_entries_; + return std::move(entries_[index]); +} + +auto MementoRingBuffer::Lookup(uint32_t index) const -> const Memento* { + if (index >= num_entries_) return nullptr; + uint32_t offset = (num_entries_ - 1u - index + first_entry_) % max_entries_; + return &entries_[offset]; +} + +void MementoRingBuffer::Rebuild(uint32_t max_entries) { + if (max_entries == max_entries_) return; + std::vector entries; + entries.reserve(num_entries_); + for (size_t i = 0; i < num_entries_; i++) { + entries.push_back( + std::move(entries_[(first_entry_ + i) % entries_.size()])); + } + first_entry_ = 0; + entries_.swap(entries); +} + HPackTable::HPackTable() : static_metadata_(GetStaticMementos()) {} HPackTable::~HPackTable() = default; /* Evict one element from the table */ void HPackTable::EvictOne() { - auto first_entry = std::move(entries_[first_entry_]); + auto first_entry = entries_.PopOne(); GPR_ASSERT(first_entry.transport_size() <= mem_used_); mem_used_ -= first_entry.transport_size(); - first_entry_ = ((first_entry_ + 1) % entries_.size()); - num_entries_--; } void HPackTable::Rebuild(uint32_t new_cap) { @@ -90,18 +126,9 @@ grpc_error_handle HPackTable::SetCurrentTableSize(uint32_t bytes) { EvictOne(); } current_table_bytes_ = bytes; - max_entries_ = hpack_constants::EntriesForBytes(bytes); - if (max_entries_ > entries_.size()) { - Rebuild(max_entries_); - } else if (max_entries_ < entries_.size() / 3) { - // TODO(ctiller): move to resource quota system, only shrink under memory - // pressure - uint32_t new_cap = - std::max(max_entries_, static_cast(kInlineEntries)); - if (new_cap != entries_.size()) { - Rebuild(new_cap); - } - } + uint32_t new_cap = std::max(hpack_constants::EntriesForBytes(bytes), + hpack_constants::kInitialTableEntries); + entries_.Rebuild(new_cap); return GRPC_ERROR_NONE; } @@ -122,7 +149,7 @@ grpc_error_handle HPackTable::Add(Memento md) { // attempt to add an entry larger than the entire table causes // the table to be emptied of all existing entries, and results in an // empty table. - while (num_entries_) { + while (entries_.num_entries()) { EvictOne(); } return GRPC_ERROR_NONE; @@ -136,10 +163,7 @@ grpc_error_handle HPackTable::Add(Memento md) { // copy the finalized entry in mem_used_ += md.transport_size(); - entries_[(first_entry_ + num_entries_) % entries_.size()] = std::move(md); - - // update accounting values - num_entries_++; + entries_.Put(std::move(md)); return GRPC_ERROR_NONE; } diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser_table.h b/src/core/ext/transport/chttp2/transport/hpack_parser_table.h index 5309ecad28798..619e4402a9183 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_parser_table.h +++ b/src/core/ext/transport/chttp2/transport/hpack_parser_table.h @@ -30,6 +30,40 @@ namespace grpc_core { +using Memento = ParsedMetadata; + +class MementoRingBuffer { + public: + // Rebuild this buffer with a new max_entries_ size. + void Rebuild(uint32_t max_entries); + + // Put a new memento. + // REQUIRES: num_entries < max_entries + void Put(Memento m); + + // Pop the oldest memento. + // REQUIRES: num_entries > 0 + Memento PopOne(); + + // Lookup the entry at index, or return nullptr if none exists. + const Memento* Lookup(uint32_t index) const; + + uint32_t max_entries() const { return max_entries_; } + uint32_t num_entries() const { return num_entries_; } + + private: + // The index of the first entry in the buffer. May be greater than + // max_entries_, in which case a wraparound has occurred. + uint32_t first_entry_ = 0; + // How many entries are in the table. + uint32_t num_entries_ = 0; + // Maximum number of entries we could possibly fit in the table, given defined + // overheads. + uint32_t max_entries_ = hpack_constants::kInitialTableEntries; + + std::vector entries_; +}; + // HPACK header table class HPackTable { public: @@ -42,7 +76,7 @@ class HPackTable { void SetMaxBytes(uint32_t max_bytes); grpc_error_handle SetCurrentTableSize(uint32_t bytes); - using Memento = ParsedMetadata; + using Memento = Memento; // Lookup, but don't ref. const Memento* Lookup(uint32_t index) const { @@ -63,7 +97,7 @@ class HPackTable { grpc_error_handle Add(Memento md) GRPC_MUST_USE_RESULT; // Current entry count in the table. - uint32_t num_entries() const { return num_entries_; } + uint32_t num_entries() const { return entries_.num_entries(); } private: struct StaticMementos { @@ -72,28 +106,14 @@ class HPackTable { }; static const StaticMementos& GetStaticMementos() GPR_ATTRIBUTE_NOINLINE; - enum { kInlineEntries = hpack_constants::kInitialTableEntries }; - using EntriesVec = absl::InlinedVector; - const Memento* LookupDynamic(uint32_t index) const { // Not static - find the value in the list of valid entries const uint32_t tbl_index = index - (hpack_constants::kLastStaticEntry + 1); - if (tbl_index < num_entries_) { - uint32_t offset = - (num_entries_ - 1u - tbl_index + first_entry_) % entries_.size(); - return &entries_[offset]; - } - // Invalid entry: return error - return nullptr; + return entries_.Lookup(tbl_index); } void EvictOne(); - void Rebuild(uint32_t new_cap); - // The first used entry in ents. - uint32_t first_entry_ = 0; - // How many entries are in the table. - uint32_t num_entries_ = 0; // The amount of memory used by the table, according to the hpack algorithm uint32_t mem_used_ = 0; // The max memory allowed to be used by the table, according to the hpack @@ -101,11 +121,8 @@ class HPackTable { uint32_t max_bytes_ = hpack_constants::kInitialTableSize; // The currently agreed size of the table, according to the hpack algorithm. uint32_t current_table_bytes_ = hpack_constants::kInitialTableSize; - // Maximum number of entries we could possibly fit in the table, given defined - // overheads. - uint32_t max_entries_ = hpack_constants::kInitialTableEntries; // HPack table entries - EntriesVec entries_{hpack_constants::kInitialTableEntries}; + MementoRingBuffer entries_; // Mementos for static data const StaticMementos& static_metadata_; };