|  | // Copyright 2024 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_QUIC_CORE_QUIC_CONNECTION_ALARMS_H_ | 
|  | #define QUICHE_QUIC_CORE_QUIC_CONNECTION_ALARMS_H_ | 
|  |  | 
|  | #include <array> | 
|  | #include <cstddef> | 
|  | #include <cstdint> | 
|  | #include <string> | 
|  |  | 
|  | #include "absl/base/nullability.h" | 
|  | #include "quiche/quic/core/quic_alarm.h" | 
|  | #include "quiche/quic/core/quic_alarm_factory.h" | 
|  | #include "quiche/quic/core/quic_arena_scoped_ptr.h" | 
|  | #include "quiche/quic/core/quic_clock.h" | 
|  | #include "quiche/quic/core/quic_connection_context.h" | 
|  | #include "quiche/quic/core/quic_one_block_arena.h" | 
|  | #include "quiche/quic/core/quic_time.h" | 
|  | #include "quiche/common/platform/api/quiche_export.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | class QUICHE_EXPORT QuicConnectionAlarmsDelegate { | 
|  | public: | 
|  | virtual ~QuicConnectionAlarmsDelegate() = default; | 
|  |  | 
|  | virtual void OnSendAlarm() = 0; | 
|  | virtual void OnAckAlarm() = 0; | 
|  | virtual void OnRetransmissionAlarm() = 0; | 
|  | virtual void OnMtuDiscoveryAlarm() = 0; | 
|  | virtual void OnProcessUndecryptablePacketsAlarm() = 0; | 
|  | virtual void OnDiscardPreviousOneRttKeysAlarm() = 0; | 
|  | virtual void OnDiscardZeroRttDecryptionKeysAlarm() = 0; | 
|  | virtual void MaybeProbeMultiPortPath() = 0; | 
|  | virtual void OnIdleDetectorAlarm() = 0; | 
|  | virtual void OnNetworkBlackholeDetectorAlarm() = 0; | 
|  | virtual void OnPingAlarm() = 0; | 
|  |  | 
|  | virtual QuicConnectionContext* context() = 0; | 
|  | virtual const QuicClock* clock() const = 0; | 
|  | }; | 
|  |  | 
|  | namespace test { | 
|  | class QuicAlarmMultiplexerPeer; | 
|  | class QuicConnectionAlarmsPeer; | 
|  | }  // namespace test | 
|  |  | 
|  | enum class QuicAlarmSlot : uint8_t { | 
|  | // An alarm that is scheduled when the SentPacketManager requires a delay | 
|  | // before sending packets and fires when the packet may be sent. | 
|  | kSend, | 
|  | // An alarm that fires when an ACK should be sent to the peer. | 
|  | kAck, | 
|  | // An alarm that fires when a packet needs to be retransmitted. | 
|  | kRetransmission, | 
|  | // An alarm that fires when an MTU probe should be sent. | 
|  | kMtuDiscovery, | 
|  | // An alarm that fires to process undecryptable packets when new decryption | 
|  | // keys are available. | 
|  | kProcessUndecryptablePackets, | 
|  | // An alarm that fires to discard keys for the previous key phase some time | 
|  | // after a key update has completed. | 
|  | kDiscardPreviousOneRttKeys, | 
|  | // An alarm that fires to discard 0-RTT decryption keys some time after the | 
|  | // first 1-RTT packet has been decrypted. Only used on server connections with | 
|  | // TLS handshaker. | 
|  | kDiscardZeroRttDecryptionKeys, | 
|  | // An alarm that fires to keep probing the multi-port path. | 
|  | kMultiPortProbing, | 
|  | // An alarm for QuicIdleNetworkDetector. | 
|  | kIdleNetworkDetector, | 
|  | // An alarm for QuicNetworkBlackholeDetection. | 
|  | kNetworkBlackholeDetector, | 
|  | // An alarm for QuicPingManager. | 
|  | kPing, | 
|  |  | 
|  | // Must be the last element. | 
|  | kSlotCount | 
|  | }; | 
|  | std::string QuicAlarmSlotName(QuicAlarmSlot slot); | 
|  |  | 
|  | // QuicAlarmMultiplexer manages the alarms used by the QuicConnection. Its main | 
|  | // purpose is to minimize the cost of scheduling and rescheduling the multiple | 
|  | // alarms that QuicConnection has by reducing all of those alarms to just two. | 
|  | class QUICHE_EXPORT QuicAlarmMultiplexer { | 
|  | public: | 
|  | static constexpr size_t kNumberOfSlots = | 
|  | static_cast<size_t>(QuicAlarmSlot::kSlotCount); | 
|  |  | 
|  | QuicAlarmMultiplexer(QuicConnectionAlarmsDelegate* absl_nonnull connection, | 
|  | QuicConnectionArena& arena, | 
|  | QuicAlarmFactory& alarm_factory); | 
|  |  | 
|  | // QuicAlarmMultiplexer is not movable, as it has platform alarms that retain | 
|  | // a long-term pointer to it. | 
|  | QuicAlarmMultiplexer(const QuicAlarmMultiplexer&) = delete; | 
|  | QuicAlarmMultiplexer(QuicAlarmMultiplexer&&) = delete; | 
|  | QuicAlarmMultiplexer& operator=(const QuicAlarmMultiplexer&) = delete; | 
|  | QuicAlarmMultiplexer& operator=(QuicAlarmMultiplexer&&) = delete; | 
|  |  | 
|  | // Implementation of QuicAlarm methods. | 
|  | void Set(QuicAlarmSlot slot, QuicTime new_deadline); | 
|  | void Update(QuicAlarmSlot slot, QuicTime new_deadline, | 
|  | QuicTimeDelta granularity); | 
|  | void Cancel(QuicAlarmSlot slot) { | 
|  | SetDeadlineFor(slot, QuicTime::Zero()); | 
|  | MaybeRescheduleUnderlyingAlarms(); | 
|  | } | 
|  | bool IsSet(QuicAlarmSlot slot) const { | 
|  | return GetDeadline(slot).IsInitialized(); | 
|  | } | 
|  | bool IsPermanentlyCancelled() const { return permanently_cancelled_; } | 
|  | QuicTime GetDeadline(QuicAlarmSlot slot) const { | 
|  | return deadlines_[static_cast<size_t>(slot)]; | 
|  | } | 
|  |  | 
|  | void CancelAllAlarms(); | 
|  |  | 
|  | // Executes callbacks for all of the alarms that are currently due. | 
|  | void FireAlarms(); | 
|  |  | 
|  | // Methods used by ScopedPacketFlusher to defer updates to the underlying | 
|  | // platform alarm. | 
|  | void DeferUnderlyingAlarmScheduling(); | 
|  | void ResumeUnderlyingAlarmScheduling(); | 
|  |  | 
|  | QuicConnectionAlarmsDelegate* delegate() { return connection_; } | 
|  |  | 
|  | // Outputs a formatted list of active alarms. | 
|  | std::string DebugString(); | 
|  |  | 
|  | private: | 
|  | friend class ::quic::test::QuicConnectionAlarmsPeer; | 
|  | friend class ::quic::test::QuicAlarmMultiplexerPeer; | 
|  |  | 
|  | void SetDeadlineFor(QuicAlarmSlot slot, QuicTime deadline) { | 
|  | deadlines_[static_cast<size_t>(slot)] = deadline; | 
|  | } | 
|  |  | 
|  | // Fires an individual alarm if it is set. | 
|  | void Fire(QuicAlarmSlot slot); | 
|  |  | 
|  | void MaybeRescheduleUnderlyingAlarms() { | 
|  | if (defer_updates_of_underlying_alarms_ || permanently_cancelled_) { | 
|  | return; | 
|  | } | 
|  | RescheduleUnderlyingAlarms(); | 
|  | } | 
|  | // Updates the underlying platform alarm. | 
|  | void RescheduleUnderlyingAlarms(); | 
|  |  | 
|  | // Deadlines for all of the alarms that can be placed into the multiplexer, | 
|  | // indexed by the values of QuicAlarmSlot enum. | 
|  | std::array<QuicTime, kNumberOfSlots> deadlines_; | 
|  |  | 
|  | // Actual alarms provided by the underlying platform. Note that there are two | 
|  | // of them: the first is used for alarms that are scheduled for now or | 
|  | // earlier, and the latter is used for alarms that are scheduled in the | 
|  | // future.  The reason those are split is that QUIC has a lot of alarms that | 
|  | // are only fired immediately, and splitting those allows to avoid having | 
|  | // extra reschedules. | 
|  | QuicArenaScopedPtr<QuicAlarm> now_alarm_; | 
|  | QuicArenaScopedPtr<QuicAlarm> later_alarm_; | 
|  |  | 
|  | // Underlying connection and individual connection components. Not owned. | 
|  | QuicConnectionAlarmsDelegate* connection_; | 
|  |  | 
|  | // Latched value of --quic_multiplexer_alarm_granularity_us. | 
|  | QuicTimeDelta underlying_alarm_granularity_; | 
|  |  | 
|  | // If true, all of the alarms have been permanently cancelled. | 
|  | bool permanently_cancelled_ = false; | 
|  | // If true, the actual underlying alarms won't be rescheduled until | 
|  | // ResumeUnderlyingAlarmScheduling() is called. | 
|  | bool defer_updates_of_underlying_alarms_ = false; | 
|  | }; | 
|  |  | 
|  | // Proxy classes that allow an individual alarm to be accessed via | 
|  | // a QuicAlarm-compatible API. | 
|  | class QUICHE_EXPORT QuicAlarmProxy { | 
|  | public: | 
|  | QuicAlarmProxy(QuicAlarmMultiplexer* multiplexer, QuicAlarmSlot slot) | 
|  | : multiplexer_(multiplexer), slot_(slot) {} | 
|  |  | 
|  | bool IsSet() const { return multiplexer_->IsSet(slot_); } | 
|  | QuicTime deadline() const { return multiplexer_->GetDeadline(slot_); } | 
|  | bool IsPermanentlyCancelled() const { | 
|  | return multiplexer_->IsPermanentlyCancelled(); | 
|  | } | 
|  |  | 
|  | void Set(QuicTime new_deadline) { multiplexer_->Set(slot_, new_deadline); } | 
|  | void Update(QuicTime new_deadline, QuicTime::Delta granularity) { | 
|  | multiplexer_->Update(slot_, new_deadline, granularity); | 
|  | } | 
|  | void Cancel() { multiplexer_->Cancel(slot_); } | 
|  |  | 
|  | void PermanentCancel() {} | 
|  |  | 
|  | private: | 
|  | friend class ::quic::test::QuicConnectionAlarmsPeer; | 
|  |  | 
|  | QuicAlarmMultiplexer* multiplexer_; | 
|  | QuicAlarmSlot slot_; | 
|  | }; | 
|  |  | 
|  | }  // namespace quic | 
|  |  | 
|  | #endif  // QUICHE_QUIC_CORE_QUIC_CONNECTION_ALARMS_H_ |