1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef QUICHE_QUIC_CORE_QUIC_WRITE_BLOCKED_LIST_H_ 6 #define QUICHE_QUIC_CORE_QUIC_WRITE_BLOCKED_LIST_H_ 7 8 #include <cstddef> 9 #include <cstdint> 10 #include <utility> 11 12 #include "absl/container/inlined_vector.h" 13 #include "quiche/http2/core/priority_write_scheduler.h" 14 #include "quiche/quic/core/quic_packets.h" 15 #include "quiche/quic/core/quic_stream_priority.h" 16 #include "quiche/quic/platform/api/quic_bug_tracker.h" 17 #include "quiche/quic/platform/api/quic_export.h" 18 #include "quiche/quic/platform/api/quic_flags.h" 19 #include "quiche/spdy/core/spdy_protocol.h" 20 21 namespace quic { 22 23 // Keeps tracks of the order of QUIC streams that have data to write. 24 // Static streams come first, in the order they were registered with 25 // QuicWriteBlockedList. They are followed by non-static streams, ordered by 26 // priority. 27 class QUICHE_EXPORT QuicWriteBlockedListInterface { 28 public: 29 virtual ~QuicWriteBlockedListInterface() = default; 30 31 virtual bool HasWriteBlockedDataStreams() const = 0; 32 virtual size_t NumBlockedSpecialStreams() const = 0; 33 virtual size_t NumBlockedStreams() const = 0; HasWriteBlockedSpecialStream()34 bool HasWriteBlockedSpecialStream() const { 35 return NumBlockedSpecialStreams() > 0; 36 } 37 38 // Returns true if there is another stream with higher priority in the queue. 39 virtual bool ShouldYield(QuicStreamId id) const = 0; 40 41 // Returns the priority of the specified stream. 42 virtual QuicStreamPriority GetPriorityOfStream(QuicStreamId id) const = 0; 43 44 // Pops the highest priority stream, special casing static streams. Latches 45 // the most recently popped data stream for batch writing purposes. 46 virtual QuicStreamId PopFront() = 0; 47 48 // Register a stream with given priority. 49 // `priority` is ignored for static streams. 50 virtual void RegisterStream(QuicStreamId stream_id, bool is_static_stream, 51 const QuicStreamPriority& priority) = 0; 52 53 // Unregister a stream. `stream_id` must be registered, either as a static 54 // stream or as a non-static stream. 55 virtual void UnregisterStream(QuicStreamId stream_id) = 0; 56 57 // Updates the stored priority of a stream. Must not be called for static 58 // streams. 59 virtual void UpdateStreamPriority(QuicStreamId stream_id, 60 const QuicStreamPriority& new_priority) = 0; 61 62 // TODO(b/147306124): Remove when deprecating 63 // reloadable_flag_quic_disable_batch_write. 64 virtual void UpdateBytesForStream(QuicStreamId stream_id, size_t bytes) = 0; 65 66 // Pushes a stream to the back of the list for its priority level *unless* it 67 // is latched for doing batched writes in which case it goes to the front of 68 // the list for its priority level. 69 // Static streams are special cased to always resume first. 70 // Stream must already be registered. 71 virtual void AddStream(QuicStreamId stream_id) = 0; 72 73 // Returns true if stream with |stream_id| is write blocked. 74 virtual bool IsStreamBlocked(QuicStreamId stream_id) const = 0; 75 }; 76 77 // Default implementation of QuicWriteBlockedListInterface. 78 class QUIC_EXPORT_PRIVATE QuicWriteBlockedList 79 : public QuicWriteBlockedListInterface { 80 public: 81 explicit QuicWriteBlockedList(); 82 QuicWriteBlockedList(const QuicWriteBlockedList&) = delete; 83 QuicWriteBlockedList& operator=(const QuicWriteBlockedList&) = delete; 84 HasWriteBlockedDataStreams()85 bool HasWriteBlockedDataStreams() const override { 86 return priority_write_scheduler_.HasReadyStreams(); 87 } 88 NumBlockedSpecialStreams()89 size_t NumBlockedSpecialStreams() const override { 90 return static_stream_collection_.num_blocked(); 91 } 92 NumBlockedStreams()93 size_t NumBlockedStreams() const override { 94 return NumBlockedSpecialStreams() + 95 priority_write_scheduler_.NumReadyStreams(); 96 } 97 98 bool ShouldYield(QuicStreamId id) const override; 99 GetPriorityOfStream(QuicStreamId id)100 QuicStreamPriority GetPriorityOfStream(QuicStreamId id) const override { 101 return QuicStreamPriority(priority_write_scheduler_.GetStreamPriority(id)); 102 } 103 104 // Pops the highest priority stream, special casing static streams. Latches 105 // the most recently popped data stream for batch writing purposes. 106 QuicStreamId PopFront() override; 107 108 // Register a stream with given priority. 109 // `priority` is ignored for static streams. 110 void RegisterStream(QuicStreamId stream_id, bool is_static_stream, 111 const QuicStreamPriority& priority) override; 112 113 // Unregister a stream. `stream_id` must be registered, either as a static 114 // stream or as a non-static stream. 115 void UnregisterStream(QuicStreamId stream_id) override; 116 117 // Updates the stored priority of a stream. Must not be called for static 118 // streams. 119 void UpdateStreamPriority(QuicStreamId stream_id, 120 const QuicStreamPriority& new_priority) override; 121 122 // TODO(b/147306124): Remove when deprecating 123 // reloadable_flag_quic_disable_batch_write. 124 void UpdateBytesForStream(QuicStreamId stream_id, size_t bytes) override; 125 126 // Pushes a stream to the back of the list for its priority level *unless* it 127 // is latched for doing batched writes in which case it goes to the front of 128 // the list for its priority level. 129 // Static streams are special cased to always resume first. 130 // Stream must already be registered. 131 void AddStream(QuicStreamId stream_id) override; 132 133 // Returns true if stream with |stream_id| is write blocked. 134 bool IsStreamBlocked(QuicStreamId stream_id) const override; 135 136 private: 137 struct QUICHE_EXPORT HttpStreamPriorityToInt { operatorHttpStreamPriorityToInt138 int operator()(const HttpStreamPriority& priority) { 139 return priority.urgency; 140 } 141 }; 142 143 struct QUICHE_EXPORT IntToHttpStreamPriority { operatorIntToHttpStreamPriority144 HttpStreamPriority operator()(int urgency) { 145 return HttpStreamPriority{urgency}; 146 } 147 }; 148 http2::PriorityWriteScheduler<QuicStreamId, HttpStreamPriority, 149 HttpStreamPriorityToInt, 150 IntToHttpStreamPriority> 151 priority_write_scheduler_; 152 153 // If performing batch writes, this will be the stream ID of the stream doing 154 // batch writes for this priority level. We will allow this stream to write 155 // until it has written kBatchWriteSize bytes, it has no more data to write, 156 // or a higher priority stream preempts. 157 QuicStreamId batch_write_stream_id_[spdy::kV3LowestPriority + 1]; 158 // Set to kBatchWriteSize when we set a new batch_write_stream_id_ for a given 159 // priority. This is decremented with each write the stream does until it is 160 // done with its batch write. 161 // TODO(b/147306124): Remove when deprecating 162 // reloadable_flag_quic_disable_batch_write. 163 size_t bytes_left_for_batch_write_[spdy::kV3LowestPriority + 1]; 164 // Tracks the last priority popped for UpdateBytesForStream() and AddStream(). 165 spdy::SpdyPriority last_priority_popped_; 166 167 // A StaticStreamCollection is a vector of <QuicStreamId, bool> pairs plus a 168 // eagerly-computed number of blocked static streams. 169 class QUIC_EXPORT_PRIVATE StaticStreamCollection { 170 public: 171 struct QUIC_EXPORT_PRIVATE StreamIdBlockedPair { 172 QuicStreamId id; 173 bool is_blocked; 174 }; 175 176 // Optimized for the typical case of 2 static streams per session. 177 using StreamsVector = absl::InlinedVector<StreamIdBlockedPair, 2>; 178 begin()179 StreamsVector::const_iterator begin() const { return streams_.cbegin(); } 180 end()181 StreamsVector::const_iterator end() const { return streams_.cend(); } 182 num_blocked()183 size_t num_blocked() const { return num_blocked_; } 184 185 // Add |id| to the collection in unblocked state. 186 void Register(QuicStreamId id); 187 188 // True if |id| is in the collection, regardless of its state. 189 bool IsRegistered(QuicStreamId id) const; 190 191 // Remove |id| from the collection. If it is in the blocked state, reduce 192 // |num_blocked_| by 1. Returns true if |id| was in the collection. 193 bool Unregister(QuicStreamId id); 194 195 // Set |id| to be blocked. If |id| is not already blocked, increase 196 // |num_blocked_| by 1. 197 // Return true if |id| is in the collection. 198 bool SetBlocked(QuicStreamId id); 199 200 // Unblock the first blocked stream in the collection. 201 // If no stream is blocked, return false. Otherwise return true, set *id to 202 // the unblocked stream id and reduce |num_blocked_| by 1. 203 bool UnblockFirstBlocked(QuicStreamId* id); 204 205 private: 206 size_t num_blocked_ = 0; 207 StreamsVector streams_; 208 }; 209 210 StaticStreamCollection static_stream_collection_; 211 212 // Latched value of reloadable_flag_quic_priority_respect_incremental. 213 const bool respect_incremental_; 214 // Latched value of reloadable_flag_quic_disable_batch_write. 215 const bool disable_batch_write_; 216 }; 217 218 } // namespace quic 219 220 #endif // QUICHE_QUIC_CORE_QUIC_WRITE_BLOCKED_LIST_H_ 221