Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
congestion_controller.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6
7#include <algorithm>
8#include <cmath>
9
11{
12
13auto congestion_state_to_string(congestion_state state) noexcept -> const char*
14{
15 switch (state)
16 {
18 return "slow_start";
20 return "congestion_avoidance";
22 return "recovery";
23 default:
24 return "unknown";
25 }
26}
27
32
35 , max_datagram_size_(max_datagram_size)
36 , congestion_recovery_start_()
37{
41}
42
43auto congestion_controller::can_send(size_t bytes) const noexcept -> bool
44{
45 if (bytes == 0)
46 {
47 return bytes_in_flight_ < cwnd_;
48 }
49 return bytes_in_flight_ + bytes <= cwnd_;
50}
51
52auto congestion_controller::available_window() const noexcept -> size_t
53{
55 {
56 return 0;
57 }
58 return cwnd_ - bytes_in_flight_;
59}
60
61auto congestion_controller::on_packet_sent(size_t bytes) -> void
62{
63 bytes_in_flight_ += bytes;
64}
65
67 std::chrono::steady_clock::time_point ack_time)
68 -> void
69{
70 // Update bytes in flight
71 if (packet.in_flight && bytes_in_flight_ >= packet.sent_bytes)
72 {
73 bytes_in_flight_ -= packet.sent_bytes;
74 }
75
76 // Don't increase cwnd in recovery
77 if (is_in_recovery(packet.sent_time))
78 {
79 return;
80 }
81
82 // Exit recovery if we're receiving ACKs for packets sent after recovery started
83 if (state_ == congestion_state::recovery)
84 {
86 }
87
88 // Congestion window increase (RFC 9002 Section 7.3.1)
89 if (state_ == congestion_state::slow_start)
90 {
91 // Slow start: increase cwnd by the number of bytes acknowledged
92 cwnd_ += packet.sent_bytes;
93
94 // Check if we've exceeded slow start threshold
95 if (cwnd_ >= ssthresh_)
96 {
98 }
99 }
100 else
101 {
102 // Congestion avoidance: AIMD
103 // cwnd += max_datagram_size * acked_bytes / cwnd
104 auto increment = (max_datagram_size_ * packet.sent_bytes) / cwnd_;
105 if (increment == 0)
106 {
107 increment = 1; // Always increase by at least 1 byte
108 }
109 cwnd_ += increment;
110 }
111}
112
114{
115 // Update bytes in flight
116 if (packet.in_flight && bytes_in_flight_ >= packet.sent_bytes)
117 {
118 bytes_in_flight_ -= packet.sent_bytes;
119 }
120
121 // Trigger congestion event
122 on_congestion_event(packet.sent_time);
123}
124
126 std::chrono::steady_clock::time_point sent_time) -> void
127{
128 // Only respond to congestion once per RTT (RFC 9002 Section 7.3.2)
129 if (is_in_recovery(sent_time))
130 {
131 return;
132 }
133
134 // Enter recovery
135 congestion_recovery_start_ = std::chrono::steady_clock::now();
137
138 // Reduce cwnd and set ssthresh
139 // ssthresh = cwnd * kLossReductionFactor
140 // cwnd = max(ssthresh, minimum_window)
141 ssthresh_ = static_cast<size_t>(
142 static_cast<double>(cwnd_) * kLossReductionFactor);
143 cwnd_ = std::max(ssthresh_, minimum_window_);
144}
145
147 std::chrono::steady_clock::time_point sent_time) -> void
148{
149 // RFC 9002 Section 7.1: Processing ECN Information
150 // ECN-CE marks indicate congestion without packet loss
151 // Respond same as packet loss, but only once per RTT
152
153 // Only respond to congestion once per RTT
154 if (is_in_recovery(sent_time))
155 {
156 return;
157 }
158
159 // Enter recovery
160 congestion_recovery_start_ = std::chrono::steady_clock::now();
162
163 // Reduce cwnd and set ssthresh
164 // RFC 9002 Section 7.1: "a sender that receives an ACK frame with
165 // ECN feedback indicating the CE codepoint MUST enter congestion
166 // avoidance"
167 ssthresh_ = static_cast<size_t>(
168 static_cast<double>(cwnd_) * kLossReductionFactor);
169 cwnd_ = std::max(ssthresh_, minimum_window_);
170}
171
173{
174 // RFC 9002 Section 7.6: Reset cwnd to minimum
175 cwnd_ = minimum_window_;
176 ssthresh_ = cwnd_;
178 congestion_recovery_start_ = std::chrono::steady_clock::time_point{};
179}
180
182 std::chrono::steady_clock::time_point sent_time) const noexcept -> bool
183{
184 if (state_ != congestion_state::recovery)
185 {
186 return false;
187 }
188
189 // We're in recovery if the packet was sent before recovery started
190 return sent_time <= congestion_recovery_start_;
191}
192
194{
195 max_datagram_size_ = size;
196 initial_window_ = kInitialWindowPackets * max_datagram_size_;
197 minimum_window_ = kMinimumWindowPackets * max_datagram_size_;
198
199 // Ensure cwnd is at least minimum window
200 cwnd_ = std::max(cwnd_, minimum_window_);
201}
202
204{
206 cwnd_ = initial_window_;
207 ssthresh_ = std::numeric_limits<size_t>::max();
208 bytes_in_flight_ = 0;
209 congestion_recovery_start_ = std::chrono::steady_clock::time_point{};
210}
211
212} // namespace kcenon::network::protocols::quic
QUIC congestion control (RFC 9002 Section 7)
auto on_packet_acked(const sent_packet &packet, std::chrono::steady_clock::time_point ack_time) -> void
Handle packet acknowledgment (RFC 9002 Section 7.3)
auto set_max_datagram_size(size_t size) -> void
Set max datagram size.
auto on_packet_lost(const sent_packet &packet) -> void
Handle packet loss (RFC 9002 Section 7.3.2)
auto on_congestion_event(std::chrono::steady_clock::time_point sent_time) -> void
Handle congestion event (RFC 9002 Section 7.3.2)
auto on_persistent_congestion(const rtt_estimator &rtt) -> void
Handle persistent congestion detection (RFC 9002 Section 7.6)
auto can_send(size_t bytes=0) const noexcept -> bool
Check if we can send more data.
auto available_window() const noexcept -> size_t
Get available congestion window.
auto on_packet_sent(size_t bytes) -> void
Record bytes sent.
static constexpr size_t kMinimumWindowPackets
Minimum window in packets.
auto reset() -> void
Reset congestion controller to initial state.
auto on_ecn_congestion(std::chrono::steady_clock::time_point sent_time) -> void
Handle ECN congestion signal (RFC 9002 Section 7.1)
auto is_in_recovery(std::chrono::steady_clock::time_point sent_time) const noexcept -> bool
Check if currently in recovery period.
static constexpr size_t kInitialWindowPackets
Number of datagrams for initial window.
RTT estimation for QUIC (RFC 9002 Section 5)
congestion_state
States of the congestion controller (RFC 9002 Section 7)
@ recovery
Congestion recovery after loss.
auto congestion_state_to_string(congestion_state state) noexcept -> const char *
Convert congestion state to string.
Information about a sent packet for loss detection (RFC 9002 Section A.1.1)