| // Copyright (c) 2019 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. | 
 |  | 
 | #ifndef QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_ | 
 | #define QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_ | 
 |  | 
 | #include <map> | 
 | #include <set> | 
 | #include <string> | 
 |  | 
 | #include "absl/strings/str_cat.h" | 
 | #include "spdy/core/write_scheduler.h" | 
 | #include "spdy/platform/api/spdy_string_utils.h" | 
 |  | 
 | namespace spdy { | 
 |  | 
 | // A write scheduler where the stream with the smallest stream ID will have the | 
 | // highest priority. | 
 | template <typename StreamIdType> | 
 | class FifoWriteScheduler : public WriteScheduler<StreamIdType> { | 
 |  public: | 
 |   using typename WriteScheduler<StreamIdType>::StreamPrecedenceType; | 
 |  | 
 |   FifoWriteScheduler() = default; | 
 |  | 
 |   // WriteScheduler methods | 
 |   void RegisterStream(StreamIdType stream_id, | 
 |                       const StreamPrecedenceType& precedence) override; | 
 |   void UnregisterStream(StreamIdType stream_id) override; | 
 |   bool StreamRegistered(StreamIdType stream_id) const override; | 
 |   // Stream precedence is available but note that it is not used for scheduling | 
 |   // in this scheduler. | 
 |   StreamPrecedenceType GetStreamPrecedence( | 
 |       StreamIdType stream_id) const override; | 
 |   void UpdateStreamPrecedence(StreamIdType stream_id, | 
 |                               const StreamPrecedenceType& precedence) override; | 
 |   std::vector<StreamIdType> GetStreamChildren( | 
 |       StreamIdType stream_id) const override; | 
 |   void RecordStreamEventTime(StreamIdType stream_id, | 
 |                              int64_t now_in_usec) override; | 
 |   int64_t GetLatestEventWithPrecedence(StreamIdType stream_id) const override; | 
 |   bool ShouldYield(StreamIdType stream_id) const override; | 
 |   void MarkStreamReady(StreamIdType stream_id, bool add_to_front) override; | 
 |   void MarkStreamNotReady(StreamIdType stream_id) override; | 
 |   bool HasReadyStreams() const override; | 
 |   StreamIdType PopNextReadyStream() override; | 
 |   std::tuple<StreamIdType, StreamPrecedenceType> | 
 |   PopNextReadyStreamAndPrecedence() override; | 
 |   size_t NumReadyStreams() const override; | 
 |   bool IsStreamReady(StreamIdType stream_id) const override; | 
 |   size_t NumRegisteredStreams() const override; | 
 |   std::string DebugString() const override; | 
 |  | 
 |  private: | 
 |   struct StreamInfo { | 
 |     SpdyPriority priority; | 
 |     int64_t event_time;  // read/write event time (us since Unix epoch). | 
 |   }; | 
 |  | 
 |   std::set<StreamIdType> ready_streams_; | 
 |   std::map<StreamIdType, StreamInfo> registered_streams_; | 
 | }; | 
 |  | 
 | template <typename StreamIdType> | 
 | void FifoWriteScheduler<StreamIdType>::RegisterStream( | 
 |     StreamIdType stream_id, | 
 |     const StreamPrecedenceType& precedence) { | 
 |   if (StreamRegistered(stream_id)) { | 
 |     SPDY_BUG << "Stream " << stream_id << " already registered"; | 
 |     return; | 
 |   } | 
 |   registered_streams_.emplace_hint( | 
 |       registered_streams_.end(), stream_id, | 
 |       StreamInfo{/*priority=*/precedence.spdy3_priority(), /*event_time=*/0}); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | void FifoWriteScheduler<StreamIdType>::UnregisterStream( | 
 |     StreamIdType stream_id) { | 
 |   if (!StreamRegistered(stream_id)) { | 
 |     SPDY_BUG << "Stream " << stream_id << " is not registered"; | 
 |     return; | 
 |   } | 
 |   registered_streams_.erase(stream_id); | 
 |   ready_streams_.erase(stream_id); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | bool FifoWriteScheduler<StreamIdType>::StreamRegistered( | 
 |     StreamIdType stream_id) const { | 
 |   return registered_streams_.find(stream_id) != registered_streams_.end(); | 
 | } | 
 |  | 
 | // Stream precedence is not supported by this scheduler. | 
 | template <typename StreamIdType> | 
 | typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType | 
 | FifoWriteScheduler<StreamIdType>::GetStreamPrecedence( | 
 |     StreamIdType stream_id) const { | 
 |   auto it = registered_streams_.find(stream_id); | 
 |   if (it == registered_streams_.end()) { | 
 |     SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; | 
 |     return StreamPrecedenceType(kV3LowestPriority); | 
 |   } | 
 |   return StreamPrecedenceType(it->second.priority); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | void FifoWriteScheduler<StreamIdType>::UpdateStreamPrecedence( | 
 |     StreamIdType stream_id, | 
 |     const StreamPrecedenceType& precedence) { | 
 |   auto it = registered_streams_.find(stream_id); | 
 |   if (it == registered_streams_.end()) { | 
 |     SPDY_DVLOG(1) << "Stream " << stream_id << " not registered"; | 
 |     return; | 
 |   } | 
 |   it->second.priority = precedence.spdy3_priority(); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | std::vector<StreamIdType> FifoWriteScheduler<StreamIdType>::GetStreamChildren( | 
 |     StreamIdType /*stream_id*/) const { | 
 |   return std::vector<StreamIdType>(); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | void FifoWriteScheduler<StreamIdType>::RecordStreamEventTime( | 
 |     StreamIdType stream_id, | 
 |     int64_t now_in_usec) { | 
 |   auto it = registered_streams_.find(stream_id); | 
 |   if (it != registered_streams_.end()) { | 
 |     it->second.event_time = now_in_usec; | 
 |   } else { | 
 |     SPDY_BUG << "Stream " << stream_id << " is not registered"; | 
 |   } | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | int64_t FifoWriteScheduler<StreamIdType>::GetLatestEventWithPrecedence( | 
 |     StreamIdType stream_id) const { | 
 |   if (!StreamRegistered(stream_id)) { | 
 |     SPDY_BUG << "Stream " << stream_id << " is not registered"; | 
 |     return 0; | 
 |   } | 
 |   int64_t latest_event_time_us = 0; | 
 |   for (auto it = registered_streams_.begin(); it != registered_streams_.end(); | 
 |        ++it) { | 
 |     if (stream_id <= it->first) { | 
 |       break; | 
 |     } | 
 |     latest_event_time_us = | 
 |         std::max(latest_event_time_us, it->second.event_time); | 
 |   } | 
 |   return latest_event_time_us; | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | bool FifoWriteScheduler<StreamIdType>::ShouldYield( | 
 |     StreamIdType stream_id) const { | 
 |   return !ready_streams_.empty() && stream_id > *ready_streams_.begin(); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | void FifoWriteScheduler<StreamIdType>::MarkStreamReady(StreamIdType stream_id, | 
 |                                                        bool /*add_to_front*/) { | 
 |   if (!StreamRegistered(stream_id)) { | 
 |     SPDY_BUG << "Stream " << stream_id << " is not registered"; | 
 |     return; | 
 |   } | 
 |   if (ready_streams_.find(stream_id) != ready_streams_.end()) { | 
 |     SPDY_DVLOG(1) << "Stream already exists in the list"; | 
 |     return; | 
 |   } | 
 |   ready_streams_.insert(stream_id); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | void FifoWriteScheduler<StreamIdType>::MarkStreamNotReady( | 
 |     StreamIdType stream_id) { | 
 |   auto it = ready_streams_.find(stream_id); | 
 |   if (it == ready_streams_.end()) { | 
 |     SPDY_DVLOG(1) << "Try to remove a stream that is not on list"; | 
 |     return; | 
 |   } | 
 |   ready_streams_.erase(it); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | bool FifoWriteScheduler<StreamIdType>::HasReadyStreams() const { | 
 |   return !ready_streams_.empty(); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | StreamIdType FifoWriteScheduler<StreamIdType>::PopNextReadyStream() { | 
 |   if (ready_streams_.empty()) { | 
 |     SPDY_BUG << "No ready streams available"; | 
 |     return 0; | 
 |   } | 
 |   auto it = ready_streams_.begin(); | 
 |   StreamIdType id = *it; | 
 |   ready_streams_.erase(it); | 
 |   return id; | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | std::tuple<StreamIdType, | 
 |            typename FifoWriteScheduler<StreamIdType>::StreamPrecedenceType> | 
 | FifoWriteScheduler<StreamIdType>::PopNextReadyStreamAndPrecedence() { | 
 |   return std::make_tuple(PopNextReadyStream(), | 
 |                          StreamPrecedenceType(kV3LowestPriority)); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | size_t FifoWriteScheduler<StreamIdType>::NumReadyStreams() const { | 
 |   return ready_streams_.size(); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | bool FifoWriteScheduler<StreamIdType>::IsStreamReady( | 
 |     StreamIdType stream_id) const { | 
 |   if (!StreamRegistered(stream_id)) { | 
 |     SPDY_BUG << "Stream " << stream_id << " is not registered"; | 
 |     return false; | 
 |   } | 
 |   return ready_streams_.find(stream_id) != ready_streams_.end(); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | size_t FifoWriteScheduler<StreamIdType>::NumRegisteredStreams() const { | 
 |   return registered_streams_.size(); | 
 | } | 
 |  | 
 | template <typename StreamIdType> | 
 | std::string FifoWriteScheduler<StreamIdType>::DebugString() const { | 
 |   return absl::StrCat( | 
 |       "FifoWriteScheduler {num_streams=", registered_streams_.size(), | 
 |       " num_ready_streams=", NumReadyStreams(), "}"); | 
 | } | 
 |  | 
 | }  // namespace spdy | 
 |  | 
 | #endif  // QUICHE_SPDY_CORE_FIFO_WRITE_SCHEDULER_H_ |