| // Copyright 2023 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_WEB_TRANSPORT_WEB_TRANSPORT_PRIORITY_SCHEDULER_H_ |
| #define QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_PRIORITY_SCHEDULER_H_ |
| |
| #include <cstddef> |
| #include <optional> |
| #include <utility> |
| |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/container/node_hash_map.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "quiche/common/btree_scheduler.h" |
| #include "quiche/common/platform/api/quiche_export.h" |
| #include "quiche/web_transport/web_transport.h" |
| |
| namespace webtransport { |
| |
| // Schedules the streams within a WebTransport session as defined by the W3C |
| // WebTransport API. Unlike the W3C API, there is no need to track groups |
| // manually: the group is created when a first stream with the associated group |
| // ID is registered, and it is deleted when the last stream associated with the |
| // group is unregistered. |
| class QUICHE_EXPORT PriorityScheduler { |
| public: |
| // Returns true if there are any streams registered. |
| bool HasRegistered() const { return active_groups_.HasRegistered(); } |
| // Returns true if there are any streams scheduled. |
| bool HasScheduled() const { return active_groups_.HasScheduled(); } |
| |
| // Returns the number of currently scheduled streams. |
| size_t NumScheduled() const { |
| size_t total = 0; |
| for (const auto& [group_id, group_scheduler] : per_group_schedulers_) { |
| total += group_scheduler.NumScheduled(); |
| } |
| return total; |
| } |
| |
| // Registers the specified stream with the supplied priority. The stream must |
| // not be already registered. |
| absl::Status Register(StreamId stream_id, const StreamPriority& priority); |
| // Unregisters a previously registered stream. |
| absl::Status Unregister(StreamId stream_id); |
| // Alters the priority of an already registered stream. |
| absl::Status UpdateSendOrder(StreamId stream_id, SendOrder new_send_order); |
| absl::Status UpdateSendGroup(StreamId stream_id, SendGroupId new_send_group); |
| |
| // Returns true if there is a stream that would go before `id` in the |
| // schedule. |
| absl::StatusOr<bool> ShouldYield(StreamId id) const; |
| |
| // Returns the priority for `id`, or nullopt if stream is not registered. |
| std::optional<StreamPriority> GetPriorityFor(StreamId stream_id) const; |
| |
| // Pops the highest priority stream. Will fail if the schedule is empty. |
| absl::StatusOr<StreamId> PopFront(); |
| |
| // Adds `stream` to the schedule if it's not already there. |
| absl::Status Schedule(StreamId stream_id); |
| // Returns true if `stream` is in the schedule. |
| bool IsScheduled(StreamId stream_id) const; |
| |
| private: |
| // All groups currently have the equal priority; this type represents the said |
| // single priority. |
| class SinglePriority { |
| public: |
| bool operator==(const SinglePriority&) const { return true; } |
| bool operator!=(const SinglePriority&) const { return false; } |
| |
| bool operator<(const SinglePriority&) const { return false; } |
| bool operator>(const SinglePriority&) const { return false; } |
| bool operator<=(const SinglePriority&) const { return true; } |
| bool operator>=(const SinglePriority&) const { return true; } |
| }; |
| |
| using PerGroupScheduler = quiche::BTreeScheduler<StreamId, SendOrder>; |
| using GroupSchedulerPair = std::pair<const SendGroupId, PerGroupScheduler>; |
| |
| // Round-robin schedule for the groups. |
| quiche::BTreeScheduler<SendGroupId, SinglePriority> active_groups_; |
| // Map group ID to the scheduler for the group in question. |
| absl::node_hash_map<SendGroupId, PerGroupScheduler> per_group_schedulers_; |
| // Map stream ID to a pointer to the entry in `per_group_schedulers_`. |
| absl::flat_hash_map<StreamId, GroupSchedulerPair*> stream_to_group_map_; |
| |
| PerGroupScheduler* SchedulerForStream(StreamId stream_id) { |
| auto it = stream_to_group_map_.find(stream_id); |
| if (it == stream_to_group_map_.end()) { |
| return nullptr; |
| } |
| return &it->second->second; |
| } |
| const PerGroupScheduler* SchedulerForStream(StreamId stream_id) const { |
| auto it = stream_to_group_map_.find(stream_id); |
| if (it == stream_to_group_map_.end()) { |
| return nullptr; |
| } |
| return &it->second->second; |
| } |
| }; |
| |
| } // namespace webtransport |
| |
| #endif // QUICHE_WEB_TRANSPORT_WEB_TRANSPORT_PRIORITY_SCHEDULER_H_ |