blob: 7e71ed050c202faf84b55ae7775fc4d45a6ea356 [file] [log] [blame]
// 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_