diff --git a/net/dcsctp/tx/retransmission_timeout.cc b/net/dcsctp/tx/retransmission_timeout.cc index 7d545a07d0..2cb59f13e1 100644 --- a/net/dcsctp/tx/retransmission_timeout.cc +++ b/net/dcsctp/tx/retransmission_timeout.cc @@ -9,17 +9,12 @@ */ #include "net/dcsctp/tx/retransmission_timeout.h" -#include +#include #include #include "net/dcsctp/public/dcsctp_options.h" namespace dcsctp { -namespace { -// https://tools.ietf.org/html/rfc4960#section-15 -constexpr double kRtoAlpha = 0.125; -constexpr double kRtoBeta = 0.25; -} // namespace RetransmissionTimeout::RetransmissionTimeout(const DcSctpOptions& options) : min_rto_(*options.rto_min), @@ -28,42 +23,39 @@ RetransmissionTimeout::RetransmissionTimeout(const DcSctpOptions& options) rto_(*options.rto_initial) {} void RetransmissionTimeout::ObserveRTT(DurationMs measured_rtt) { - double rtt = *measured_rtt; + int32_t rtt = *measured_rtt; // Unrealistic values will be skipped. If a wrongly measured (or otherwise // corrupt) value was processed, it could change the state in a way that would // take a very long time to recover. - if (rtt < 0.0 || rtt > max_rtt_) { + if (rtt < 0 || rtt > max_rtt_) { return; } + // From https://tools.ietf.org/html/rfc4960#section-6.3.1, but avoiding + // floating point math by implementing algorithm from "V. Jacobson: Congestion + // avoidance and control", but adapted for SCTP. if (first_measurement_) { - // https://tools.ietf.org/html/rfc4960#section-6.3.1 - // "When the first RTT measurement R is made, set - // SRTT <- R, - // RTTVAR <- R/2, and - // RTO <- SRTT + 4 * RTTVAR." - srtt_ = rtt; - rttvar_ = rtt * 0.5; - rto_ = srtt_ + 4 * rttvar_; + scaled_srtt_ = rtt << kRttShift; + scaled_rtt_var_ = (rtt / 2) << kRttVarShift; first_measurement_ = false; } else { - // https://tools.ietf.org/html/rfc4960#section-6.3.1 - // "When a new RTT measurement R' is made, set - // RTTVAR <- (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'| - // SRTT <- (1 - RTO.Alpha) * SRTT + RTO.Alpha * R' - // RTO <- SRTT + 4 * RTTVAR." - rttvar_ = (1 - kRtoBeta) * rttvar_ + kRtoBeta * std::abs(srtt_ - rtt); - srtt_ = (1 - kRtoAlpha) * srtt_ + kRtoAlpha * rtt; - rto_ = srtt_ + 4 * rttvar_; + rtt -= (scaled_srtt_ >> kRttShift); + scaled_srtt_ += rtt; + if (rtt < 0) { + rtt = -rtt; + } + rtt -= (scaled_rtt_var_ >> kRttVarShift); + scaled_rtt_var_ += rtt; } + rto_ = (scaled_srtt_ >> kRttShift) + scaled_rtt_var_; // If the RTO becomes smaller or equal to RTT, expiration timers will be // scheduled at the same time as packets are expected. Only happens in // extremely stable RTTs, i.e. in simulations. - rto_ = std::fmax(rto_, rtt + 1); + rto_ = std::max(rto_, rtt + 1); // Clamp RTO between min and max. - rto_ = std::fmin(std::fmax(rto_, min_rto_), max_rto_); + rto_ = std::min(std::max(rto_, min_rto_), max_rto_); } } // namespace dcsctp diff --git a/net/dcsctp/tx/retransmission_timeout.h b/net/dcsctp/tx/retransmission_timeout.h index 0fac33e59c..f3a95532d0 100644 --- a/net/dcsctp/tx/retransmission_timeout.h +++ b/net/dcsctp/tx/retransmission_timeout.h @@ -27,6 +27,8 @@ namespace dcsctp { // a lot, which is an indicator of a bad connection. class RetransmissionTimeout { public: + static constexpr int kRttShift = 3; + static constexpr int kRttVarShift = 2; explicit RetransmissionTimeout(const DcSctpOptions& options); // To be called when a RTT has been measured, to update the RTO value. @@ -36,22 +38,20 @@ class RetransmissionTimeout { DurationMs rto() const { return DurationMs(rto_); } // Returns the smoothed RTT value, in milliseconds. - DurationMs srtt() const { return DurationMs(srtt_); } + DurationMs srtt() const { return DurationMs(scaled_srtt_ >> kRttShift); } private: - // Note that all intermediate state calculation is done in the floating point - // domain, to maintain precision. - const double min_rto_; - const double max_rto_; - const double max_rtt_; + const int32_t min_rto_; + const int32_t max_rto_; + const int32_t max_rtt_; // If this is the first measurement bool first_measurement_ = true; - // Smoothed Round-Trip Time - double srtt_ = 0.0; - // Round-Trip Time Variation - double rttvar_ = 0.0; + // Smoothed Round-Trip Time, shifted by kRttShift + int32_t scaled_srtt_ = 0; + // Round-Trip Time Variation, shifted by kRttVarShift + int32_t scaled_rtt_var_ = 0; // Retransmission Timeout - double rto_; + int32_t rto_; }; } // namespace dcsctp diff --git a/net/dcsctp/tx/retransmission_timeout_test.cc b/net/dcsctp/tx/retransmission_timeout_test.cc index 3b2e3399fe..d2d071948e 100644 --- a/net/dcsctp/tx/retransmission_timeout_test.cc +++ b/net/dcsctp/tx/retransmission_timeout_test.cc @@ -88,7 +88,7 @@ TEST(RetransmissionTimeoutTest, CalculatesRtoForStableRtt) { rto_.ObserveRTT(DurationMs(125)); EXPECT_EQ(*rto_.rto(), 233); rto_.ObserveRTT(DurationMs(127)); - EXPECT_EQ(*rto_.rto(), 208); + EXPECT_EQ(*rto_.rto(), 209); } TEST(RetransmissionTimeoutTest, CalculatesRtoForUnstableRtt) { @@ -116,21 +116,21 @@ TEST(RetransmissionTimeoutTest, WillStabilizeAfterAWhile) { rto_.ObserveRTT(DurationMs(124)); EXPECT_EQ(*rto_.rto(), 800); rto_.ObserveRTT(DurationMs(122)); - EXPECT_EQ(*rto_.rto(), 709); + EXPECT_EQ(*rto_.rto(), 710); rto_.ObserveRTT(DurationMs(123)); - EXPECT_EQ(*rto_.rto(), 630); + EXPECT_EQ(*rto_.rto(), 631); rto_.ObserveRTT(DurationMs(124)); - EXPECT_EQ(*rto_.rto(), 561); + EXPECT_EQ(*rto_.rto(), 562); rto_.ObserveRTT(DurationMs(122)); - EXPECT_EQ(*rto_.rto(), 504); + EXPECT_EQ(*rto_.rto(), 505); rto_.ObserveRTT(DurationMs(124)); - EXPECT_EQ(*rto_.rto(), 453); + EXPECT_EQ(*rto_.rto(), 454); rto_.ObserveRTT(DurationMs(124)); - EXPECT_EQ(*rto_.rto(), 409); + EXPECT_EQ(*rto_.rto(), 410); rto_.ObserveRTT(DurationMs(124)); EXPECT_EQ(*rto_.rto(), 372); rto_.ObserveRTT(DurationMs(124)); - EXPECT_EQ(*rto_.rto(), 339); + EXPECT_EQ(*rto_.rto(), 340); } TEST(RetransmissionTimeoutTest, WillAlwaysStayAboveRTT) {