blob: 593d639fa7e175d561f1771273472afdf0b4d69a [file] [log] [blame]
Victor Boivie6fa0cfa2021-03-30 22:54:41 +02001/*
2 * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10#include "net/dcsctp/timer/timer.h"
11
Victor Boivie5d3bda52021-04-12 21:59:19 +020012#include <algorithm>
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020013#include <cstdint>
Victor Boivie5d3bda52021-04-12 21:59:19 +020014#include <limits>
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020015#include <memory>
16#include <unordered_map>
17#include <utility>
18
19#include "absl/memory/memory.h"
20#include "absl/strings/string_view.h"
21#include "net/dcsctp/public/timeout.h"
Victor Boivie5d3bda52021-04-12 21:59:19 +020022#include "rtc_base/checks.h"
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020023
24namespace dcsctp {
25namespace {
Victor Boivie5d3bda52021-04-12 21:59:19 +020026TimeoutID MakeTimeoutId(TimerID timer_id, TimerGeneration generation) {
27 return TimeoutID(static_cast<uint64_t>(*timer_id) << 32 | *generation);
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020028}
29
30DurationMs GetBackoffDuration(TimerBackoffAlgorithm algorithm,
31 DurationMs base_duration,
32 int expiration_count) {
33 switch (algorithm) {
34 case TimerBackoffAlgorithm::kFixed:
35 return base_duration;
Victor Boivie5d3bda52021-04-12 21:59:19 +020036 case TimerBackoffAlgorithm::kExponential: {
37 int32_t duration_ms = *base_duration;
38
39 while (expiration_count > 0 && duration_ms < *Timer::kMaxTimerDuration) {
40 duration_ms *= 2;
41 --expiration_count;
42 }
43
44 return DurationMs(std::min(duration_ms, *Timer::kMaxTimerDuration));
45 }
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020046 }
47}
48} // namespace
49
Victor Boivie5d3bda52021-04-12 21:59:19 +020050constexpr DurationMs Timer::kMaxTimerDuration;
51
52Timer::Timer(TimerID id,
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020053 absl::string_view name,
54 OnExpired on_expired,
55 UnregisterHandler unregister_handler,
56 std::unique_ptr<Timeout> timeout,
57 const TimerOptions& options)
58 : id_(id),
59 name_(name),
60 options_(options),
61 on_expired_(std::move(on_expired)),
62 unregister_handler_(std::move(unregister_handler)),
63 timeout_(std::move(timeout)),
64 duration_(options.duration) {}
65
66Timer::~Timer() {
67 Stop();
68 unregister_handler_();
69}
70
71void Timer::Start() {
72 expiration_count_ = 0;
73 if (!is_running()) {
74 is_running_ = true;
Victor Boivie5d3bda52021-04-12 21:59:19 +020075 generation_ = TimerGeneration(*generation_ + 1);
76 timeout_->Start(duration_, MakeTimeoutId(id_, generation_));
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020077 } else {
78 // Timer was running - stop and restart it, to make it expire in `duration_`
79 // from now.
Victor Boivie5d3bda52021-04-12 21:59:19 +020080 generation_ = TimerGeneration(*generation_ + 1);
81 timeout_->Restart(duration_, MakeTimeoutId(id_, generation_));
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020082 }
83}
84
85void Timer::Stop() {
86 if (is_running()) {
87 timeout_->Stop();
88 expiration_count_ = 0;
89 is_running_ = false;
90 }
91}
92
Victor Boivie5d3bda52021-04-12 21:59:19 +020093void Timer::Trigger(TimerGeneration generation) {
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020094 if (is_running_ && generation == generation_) {
95 ++expiration_count_;
Victor Boiviebb7ee952021-05-05 12:36:52 +020096 is_running_ = false;
97 if (options_.max_restarts < 0 ||
98 expiration_count_ <= options_.max_restarts) {
99 // The timer should still be running after this triggers. Start a new
100 // timer. Note that it might be very quickly restarted again, if the
101 // `on_expired_` callback returns a new duration.
102 is_running_ = true;
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200103 DurationMs duration = GetBackoffDuration(options_.backoff_algorithm,
104 duration_, expiration_count_);
Victor Boivie5d3bda52021-04-12 21:59:19 +0200105 generation_ = TimerGeneration(*generation_ + 1);
106 timeout_->Start(duration, MakeTimeoutId(id_, generation_));
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200107 }
Victor Boiviebb7ee952021-05-05 12:36:52 +0200108
109 absl::optional<DurationMs> new_duration = on_expired_();
110 if (new_duration.has_value() && new_duration != duration_) {
111 duration_ = new_duration.value();
112 if (is_running_) {
113 // Restart it with new duration.
114 timeout_->Stop();
115
116 DurationMs duration = GetBackoffDuration(options_.backoff_algorithm,
117 duration_, expiration_count_);
118 generation_ = TimerGeneration(*generation_ + 1);
119 timeout_->Start(duration, MakeTimeoutId(id_, generation_));
120 }
121 }
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200122 }
123}
124
125void TimerManager::HandleTimeout(TimeoutID timeout_id) {
Victor Boivie5d3bda52021-04-12 21:59:19 +0200126 TimerID timer_id(*timeout_id >> 32);
127 TimerGeneration generation(*timeout_id);
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200128 auto it = timers_.find(timer_id);
129 if (it != timers_.end()) {
130 it->second->Trigger(generation);
131 }
132}
133
134std::unique_ptr<Timer> TimerManager::CreateTimer(absl::string_view name,
135 Timer::OnExpired on_expired,
136 const TimerOptions& options) {
Victor Boivie5d3bda52021-04-12 21:59:19 +0200137 next_id_ = TimerID(*next_id_ + 1);
138 TimerID id = next_id_;
139 // This would overflow after 4 billion timers created, which in SCTP would be
140 // after 800 million reconnections on a single socket. Ensure this will never
141 // happen.
142 RTC_CHECK_NE(*id, std::numeric_limits<uint32_t>::max());
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200143 auto timer = absl::WrapUnique(new Timer(
144 id, name, std::move(on_expired), [this, id]() { timers_.erase(id); },
145 create_timeout_(), options));
146 timers_[id] = timer.get();
147 return timer;
148}
149
150} // namespace dcsctp