dcsctp: Simplify interface for unchanged timeout

When a timer expires, it can optionally return a new expiration value.
Clearly, that value can't be zero, as that would make it expire
immediately again.

To simplify the interface, and make it easier to migrate to
rtc::TimeDelta, change it from an optional value to an always-present
value that - if zero - means that the expiration time should be
unchanged.

This is just an internal refactoring, and not part of any external
interface.

Bug: webrtc:15593
Change-Id: I6e7010d2dbe774ccb260e14fd6b9861c319e2411
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325281
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41045}
This commit is contained in:
Victor Boivie 2023-10-20 13:03:16 +02:00 committed by WebRTC LUCI CQ
parent 783f1d850e
commit 51b93a5417
14 changed files with 44 additions and 42 deletions

View file

@ -42,7 +42,7 @@ class DataTrackerTest : public testing::Test {
}), }),
timer_(timer_manager_.CreateTimer( timer_(timer_manager_.CreateTimer(
"test/delayed_ack", "test/delayed_ack",
[]() { return absl::nullopt; }, []() { return DurationMs(0); },
TimerOptions(DurationMs(0)))), TimerOptions(DurationMs(0)))),
tracker_( tracker_(
std::make_unique<DataTracker>("log: ", timer_.get(), kInitialTSN)) { std::make_unique<DataTracker>("log: ", timer_.get(), kInitialTSN)) {

View file

@ -921,7 +921,7 @@ bool DcSctpSocket::HandleUnrecognizedChunk(
return continue_processing; return continue_processing;
} }
absl::optional<DurationMs> DcSctpSocket::OnInitTimerExpiry() { DurationMs DcSctpSocket::OnInitTimerExpiry() {
RTC_DLOG(LS_VERBOSE) << log_prefix() << "Timer " << t1_init_->name() RTC_DLOG(LS_VERBOSE) << log_prefix() << "Timer " << t1_init_->name()
<< " has expired: " << t1_init_->expiration_count() << " has expired: " << t1_init_->expiration_count()
<< "/" << t1_init_->options().max_restarts.value_or(-1); << "/" << t1_init_->options().max_restarts.value_or(-1);
@ -933,10 +933,10 @@ absl::optional<DurationMs> DcSctpSocket::OnInitTimerExpiry() {
InternalClose(ErrorKind::kTooManyRetries, "No INIT_ACK received"); InternalClose(ErrorKind::kTooManyRetries, "No INIT_ACK received");
} }
RTC_DCHECK(IsConsistent()); RTC_DCHECK(IsConsistent());
return absl::nullopt; return DurationMs(0);
} }
absl::optional<DurationMs> DcSctpSocket::OnCookieTimerExpiry() { DurationMs DcSctpSocket::OnCookieTimerExpiry() {
// https://tools.ietf.org/html/rfc4960#section-4 // https://tools.ietf.org/html/rfc4960#section-4
// "If the T1-cookie timer expires, the endpoint MUST retransmit COOKIE // "If the T1-cookie timer expires, the endpoint MUST retransmit COOKIE
// ECHO and restart the T1-cookie timer without changing state. This MUST // ECHO and restart the T1-cookie timer without changing state. This MUST
@ -957,10 +957,10 @@ absl::optional<DurationMs> DcSctpSocket::OnCookieTimerExpiry() {
} }
RTC_DCHECK(IsConsistent()); RTC_DCHECK(IsConsistent());
return absl::nullopt; return DurationMs(0);
} }
absl::optional<DurationMs> DcSctpSocket::OnShutdownTimerExpiry() { DurationMs DcSctpSocket::OnShutdownTimerExpiry() {
RTC_DLOG(LS_VERBOSE) << log_prefix() << "Timer " << t2_shutdown_->name() RTC_DLOG(LS_VERBOSE) << log_prefix() << "Timer " << t2_shutdown_->name()
<< " has expired: " << t2_shutdown_->expiration_count() << " has expired: " << t2_shutdown_->expiration_count()
<< "/" << "/"
@ -980,7 +980,7 @@ absl::optional<DurationMs> DcSctpSocket::OnShutdownTimerExpiry() {
InternalClose(ErrorKind::kTooManyRetries, "No SHUTDOWN_ACK received"); InternalClose(ErrorKind::kTooManyRetries, "No SHUTDOWN_ACK received");
RTC_DCHECK(IsConsistent()); RTC_DCHECK(IsConsistent());
return absl::nullopt; return DurationMs(0);
} }
// https://tools.ietf.org/html/rfc4960#section-9.2 // https://tools.ietf.org/html/rfc4960#section-9.2

View file

@ -155,9 +155,9 @@ class DcSctpSocket : public DcSctpSocketInterface {
// Closes the association, because of too many retransmission errors. // Closes the association, because of too many retransmission errors.
void CloseConnectionBecauseOfTooManyTransmissionErrors(); void CloseConnectionBecauseOfTooManyTransmissionErrors();
// Timer expiration handlers // Timer expiration handlers
absl::optional<DurationMs> OnInitTimerExpiry(); DurationMs OnInitTimerExpiry();
absl::optional<DurationMs> OnCookieTimerExpiry(); DurationMs OnCookieTimerExpiry();
absl::optional<DurationMs> OnShutdownTimerExpiry(); DurationMs OnShutdownTimerExpiry();
void OnSentPacket(rtc::ArrayView<const uint8_t> packet, void OnSentPacket(rtc::ArrayView<const uint8_t> packet,
SendPacketStatus status); SendPacketStatus status);
// Sends SHUTDOWN or SHUTDOWN-ACK if the socket is shutting down and if all // Sends SHUTDOWN or SHUTDOWN-ACK if the socket is shutting down and if all

View file

@ -164,7 +164,7 @@ void HeartbeatHandler::HandleHeartbeatAck(HeartbeatAckChunk chunk) {
ctx_->ClearTxErrorCounter(); ctx_->ClearTxErrorCounter();
} }
absl::optional<DurationMs> HeartbeatHandler::OnIntervalTimerExpiry() { DurationMs HeartbeatHandler::OnIntervalTimerExpiry() {
if (ctx_->is_connection_established()) { if (ctx_->is_connection_established()) {
HeartbeatInfo info(ctx_->callbacks().TimeMillis()); HeartbeatInfo info(ctx_->callbacks().TimeMillis());
timeout_timer_->set_duration(ctx_->current_rto()); timeout_timer_->set_duration(ctx_->current_rto());
@ -183,14 +183,14 @@ absl::optional<DurationMs> HeartbeatHandler::OnIntervalTimerExpiry() {
<< log_prefix_ << log_prefix_
<< "Will not send HEARTBEAT when connection not established"; << "Will not send HEARTBEAT when connection not established";
} }
return absl::nullopt; return DurationMs(0);
} }
absl::optional<DurationMs> HeartbeatHandler::OnTimeoutTimerExpiry() { DurationMs HeartbeatHandler::OnTimeoutTimerExpiry() {
// Note that the timeout timer is not restarted. It will be started again when // Note that the timeout timer is not restarted. It will be started again when
// the interval timer expires. // the interval timer expires.
RTC_DCHECK(!timeout_timer_->is_running()); RTC_DCHECK(!timeout_timer_->is_running());
ctx_->IncrementTxErrorCounter("HEARTBEAT timeout"); ctx_->IncrementTxErrorCounter("HEARTBEAT timeout");
return absl::nullopt; return DurationMs(0);
} }
} // namespace dcsctp } // namespace dcsctp

View file

@ -50,8 +50,8 @@ class HeartbeatHandler {
void HandleHeartbeatAck(HeartbeatAckChunk chunk); void HandleHeartbeatAck(HeartbeatAckChunk chunk);
private: private:
absl::optional<DurationMs> OnIntervalTimerExpiry(); DurationMs OnIntervalTimerExpiry();
absl::optional<DurationMs> OnTimeoutTimerExpiry(); DurationMs OnTimeoutTimerExpiry();
const absl::string_view log_prefix_; const absl::string_view log_prefix_;
Context* ctx_; Context* ctx_;

View file

@ -347,13 +347,13 @@ void StreamResetHandler::ResetStreams(
} }
} }
absl::optional<DurationMs> StreamResetHandler::OnReconfigTimerExpiry() { DurationMs StreamResetHandler::OnReconfigTimerExpiry() {
if (current_request_->has_been_sent()) { if (current_request_->has_been_sent()) {
// There is an outstanding request, which timed out while waiting for a // There is an outstanding request, which timed out while waiting for a
// response. // response.
if (!ctx_->IncrementTxErrorCounter("RECONFIG timeout")) { if (!ctx_->IncrementTxErrorCounter("RECONFIG timeout")) {
// Timed out. The connection will close after processing the timers. // Timed out. The connection will close after processing the timers.
return absl::nullopt; return DurationMs(0);
} }
} else { } else {
// There is no outstanding request, but there is a prepared one. This means // There is no outstanding request, but there is a prepared one. This means

View file

@ -211,7 +211,7 @@ class StreamResetHandler {
void HandleResponse(const ParameterDescriptor& descriptor); void HandleResponse(const ParameterDescriptor& descriptor);
// Expiration handler for the Reconfig timer. // Expiration handler for the Reconfig timer.
absl::optional<DurationMs> OnReconfigTimerExpiry(); DurationMs OnReconfigTimerExpiry();
const absl::string_view log_prefix_; const absl::string_view log_prefix_;
Context* ctx_; Context* ctx_;

View file

@ -97,11 +97,11 @@ class StreamResetHandlerTest : public testing::Test {
}), }),
delayed_ack_timer_(timer_manager_.CreateTimer( delayed_ack_timer_(timer_manager_.CreateTimer(
"test/delayed_ack", "test/delayed_ack",
[]() { return absl::nullopt; }, []() { return DurationMs(0); },
TimerOptions(DurationMs(0)))), TimerOptions(DurationMs(0)))),
t3_rtx_timer_(timer_manager_.CreateTimer( t3_rtx_timer_(timer_manager_.CreateTimer(
"test/t3_rtx", "test/t3_rtx",
[]() { return absl::nullopt; }, []() { return DurationMs(0); },
TimerOptions(DurationMs(0)))), TimerOptions(DurationMs(0)))),
data_tracker_(std::make_unique<DataTracker>("log: ", data_tracker_(std::make_unique<DataTracker>("log: ",
delayed_ack_timer_.get(), delayed_ack_timer_.get(),

View file

@ -125,7 +125,7 @@ void TransmissionControlBlock::ObserveRTT(DurationMs rtt) {
delayed_ack_timer_->set_duration(delayed_ack_tmo); delayed_ack_timer_->set_duration(delayed_ack_tmo);
} }
absl::optional<DurationMs> TransmissionControlBlock::OnRtxTimerExpiry() { DurationMs TransmissionControlBlock::OnRtxTimerExpiry() {
TimeMs now = callbacks_.TimeMillis(); TimeMs now = callbacks_.TimeMillis();
RTC_DLOG(LS_INFO) << log_prefix_ << "Timer " << t3_rtx_->name() RTC_DLOG(LS_INFO) << log_prefix_ << "Timer " << t3_rtx_->name()
<< " has expired"; << " has expired";
@ -139,13 +139,13 @@ absl::optional<DurationMs> TransmissionControlBlock::OnRtxTimerExpiry() {
SendBufferedPackets(now); SendBufferedPackets(now);
} }
} }
return absl::nullopt; return DurationMs(0);
} }
absl::optional<DurationMs> TransmissionControlBlock::OnDelayedAckTimerExpiry() { DurationMs TransmissionControlBlock::OnDelayedAckTimerExpiry() {
data_tracker_.HandleDelayedAckTimerExpiry(); data_tracker_.HandleDelayedAckTimerExpiry();
MaybeSendSack(); MaybeSendSack();
return absl::nullopt; return DurationMs(0);
} }
void TransmissionControlBlock::MaybeSendSack() { void TransmissionControlBlock::MaybeSendSack() {

View file

@ -149,9 +149,9 @@ class TransmissionControlBlock : public Context {
private: private:
// Will be called when the retransmission timer (t3-rtx) expires. // Will be called when the retransmission timer (t3-rtx) expires.
absl::optional<DurationMs> OnRtxTimerExpiry(); DurationMs OnRtxTimerExpiry();
// Will be called when the delayed ack timer expires. // Will be called when the delayed ack timer expires.
absl::optional<DurationMs> OnDelayedAckTimerExpiry(); DurationMs OnDelayedAckTimerExpiry();
const absl::string_view log_prefix_; const absl::string_view log_prefix_;
const DcSctpOptions options_; const DcSctpOptions options_;

View file

@ -109,9 +109,10 @@ void Timer::Trigger(TimerGeneration generation) {
timeout_->Start(duration, MakeTimeoutId(id_, generation_)); timeout_->Start(duration, MakeTimeoutId(id_, generation_));
} }
absl::optional<DurationMs> new_duration = on_expired_(); DurationMs new_duration = on_expired_();
if (new_duration.has_value() && new_duration != duration_) { RTC_DCHECK(new_duration != DurationMs::InfiniteDuration());
duration_ = new_duration.value(); if (new_duration > DurationMs(0) && new_duration != duration_) {
duration_ = new_duration;
if (is_running_) { if (is_running_) {
// Restart it with new duration. // Restart it with new duration.
timeout_->Stop(); timeout_->Stop();

View file

@ -102,10 +102,11 @@ class Timer {
// The maximum timer duration - one day. // The maximum timer duration - one day.
static constexpr DurationMs kMaxTimerDuration = DurationMs(24 * 3600 * 1000); static constexpr DurationMs kMaxTimerDuration = DurationMs(24 * 3600 * 1000);
// When expired, the timer handler can optionally return a new duration which // When expired, the timer handler can optionally return a new non-zero
// will be set as `duration` and used as base duration when the timer is // duration which will be set as `duration` and used as base duration when the
// restarted and as input to the backoff algorithm. // timer is restarted and as input to the backoff algorithm. If zero is
using OnExpired = std::function<absl::optional<DurationMs>()>; // returned, the current duration is used.
using OnExpired = std::function<DurationMs()>;
// TimerManager will have pointers to these instances, so they must not move. // TimerManager will have pointers to these instances, so they must not move.
Timer(const Timer&) = delete; Timer(const Timer&) = delete;

View file

@ -29,7 +29,7 @@ class TimerTest : public testing::Test {
manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) { manager_([this](webrtc::TaskQueueBase::DelayPrecision precision) {
return timeout_manager_.CreateTimeout(precision); return timeout_manager_.CreateTimeout(precision);
}) { }) {
ON_CALL(on_expired_, Call).WillByDefault(Return(absl::nullopt)); ON_CALL(on_expired_, Call).WillByDefault(Return(DurationMs(0)));
} }
void AdvanceTimeAndRunTimers(DurationMs duration) { void AdvanceTimeAndRunTimers(DurationMs duration) {
@ -48,7 +48,7 @@ class TimerTest : public testing::Test {
TimeMs now_ = TimeMs(0); TimeMs now_ = TimeMs(0);
FakeTimeoutManager timeout_manager_; FakeTimeoutManager timeout_manager_;
TimerManager manager_; TimerManager manager_;
testing::MockFunction<absl::optional<DurationMs>()> on_expired_; testing::MockFunction<DurationMs()> on_expired_;
}; };
TEST_F(TimerTest, TimerIsInitiallyStopped) { TEST_F(TimerTest, TimerIsInitiallyStopped) {
@ -366,7 +366,7 @@ TEST_F(TimerTest, TimerCanBeStartedFromWithinExpirationHandler) {
EXPECT_TRUE(t1->is_running()); EXPECT_TRUE(t1->is_running());
t1->set_duration(DurationMs(5000)); t1->set_duration(DurationMs(5000));
t1->Start(); t1->Start();
return absl::nullopt; return DurationMs(0);
}); });
AdvanceTimeAndRunTimers(DurationMs(1000)); AdvanceTimeAndRunTimers(DurationMs(1000));
@ -378,7 +378,7 @@ TEST_F(TimerTest, TimerCanBeStartedFromWithinExpirationHandler) {
EXPECT_TRUE(t1->is_running()); EXPECT_TRUE(t1->is_running());
t1->set_duration(DurationMs(5000)); t1->set_duration(DurationMs(5000));
t1->Start(); t1->Start();
return absl::make_optional(DurationMs(8000)); return DurationMs(8000);
}); });
AdvanceTimeAndRunTimers(DurationMs(1)); AdvanceTimeAndRunTimers(DurationMs(1));
@ -435,12 +435,12 @@ TEST(TimerManagerTest, TimerManagerPassesPrecisionToCreateTimeoutMethod) {
}); });
// Default TimerOptions. // Default TimerOptions.
manager.CreateTimer( manager.CreateTimer(
"test_timer", []() { return absl::optional<DurationMs>(); }, "test_timer", []() { return DurationMs(0); },
TimerOptions(DurationMs(123))); TimerOptions(DurationMs(123)));
EXPECT_EQ(create_timer_precison, webrtc::TaskQueueBase::DelayPrecision::kLow); EXPECT_EQ(create_timer_precison, webrtc::TaskQueueBase::DelayPrecision::kLow);
// High precision TimerOptions. // High precision TimerOptions.
manager.CreateTimer( manager.CreateTimer(
"test_timer", []() { return absl::optional<DurationMs>(); }, "test_timer", []() { return DurationMs(0); },
TimerOptions(DurationMs(123), TimerBackoffAlgorithm::kExponential, TimerOptions(DurationMs(123), TimerBackoffAlgorithm::kExponential,
absl::nullopt, DurationMs::InfiniteDuration(), absl::nullopt, DurationMs::InfiniteDuration(),
webrtc::TaskQueueBase::DelayPrecision::kHigh)); webrtc::TaskQueueBase::DelayPrecision::kHigh));
@ -448,7 +448,7 @@ TEST(TimerManagerTest, TimerManagerPassesPrecisionToCreateTimeoutMethod) {
webrtc::TaskQueueBase::DelayPrecision::kHigh); webrtc::TaskQueueBase::DelayPrecision::kHigh);
// Low precision TimerOptions. // Low precision TimerOptions.
manager.CreateTimer( manager.CreateTimer(
"test_timer", []() { return absl::optional<DurationMs>(); }, "test_timer", []() { return DurationMs(0); },
TimerOptions(DurationMs(123), TimerBackoffAlgorithm::kExponential, TimerOptions(DurationMs(123), TimerBackoffAlgorithm::kExponential,
absl::nullopt, DurationMs::InfiniteDuration(), absl::nullopt, DurationMs::InfiniteDuration(),
webrtc::TaskQueueBase::DelayPrecision::kLow)); webrtc::TaskQueueBase::DelayPrecision::kLow));

View file

@ -74,7 +74,7 @@ class RetransmissionQueueTest : public testing::Test {
}), }),
timer_(timer_manager_.CreateTimer( timer_(timer_manager_.CreateTimer(
"test/t3_rtx", "test/t3_rtx",
[]() { return absl::nullopt; }, []() { return DurationMs(0); },
TimerOptions(options_.rto_initial))) {} TimerOptions(options_.rto_initial))) {}
std::function<SendQueue::DataToSend(TimeMs, size_t)> CreateChunk( std::function<SendQueue::DataToSend(TimeMs, size_t)> CreateChunk(