mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00

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}
427 lines
13 KiB
C++
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
|