diff options
Diffstat (limited to 'net/dcsctp/timer/timer.cc')
-rw-r--r-- | net/dcsctp/timer/timer.cc | 72 |
1 files changed, 51 insertions, 21 deletions
diff --git a/net/dcsctp/timer/timer.cc b/net/dcsctp/timer/timer.cc index 2376e7aecb..593d639fa7 100644 --- a/net/dcsctp/timer/timer.cc +++ b/net/dcsctp/timer/timer.cc @@ -9,7 +9,9 @@ */ #include "net/dcsctp/timer/timer.h" +#include <algorithm> #include <cstdint> +#include <limits> #include <memory> #include <unordered_map> #include <utility> @@ -17,11 +19,12 @@ #include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "net/dcsctp/public/timeout.h" +#include "rtc_base/checks.h" namespace dcsctp { namespace { -TimeoutID MakeTimeoutId(uint32_t timer_id, uint32_t generation) { - return TimeoutID(static_cast<uint64_t>(timer_id) << 32 | generation); +TimeoutID MakeTimeoutId(TimerID timer_id, TimerGeneration generation) { + return TimeoutID(static_cast<uint64_t>(*timer_id) << 32 | *generation); } DurationMs GetBackoffDuration(TimerBackoffAlgorithm algorithm, @@ -30,13 +33,23 @@ DurationMs GetBackoffDuration(TimerBackoffAlgorithm algorithm, switch (algorithm) { case TimerBackoffAlgorithm::kFixed: return base_duration; - case TimerBackoffAlgorithm::kExponential: - return DurationMs(*base_duration * (1 << expiration_count)); + case TimerBackoffAlgorithm::kExponential: { + int32_t duration_ms = *base_duration; + + while (expiration_count > 0 && duration_ms < *Timer::kMaxTimerDuration) { + duration_ms *= 2; + --expiration_count; + } + + return DurationMs(std::min(duration_ms, *Timer::kMaxTimerDuration)); + } } } } // namespace -Timer::Timer(uint32_t id, +constexpr DurationMs Timer::kMaxTimerDuration; + +Timer::Timer(TimerID id, absl::string_view name, OnExpired on_expired, UnregisterHandler unregister_handler, @@ -59,11 +72,13 @@ void Timer::Start() { expiration_count_ = 0; if (!is_running()) { is_running_ = true; - timeout_->Start(duration_, MakeTimeoutId(id_, ++generation_)); + generation_ = TimerGeneration(*generation_ + 1); + timeout_->Start(duration_, MakeTimeoutId(id_, generation_)); } else { // Timer was running - stop and restart it, to make it expire in `duration_` // from now. - timeout_->Restart(duration_, MakeTimeoutId(id_, ++generation_)); + generation_ = TimerGeneration(*generation_ + 1); + timeout_->Restart(duration_, MakeTimeoutId(id_, generation_)); } } @@ -75,31 +90,41 @@ void Timer::Stop() { } } -void Timer::Trigger(uint32_t generation) { +void Timer::Trigger(TimerGeneration generation) { if (is_running_ && generation == generation_) { ++expiration_count_; - if (options_.max_restarts >= 0 && - expiration_count_ > options_.max_restarts) { - is_running_ = false; + is_running_ = false; + if (options_.max_restarts < 0 || + expiration_count_ <= options_.max_restarts) { + // The timer should still be running after this triggers. Start a new + // timer. Note that it might be very quickly restarted again, if the + // `on_expired_` callback returns a new duration. + is_running_ = true; + DurationMs duration = GetBackoffDuration(options_.backoff_algorithm, + duration_, expiration_count_); + generation_ = TimerGeneration(*generation_ + 1); + timeout_->Start(duration, MakeTimeoutId(id_, generation_)); } absl::optional<DurationMs> new_duration = on_expired_(); - if (new_duration.has_value()) { + if (new_duration.has_value() && new_duration != duration_) { duration_ = new_duration.value(); - } + if (is_running_) { + // Restart it with new duration. + timeout_->Stop(); - if (is_running_) { - // Restart it with new duration. - DurationMs duration = GetBackoffDuration(options_.backoff_algorithm, - duration_, expiration_count_); - timeout_->Start(duration, MakeTimeoutId(id_, ++generation_)); + DurationMs duration = GetBackoffDuration(options_.backoff_algorithm, + duration_, expiration_count_); + generation_ = TimerGeneration(*generation_ + 1); + timeout_->Start(duration, MakeTimeoutId(id_, generation_)); + } } } } void TimerManager::HandleTimeout(TimeoutID timeout_id) { - uint32_t timer_id = *timeout_id >> 32; - uint32_t generation = *timeout_id; + TimerID timer_id(*timeout_id >> 32); + TimerGeneration generation(*timeout_id); auto it = timers_.find(timer_id); if (it != timers_.end()) { it->second->Trigger(generation); @@ -109,7 +134,12 @@ void TimerManager::HandleTimeout(TimeoutID timeout_id) { std::unique_ptr<Timer> TimerManager::CreateTimer(absl::string_view name, Timer::OnExpired on_expired, const TimerOptions& options) { - uint32_t id = ++next_id_; + next_id_ = TimerID(*next_id_ + 1); + TimerID id = next_id_; + // This would overflow after 4 billion timers created, which in SCTP would be + // after 800 million reconnections on a single socket. Ensure this will never + // happen. + RTC_CHECK_NE(*id, std::numeric_limits<uint32_t>::max()); auto timer = absl::WrapUnique(new Timer( id, name, std::move(on_expired), [this, id]() { timers_.erase(id); }, create_timeout_(), options)); |