dcsctp: Use TimeDelta in TX path

This commit replaces the internal use of DurationMs, with millisecond
precision, to webrtc::TimeDelta, which uses microsecond precision.

This is just a refactoring. The only change to the public API is
convenience methods to convert between DurationMs and webrtc::TimeDelta.

Bug: webrtc:15593
Change-Id: Ida861bf585c716be5f898d0e7ef98da2c15268b7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325402
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41062}
This commit is contained in:
Victor Boivie 2023-10-20 13:16:35 +02:00 committed by WebRTC LUCI CQ
parent 8039cdbe48
commit b78e6a9305
16 changed files with 48 additions and 31 deletions

View File

@ -11,6 +11,7 @@ import("../../../webrtc.gni")
rtc_source_set("types") {
deps = [
"../../../api:array_view",
"../../../api/units:time_delta",
"../../../rtc_base:strong_alias",
]
sources = [

View File

@ -14,6 +14,7 @@
#include <cstdint>
#include <limits>
#include "api/units/time_delta.h"
#include "rtc_base/strong_alias.h"
namespace dcsctp {
@ -41,6 +42,10 @@ class DurationMs : public webrtc::StrongAlias<class DurationMsTag, int32_t> {
constexpr explicit DurationMs(const UnderlyingType& v)
: webrtc::StrongAlias<class DurationMsTag, int32_t>(v) {}
constexpr explicit DurationMs(webrtc::TimeDelta v)
: webrtc::StrongAlias<class DurationMsTag, int32_t>(
v.IsInfinite() ? InfiniteDuration() : DurationMs(v.ms())) {}
static constexpr DurationMs InfiniteDuration() {
return DurationMs(std::numeric_limits<int32_t>::max());
}
@ -58,6 +63,11 @@ class DurationMs : public webrtc::StrongAlias<class DurationMsTag, int32_t> {
value_ *= factor;
return *this;
}
constexpr webrtc::TimeDelta ToTimeDelta() const {
return *this == DurationMs::InfiniteDuration()
? webrtc::TimeDelta::PlusInfinity()
: webrtc::TimeDelta::Millis(value_);
}
};
constexpr inline DurationMs operator+(DurationMs lhs, DurationMs rhs) {

View File

@ -39,8 +39,8 @@ class Context {
// Returns the socket callbacks.
virtual DcSctpSocketCallbacks& callbacks() const = 0;
// Observes a measured RTT value, in milliseconds.
virtual void ObserveRTT(DurationMs rtt_ms) = 0;
// Observes a measured RTT value.
virtual void ObserveRTT(webrtc::TimeDelta rtt_ms) = 0;
// Returns the current Retransmission Timeout (rto) value, in milliseconds.
virtual DurationMs current_rto() const = 0;

View File

@ -155,7 +155,7 @@ void HeartbeatHandler::HandleHeartbeatAck(HeartbeatAckChunk chunk) {
TimeMs now = ctx_->callbacks().TimeMillis();
if (info->created_at() > TimeMs(0) && info->created_at() <= now) {
ctx_->ObserveRTT(now - info->created_at());
ctx_->ObserveRTT((now - info->created_at()).ToTimeDelta());
}
// https://tools.ietf.org/html/rfc4960#section-8.1

View File

@ -30,6 +30,7 @@ using ::testing::IsEmpty;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SizeIs;
using ::webrtc::TimeDelta;
constexpr DurationMs kHeartbeatInterval = DurationMs(30'000);
@ -136,7 +137,7 @@ TEST_F(HeartbeatHandlerTest, SendsHeartbeatRequestsOnIdleChannel) {
// Respond a while later. This RTT will be measured by the handler
constexpr DurationMs rtt(313);
EXPECT_CALL(context_, ObserveRTT(rtt)).Times(1);
EXPECT_CALL(context_, ObserveRTT(rtt.ToTimeDelta())).Times(1);
callbacks_.AdvanceTime(rtt);
handler_.HandleHeartbeatAck(std::move(ack));

View File

@ -51,7 +51,7 @@ class MockContext : public Context {
MOCK_METHOD(TSN, peer_initial_tsn, (), (const, override));
MOCK_METHOD(DcSctpSocketCallbacks&, callbacks, (), (const, override));
MOCK_METHOD(void, ObserveRTT, (DurationMs rtt_ms), (override));
MOCK_METHOD(void, ObserveRTT, (webrtc::TimeDelta rtt), (override));
MOCK_METHOD(DurationMs, current_rto, (), (const, override));
MOCK_METHOD(bool,
IncrementTxErrorCounter,

View File

@ -48,6 +48,7 @@ using ::testing::Property;
using ::testing::Return;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
using ::webrtc::TimeDelta;
using ResponseResult = ReconfigurationResponseParameter::Result;
using SkippedStream = AnyForwardTsnChunk::SkippedStream;
@ -115,7 +116,7 @@ class StreamResetHandlerTest : public testing::Test {
kMyInitialTsn,
kArwnd,
producer_,
[](DurationMs rtt_ms) {},
[](TimeDelta rtt) {},
[]() {},
*t3_rtx_timer_,
DcSctpOptions())),
@ -205,7 +206,7 @@ class StreamResetHandlerTest : public testing::Test {
reasm_->RestoreFromState(state);
retransmission_queue_ = std::make_unique<RetransmissionQueue>(
"", &callbacks_, kMyInitialTsn, kArwnd, producer_,
[](DurationMs rtt_ms) {}, []() {}, *t3_rtx_timer_, DcSctpOptions(),
[](TimeDelta rtt) {}, []() {}, *t3_rtx_timer_, DcSctpOptions(),
/*supports_partial_reliability=*/true,
/*use_message_interleaving=*/false);
retransmission_queue_->RestoreFromState(state);

View File

@ -37,6 +37,7 @@
#include "rtc_base/strings/string_builder.h"
namespace dcsctp {
using ::webrtc::TimeDelta;
TransmissionControlBlock::TransmissionControlBlock(
TimerManager& timer_manager,
@ -112,10 +113,10 @@ TransmissionControlBlock::TransmissionControlBlock(
send_queue.EnableMessageInterleaving(capabilities.message_interleaving);
}
void TransmissionControlBlock::ObserveRTT(DurationMs rtt) {
void TransmissionControlBlock::ObserveRTT(TimeDelta rtt) {
DurationMs prev_rto = rto_.rto();
rto_.ObserveRTT(rtt);
RTC_DLOG(LS_VERBOSE) << log_prefix_ << "new rtt=" << *rtt
rto_.ObserveRTT(DurationMs(rtt));
RTC_DLOG(LS_VERBOSE) << log_prefix_ << "new rtt=" << webrtc::ToString(rtt)
<< ", srtt=" << *rto_.srtt() << ", rto=" << *rto_.rto()
<< " (" << *prev_rto << ")";
t3_rtx_->set_duration(rto_.rto());

View File

@ -67,7 +67,7 @@ class TransmissionControlBlock : public Context {
TSN my_initial_tsn() const override { return my_initial_tsn_; }
TSN peer_initial_tsn() const override { return peer_initial_tsn_; }
DcSctpSocketCallbacks& callbacks() const override { return callbacks_; }
void ObserveRTT(DurationMs rtt) override;
void ObserveRTT(webrtc::TimeDelta rtt) override;
DurationMs current_rto() const override { return rto_.rto(); }
bool IncrementTxErrorCounter(absl::string_view reason) override {
return tx_error_counter_.Increment(reason);

View File

@ -103,6 +103,7 @@ rtc_library("outstanding_data") {
":retransmission_timeout",
":send_queue",
"../../../api:array_view",
"../../../api/units:time_delta",
"../../../rtc_base:checks",
"../../../rtc_base:logging",
"../../../rtc_base/containers:flat_set",

View File

@ -14,6 +14,7 @@
#include <utility>
#include <vector>
#include "api/units/time_delta.h"
#include "net/dcsctp/common/math.h"
#include "net/dcsctp/common/sequence_numbers.h"
#include "net/dcsctp/public/types.h"
@ -441,8 +442,8 @@ void OutstandingData::NackAll() {
RTC_DCHECK(IsConsistent());
}
absl::optional<DurationMs> OutstandingData::MeasureRTT(TimeMs now,
UnwrappedTSN tsn) const {
webrtc::TimeDelta OutstandingData::MeasureRTT(TimeMs now,
UnwrappedTSN tsn) const {
auto it = outstanding_data_.find(tsn);
if (it != outstanding_data_.end() && !it->second.has_been_retransmitted()) {
// https://tools.ietf.org/html/rfc4960#section-6.3.1
@ -450,9 +451,9 @@ absl::optional<DurationMs> OutstandingData::MeasureRTT(TimeMs now,
// packets that were retransmitted (and thus for which it is ambiguous
// whether the reply was for the first instance of the chunk or for a
// later instance)"
return now - it->second.time_sent();
return (now - it->second.time_sent()).ToTimeDelta();
}
return absl::nullopt;
return webrtc::TimeDelta::PlusInfinity();
}
std::vector<std::pair<TSN, OutstandingData::State>>

View File

@ -147,8 +147,8 @@ class OutstandingData {
// Given the current time and a TSN, it returns the measured RTT between when
// the chunk was sent and now. It takes into acccount Karn's algorithm, so if
// the chunk has ever been retransmitted, it will return absl::nullopt.
absl::optional<DurationMs> MeasureRTT(TimeMs now, UnwrappedTSN tsn) const;
// the chunk has ever been retransmitted, it will return `PlusInfinity()`.
webrtc::TimeDelta MeasureRTT(TimeMs now, UnwrappedTSN tsn) const;
// Returns the internal state of all queued chunks. This is only used in
// unit-tests.

View File

@ -37,6 +37,7 @@ using ::testing::Property;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::UnorderedElementsAre;
using ::webrtc::TimeDelta;
constexpr TimeMs kNow(42);
constexpr OutgoingMessageId kMessageId = OutgoingMessageId(17);
@ -365,12 +366,11 @@ TEST_F(OutstandingDataTest, MeasureRTT) {
buf_.Insert(kMessageId, gen_.Ordered({1}, "BE"), kNow + DurationMs(1));
buf_.Insert(kMessageId, gen_.Ordered({1}, "BE"), kNow + DurationMs(2));
static constexpr DurationMs kDuration(123);
ASSERT_HAS_VALUE_AND_ASSIGN(
DurationMs duration,
buf_.MeasureRTT(kNow + kDuration, unwrapper_.Unwrap(TSN(11))));
static constexpr TimeDelta kDuration = TimeDelta::Millis(123);
TimeDelta duration =
buf_.MeasureRTT(kNow + DurationMs(kDuration), unwrapper_.Unwrap(TSN(11)));
EXPECT_EQ(duration, kDuration - DurationMs(1));
EXPECT_EQ(duration, kDuration - TimeDelta::Millis(1));
}
TEST_F(OutstandingDataTest, MustRetransmitBeforeGettingNackedAgain) {

View File

@ -44,6 +44,7 @@
namespace dcsctp {
namespace {
using ::webrtc::TimeDelta;
// Allow sending only slightly less than an MTU, to account for headers.
constexpr float kMinBytesRequiredToSendFactor = 0.9;
@ -55,7 +56,7 @@ RetransmissionQueue::RetransmissionQueue(
TSN my_initial_tsn,
size_t a_rwnd,
SendQueue& send_queue,
std::function<void(DurationMs rtt)> on_new_rtt,
std::function<void(TimeDelta rtt)> on_new_rtt,
std::function<void()> on_clear_retransmission_counter,
Timer& t3_rtx,
const DcSctpOptions& options,
@ -345,11 +346,10 @@ void RetransmissionQueue::UpdateRTT(TimeMs now,
// TODO(boivie): Consider occasionally sending DATA chunks with I-bit set and
// use only those packets for measurement.
absl::optional<DurationMs> rtt =
outstanding_data_.MeasureRTT(now, cumulative_tsn_ack);
TimeDelta rtt = outstanding_data_.MeasureRTT(now, cumulative_tsn_ack);
if (rtt.has_value()) {
on_new_rtt_(*rtt);
if (rtt.IsFinite()) {
on_new_rtt_(rtt);
}
}

View File

@ -60,7 +60,7 @@ class RetransmissionQueue {
TSN my_initial_tsn,
size_t a_rwnd,
SendQueue& send_queue,
std::function<void(DurationMs rtt)> on_new_rtt,
std::function<void(webrtc::TimeDelta rtt)> on_new_rtt,
std::function<void()> on_clear_retransmission_counter,
Timer& t3_rtx,
const DcSctpOptions& options,
@ -230,7 +230,7 @@ class RetransmissionQueue {
// The size of the data chunk (DATA/I-DATA) header that is used.
const size_t data_chunk_header_size_;
// Called when a new RTT measurement has been done
const std::function<void(DurationMs rtt)> on_new_rtt_;
const std::function<void(webrtc::TimeDelta rtt)> on_new_rtt_;
// Called when a SACK has been seen that cleared the retransmission counter.
const std::function<void()> on_clear_retransmission_counter_;
// The retransmission counter.

View File

@ -52,6 +52,7 @@ using ::testing::Pair;
using ::testing::Return;
using ::testing::SizeIs;
using ::testing::UnorderedElementsAre;
using ::webrtc::TimeDelta;
constexpr uint32_t kArwnd = 100000;
constexpr uint32_t kMaxMtu = 1191;
@ -130,7 +131,7 @@ class RetransmissionQueueTest : public testing::Test {
TimeMs now_ = TimeMs(0);
FakeTimeoutManager timeout_manager_;
TimerManager timer_manager_;
NiceMock<MockFunction<void(DurationMs rtt_ms)>> on_rtt_;
NiceMock<MockFunction<void(TimeDelta rtt_ms)>> on_rtt_;
NiceMock<MockFunction<void()>> on_clear_retransmission_counter_;
NiceMock<MockSendQueue> producer_;
std::unique_ptr<Timer> timer_;
@ -864,7 +865,7 @@ TEST_F(RetransmissionQueueTest, MeasureRTT) {
now_ = now_ + DurationMs(123);
EXPECT_CALL(on_rtt_, Call(DurationMs(123))).Times(1);
EXPECT_CALL(on_rtt_, Call(TimeDelta::Millis(123))).Times(1);
queue.HandleSack(now_, SackChunk(TSN(10), kArwnd, {}, {}));
}