aboutsummaryrefslogtreecommitdiff
path: root/net/dcsctp/timer/timer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/dcsctp/timer/timer.cc')
-rw-r--r--net/dcsctp/timer/timer.cc72
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));