blob: 2376e7aecbb70ae0273356c542c0d345d5b9eca9 [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
12#include <cstdint>
13#include <memory>
14#include <unordered_map>
15#include <utility>
16
17#include "absl/memory/memory.h"
18#include "absl/strings/string_view.h"
19#include "net/dcsctp/public/timeout.h"
20
21namespace dcsctp {
22namespace {
23TimeoutID MakeTimeoutId(uint32_t timer_id, uint32_t generation) {
24 return TimeoutID(static_cast<uint64_t>(timer_id) << 32 | generation);
25}
26
27DurationMs GetBackoffDuration(TimerBackoffAlgorithm algorithm,
28 DurationMs base_duration,
29 int expiration_count) {
30 switch (algorithm) {
31 case TimerBackoffAlgorithm::kFixed:
32 return base_duration;
33 case TimerBackoffAlgorithm::kExponential:
34 return DurationMs(*base_duration * (1 << expiration_count));
35 }
36}
37} // namespace
38
39Timer::Timer(uint32_t id,
40 absl::string_view name,
41 OnExpired on_expired,
42 UnregisterHandler unregister_handler,
43 std::unique_ptr<Timeout> timeout,
44 const TimerOptions& options)
45 : id_(id),
46 name_(name),
47 options_(options),
48 on_expired_(std::move(on_expired)),
49 unregister_handler_(std::move(unregister_handler)),
50 timeout_(std::move(timeout)),
51 duration_(options.duration) {}
52
53Timer::~Timer() {
54 Stop();
55 unregister_handler_();
56}
57
58void Timer::Start() {
59 expiration_count_ = 0;
60 if (!is_running()) {
61 is_running_ = true;
62 timeout_->Start(duration_, MakeTimeoutId(id_, ++generation_));
63 } else {
64 // Timer was running - stop and restart it, to make it expire in `duration_`
65 // from now.
66 timeout_->Restart(duration_, MakeTimeoutId(id_, ++generation_));
67 }
68}
69
70void Timer::Stop() {
71 if (is_running()) {
72 timeout_->Stop();
73 expiration_count_ = 0;
74 is_running_ = false;
75 }
76}
77
78void Timer::Trigger(uint32_t generation) {
79 if (is_running_ && generation == generation_) {
80 ++expiration_count_;
81 if (options_.max_restarts >= 0 &&
82 expiration_count_ > options_.max_restarts) {
83 is_running_ = false;
84 }
85
86 absl::optional<DurationMs> new_duration = on_expired_();
87 if (new_duration.has_value()) {
88 duration_ = new_duration.value();
89 }
90
91 if (is_running_) {
92 // Restart it with new duration.
93 DurationMs duration = GetBackoffDuration(options_.backoff_algorithm,
94 duration_, expiration_count_);
95 timeout_->Start(duration, MakeTimeoutId(id_, ++generation_));
96 }
97 }
98}
99
100void TimerManager::HandleTimeout(TimeoutID timeout_id) {
101 uint32_t timer_id = *timeout_id >> 32;
102 uint32_t generation = *timeout_id;
103 auto it = timers_.find(timer_id);
104 if (it != timers_.end()) {
105 it->second->Trigger(generation);
106 }
107}
108
109std::unique_ptr<Timer> TimerManager::CreateTimer(absl::string_view name,
110 Timer::OnExpired on_expired,
111 const TimerOptions& options) {
112 uint32_t id = ++next_id_;
113 auto timer = absl::WrapUnique(new Timer(
114 id, name, std::move(on_expired), [this, id]() { timers_.erase(id); },
115 create_timeout_(), options));
116 timers_[id] = timer.get();
117 return timer;
118}
119
120} // namespace dcsctp