blob: 2ad6d749714b3490b1d28dcc411b5967f79b45ff [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>
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020016#include <utility>
17
18#include "absl/memory/memory.h"
19#include "absl/strings/string_view.h"
20#include "net/dcsctp/public/timeout.h"
Victor Boivie5d3bda52021-04-12 21:59:19 +020021#include "rtc_base/checks.h"
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020022
23namespace dcsctp {
24namespace {
Victor Boivie5d3bda52021-04-12 21:59:19 +020025TimeoutID MakeTimeoutId(TimerID timer_id, TimerGeneration generation) {
26 return TimeoutID(static_cast<uint64_t>(*timer_id) << 32 | *generation);
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020027}
28
29DurationMs GetBackoffDuration(TimerBackoffAlgorithm algorithm,
30 DurationMs base_duration,
31 int expiration_count) {
32 switch (algorithm) {
33 case TimerBackoffAlgorithm::kFixed:
34 return base_duration;
Victor Boivie5d3bda52021-04-12 21:59:19 +020035 case TimerBackoffAlgorithm::kExponential: {
36 int32_t duration_ms = *base_duration;
37
38 while (expiration_count > 0 && duration_ms < *Timer::kMaxTimerDuration) {
39 duration_ms *= 2;
40 --expiration_count;
41 }
42
43 return DurationMs(std::min(duration_ms, *Timer::kMaxTimerDuration));
44 }
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020045 }
46}
47} // namespace
48
Victor Boivie5d3bda52021-04-12 21:59:19 +020049constexpr DurationMs Timer::kMaxTimerDuration;
50
51Timer::Timer(TimerID id,
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020052 absl::string_view name,
53 OnExpired on_expired,
54 UnregisterHandler unregister_handler,
55 std::unique_ptr<Timeout> timeout,
56 const TimerOptions& options)
57 : id_(id),
58 name_(name),
59 options_(options),
60 on_expired_(std::move(on_expired)),
61 unregister_handler_(std::move(unregister_handler)),
62 timeout_(std::move(timeout)),
63 duration_(options.duration) {}
64
65Timer::~Timer() {
66 Stop();
67 unregister_handler_();
68}
69
70void Timer::Start() {
71 expiration_count_ = 0;
72 if (!is_running()) {
73 is_running_ = true;
Victor Boivie5d3bda52021-04-12 21:59:19 +020074 generation_ = TimerGeneration(*generation_ + 1);
75 timeout_->Start(duration_, MakeTimeoutId(id_, generation_));
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020076 } else {
77 // Timer was running - stop and restart it, to make it expire in `duration_`
78 // from now.
Victor Boivie5d3bda52021-04-12 21:59:19 +020079 generation_ = TimerGeneration(*generation_ + 1);
80 timeout_->Restart(duration_, MakeTimeoutId(id_, generation_));
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020081 }
82}
83
84void Timer::Stop() {
85 if (is_running()) {
86 timeout_->Stop();
87 expiration_count_ = 0;
88 is_running_ = false;
89 }
90}
91
Victor Boivie5d3bda52021-04-12 21:59:19 +020092void Timer::Trigger(TimerGeneration generation) {
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020093 if (is_running_ && generation == generation_) {
94 ++expiration_count_;
Victor Boiviebb7ee952021-05-05 12:36:52 +020095 is_running_ = false;
Victor Boivie9680d292021-08-30 10:23:49 +020096 if (!options_.max_restarts.has_value() ||
97 expiration_count_ <= *options_.max_restarts) {
Victor Boiviebb7ee952021-05-05 12:36:52 +020098 // The timer should still be running after this triggers. Start a new
99 // timer. Note that it might be very quickly restarted again, if the
100 // `on_expired_` callback returns a new duration.
101 is_running_ = true;
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200102 DurationMs duration = GetBackoffDuration(options_.backoff_algorithm,
103 duration_, expiration_count_);
Victor Boivie5d3bda52021-04-12 21:59:19 +0200104 generation_ = TimerGeneration(*generation_ + 1);
105 timeout_->Start(duration, MakeTimeoutId(id_, generation_));
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200106 }
Victor Boiviebb7ee952021-05-05 12:36:52 +0200107
108 absl::optional<DurationMs> new_duration = on_expired_();
109 if (new_duration.has_value() && new_duration != duration_) {
110 duration_ = new_duration.value();
111 if (is_running_) {
112 // Restart it with new duration.
113 timeout_->Stop();
114
115 DurationMs duration = GetBackoffDuration(options_.backoff_algorithm,
116 duration_, expiration_count_);
117 generation_ = TimerGeneration(*generation_ + 1);
118 timeout_->Start(duration, MakeTimeoutId(id_, generation_));
119 }
120 }
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200121 }
122}
123
124void TimerManager::HandleTimeout(TimeoutID timeout_id) {
Victor Boivie5d3bda52021-04-12 21:59:19 +0200125 TimerID timer_id(*timeout_id >> 32);
126 TimerGeneration generation(*timeout_id);
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200127 auto it = timers_.find(timer_id);
128 if (it != timers_.end()) {
129 it->second->Trigger(generation);
130 }
131}
132
133std::unique_ptr<Timer> TimerManager::CreateTimer(absl::string_view name,
134 Timer::OnExpired on_expired,
135 const TimerOptions& options) {
Victor Boivie5d3bda52021-04-12 21:59:19 +0200136 next_id_ = TimerID(*next_id_ + 1);
137 TimerID id = next_id_;
138 // This would overflow after 4 billion timers created, which in SCTP would be
139 // after 800 million reconnections on a single socket. Ensure this will never
140 // happen.
141 RTC_CHECK_NE(*id, std::numeric_limits<uint32_t>::max());
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200142 auto timer = absl::WrapUnique(new Timer(
143 id, name, std::move(on_expired), [this, id]() { timers_.erase(id); },
144 create_timeout_(), options));
145 timers_[id] = timer.get();
146 return timer;
147}
148
149} // namespace dcsctp