| // 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_ |