webrtc/net/dcsctp/timer/timer_test.cc
Victor Boivie cebbff7f58 dcsctp: Specify the max timer backoff duration
By allowing the max timer backoff duration to be limited, a socket can
fast recover in case of intermittent network issues. Before this CL, the
exponential backoff algorithm could result in very long retry durations
(in the order of minutes), when connection has been lost or been flaky
for a long while.

Note that limiting the maximum backoff duration might require
compensating the maximum retransmission limit to avoid closing the
socket prematurely due to reaching the maximum retransmission limit much
faster than previously.

Bug: webrtc:13129
Change-Id: Ib94030d666433e3fa1a2c8ef69750a1afab8ef94
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/230702
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34913}
2021-09-03 10:26:50 +00:00

427 lines
13 KiB
C++

/*
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "net/dcsctp/timer/timer.h"
#include <memory>
#include "absl/types/optional.h"
#include "net/dcsctp/public/timeout.h"
#include "net/dcsctp/timer/fake_timeout.h"
#include "rtc_base/gunit.h"
#include "test/gmock.h"
namespace dcsctp {
namespace {
using ::testing::Return;
class TimerTest : public testing::Test {
protected:
TimerTest()
: timeout_manager_([this]() { return now_; }),
manager_([this]() { return timeout_manager_.CreateTimeout(); }) {
ON_CALL(on_expired_, Call).WillByDefault(Return(absl::nullopt));
}
void AdvanceTimeAndRunTimers(DurationMs duration) {
now_ = now_ + duration;
for (;;) {
absl::optional<TimeoutID> timeout_id =
timeout_manager_.GetNextExpiredTimeout();
if (!timeout_id.has_value()) {
break;
}
manager_.HandleTimeout(*timeout_id);
}
}
TimeMs now_ = TimeMs(0);
FakeTimeoutManager timeout_manager_;
TimerManager manager_;
testing::MockFunction<absl::optional<DurationMs>()> on_expired_;
};
TEST_F(TimerTest, TimerIsInitiallyStopped) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
EXPECT_FALSE(t1->is_running());
}
TEST_F(TimerTest, TimerExpiresAtGivenTime) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
EXPECT_TRUE(t1->is_running());
AdvanceTimeAndRunTimers(DurationMs(4000));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
}
TEST_F(TimerTest, TimerReschedulesAfterExpiredWithFixedBackoff) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
EXPECT_EQ(t1->expiration_count(), 0);
AdvanceTimeAndRunTimers(DurationMs(4000));
// Fire first time
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_TRUE(t1->is_running());
EXPECT_EQ(t1->expiration_count(), 1);
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4000));
// Second time
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_TRUE(t1->is_running());
EXPECT_EQ(t1->expiration_count(), 2);
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4000));
// Third time
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_TRUE(t1->is_running());
EXPECT_EQ(t1->expiration_count(), 3);
}
TEST_F(TimerTest, TimerWithNoRestarts) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed,
/*max_restart=*/0));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
AdvanceTimeAndRunTimers(DurationMs(4000));
// Fire first time
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_FALSE(t1->is_running());
// Second time - shouldn't fire
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(5000));
EXPECT_FALSE(t1->is_running());
}
TEST_F(TimerTest, TimerWithOneRestart) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed,
/*max_restart=*/1));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
AdvanceTimeAndRunTimers(DurationMs(4000));
// Fire first time
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_TRUE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4000));
// Second time - max restart limit reached.
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_FALSE(t1->is_running());
// Third time - should not fire.
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(5000));
EXPECT_FALSE(t1->is_running());
}
TEST_F(TimerTest, TimerWithTwoRestart) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed,
/*max_restart=*/2));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
AdvanceTimeAndRunTimers(DurationMs(4000));
// Fire first time
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_TRUE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4000));
// Second time
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_TRUE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4000));
// Third time
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_FALSE(t1->is_running());
}
TEST_F(TimerTest, TimerWithExponentialBackoff) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
t1->Start();
// Fire first time at 5 seconds
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(5000));
// Second time at 5*2^1 = 10 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(9000));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
// Third time at 5*2^2 = 20 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(19000));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
// Fourth time at 5*2^3 = 40 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(39000));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
}
TEST_F(TimerTest, StartTimerWillStopAndStart) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
t1->Start();
AdvanceTimeAndRunTimers(DurationMs(3000));
t1->Start();
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(2000));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(3000));
}
TEST_F(TimerTest, ExpirationCounterWillResetIfStopped) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
t1->Start();
// Fire first time at 5 seconds
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(5000));
EXPECT_EQ(t1->expiration_count(), 1);
// Second time at 5*2^1 = 10 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(9000));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_EQ(t1->expiration_count(), 2);
t1->Start();
EXPECT_EQ(t1->expiration_count(), 0);
// Third time at 5*2^0 = 5 seconds later.
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4000));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_EQ(t1->expiration_count(), 1);
}
TEST_F(TimerTest, StopTimerWillMakeItNotExpire) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kExponential));
t1->Start();
EXPECT_TRUE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4000));
t1->Stop();
EXPECT_FALSE(t1->is_running());
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(1000));
}
TEST_F(TimerTest, ReturningNewDurationWhenExpired) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(5000), TimerBackoffAlgorithm::kFixed));
EXPECT_CALL(on_expired_, Call).Times(0);
t1->Start();
EXPECT_EQ(t1->duration(), DurationMs(5000));
AdvanceTimeAndRunTimers(DurationMs(4000));
// Fire first time
EXPECT_CALL(on_expired_, Call).WillOnce(Return(DurationMs(2000)));
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_EQ(t1->duration(), DurationMs(2000));
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(1000));
// Second time
EXPECT_CALL(on_expired_, Call).WillOnce(Return(DurationMs(10000)));
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_EQ(t1->duration(), DurationMs(10000));
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(9000));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
}
TEST_F(TimerTest, TimersHaveMaximumDuration) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(1000), TimerBackoffAlgorithm::kExponential));
t1->set_duration(DurationMs(2 * *Timer::kMaxTimerDuration));
EXPECT_EQ(t1->duration(), Timer::kMaxTimerDuration);
}
TEST_F(TimerTest, TimersHaveMaximumBackoffDuration) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(1000), TimerBackoffAlgorithm::kExponential));
t1->Start();
int max_exponent = static_cast<int>(log2(*Timer::kMaxTimerDuration / 1000));
for (int i = 0; i < max_exponent; ++i) {
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000 * (1 << i)));
}
// Reached the maximum duration.
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(Timer::kMaxTimerDuration);
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(Timer::kMaxTimerDuration);
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(Timer::kMaxTimerDuration);
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(Timer::kMaxTimerDuration);
}
TEST_F(TimerTest, TimerCanBeStartedFromWithinExpirationHandler) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(1000), TimerBackoffAlgorithm::kFixed));
t1->Start();
// Start a timer, but don't return any new duration in callback.
EXPECT_CALL(on_expired_, Call).WillOnce([&]() {
EXPECT_TRUE(t1->is_running());
t1->set_duration(DurationMs(5000));
t1->Start();
return absl::nullopt;
});
AdvanceTimeAndRunTimers(DurationMs(1000));
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4999));
// Start a timer, and return any new duration in callback.
EXPECT_CALL(on_expired_, Call).WillOnce([&]() {
EXPECT_TRUE(t1->is_running());
t1->set_duration(DurationMs(5000));
t1->Start();
return absl::make_optional(DurationMs(8000));
});
AdvanceTimeAndRunTimers(DurationMs(1));
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(7999));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1));
}
TEST_F(TimerTest, DurationStaysWithinMaxTimerBackOffDuration) {
std::unique_ptr<Timer> t1 = manager_.CreateTimer(
"t1", on_expired_.AsStdFunction(),
TimerOptions(DurationMs(1000), TimerBackoffAlgorithm::kExponential,
/*max_restarts=*/absl::nullopt, DurationMs(5000)));
t1->Start();
// Initial timeout, 1000 ms
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1000));
// Exponential backoff -> 2000 ms
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(1999));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1));
// Exponential backoff -> 4000 ms
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(3999));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1));
// Limited backoff -> 5000ms
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4999));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1));
// ... where it plateaus
EXPECT_CALL(on_expired_, Call).Times(0);
AdvanceTimeAndRunTimers(DurationMs(4999));
EXPECT_CALL(on_expired_, Call).Times(1);
AdvanceTimeAndRunTimers(DurationMs(1));
}
} // namespace
} // namespace dcsctp