blob: bf923ea4cae746bd42b1192c5dccde0efe691476 [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#ifndef NET_DCSCTP_TIMER_TIMER_H_
11#define NET_DCSCTP_TIMER_TIMER_H_
12
13#include <stdint.h>
14
Victor Boivie5d3bda52021-04-12 21:59:19 +020015#include <algorithm>
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020016#include <functional>
17#include <memory>
18#include <string>
19#include <unordered_map>
20#include <utility>
21
22#include "absl/strings/string_view.h"
23#include "absl/types/optional.h"
Victor Boivie5d3bda52021-04-12 21:59:19 +020024#include "net/dcsctp/public/strong_alias.h"
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020025#include "net/dcsctp/public/timeout.h"
26
27namespace dcsctp {
28
Victor Boivie5d3bda52021-04-12 21:59:19 +020029using TimerID = StrongAlias<class TimerIDTag, uint32_t>;
30using TimerGeneration = StrongAlias<class TimerGenerationTag, uint32_t>;
31
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020032enum class TimerBackoffAlgorithm {
33 // The base duration will be used for any restart.
34 kFixed,
35 // An exponential backoff is used for restarts, with a 2x multiplier, meaning
36 // that every restart will use a duration that is twice as long as the
37 // previous.
38 kExponential,
39};
40
41struct TimerOptions {
42 explicit TimerOptions(DurationMs duration)
43 : TimerOptions(duration, TimerBackoffAlgorithm::kExponential) {}
44 TimerOptions(DurationMs duration, TimerBackoffAlgorithm backoff_algorithm)
45 : TimerOptions(duration, backoff_algorithm, -1) {}
46 TimerOptions(DurationMs duration,
47 TimerBackoffAlgorithm backoff_algorithm,
48 int max_restarts)
49 : duration(duration),
50 backoff_algorithm(backoff_algorithm),
51 max_restarts(max_restarts) {}
52
53 // The initial timer duration. Can be overridden with `set_duration`.
54 const DurationMs duration;
55 // If the duration should be increased (using exponential backoff) when it is
56 // restarted. If not set, the same duration will be used.
57 const TimerBackoffAlgorithm backoff_algorithm;
58 // The maximum number of times that the timer will be automatically restarted.
59 const int max_restarts;
60};
61
62// A high-level timer (in contrast to the low-level `Timeout` class).
63//
64// Timers are started and can be stopped or restarted. When a timer expires,
65// the provided `on_expired` callback will be triggered. A timer is
66// automatically restarted, as long as the number of restarts is below the
67// configurable `max_restarts` parameter. The `is_running` property can be
68// queried to know if it's still running after having expired.
69//
70// When a timer is restarted, it will use a configurable `backoff_algorithm` to
71// possibly adjust the duration of the next expiry. It is also possible to
72// return a new base duration (which is the duration before it's adjusted by the
73// backoff algorithm).
74class Timer {
75 public:
Victor Boivie5d3bda52021-04-12 21:59:19 +020076 // The maximum timer duration - one day.
77 static constexpr DurationMs kMaxTimerDuration = DurationMs(24 * 3600 * 1000);
78
Victor Boivie6fa0cfa2021-03-30 22:54:41 +020079 // When expired, the timer handler can optionally return a new duration which
80 // will be set as `duration` and used as base duration when the timer is
81 // restarted and as input to the backoff algorithm.
82 using OnExpired = std::function<absl::optional<DurationMs>()>;
83
84 // TimerManager will have pointers to these instances, so they must not move.
85 Timer(const Timer&) = delete;
86 Timer& operator=(const Timer&) = delete;
87
88 ~Timer();
89
90 // Starts the timer if it's stopped or restarts the timer if it's already
91 // running. The `expiration_count` will be reset.
92 void Start();
93
94 // Stops the timer. This can also be called when the timer is already stopped.
95 // The `expiration_count` will be reset.
96 void Stop();
97
98 // Sets the base duration. The actual timer duration may be larger depending
99 // on the backoff algorithm.
Victor Boivie5d3bda52021-04-12 21:59:19 +0200100 void set_duration(DurationMs duration) {
101 duration_ = std::min(duration, kMaxTimerDuration);
102 }
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200103
104 // Retrieves the base duration. The actual timer duration may be larger
105 // depending on the backoff algorithm.
106 DurationMs duration() const { return duration_; }
107
108 // Returns the number of times the timer has expired.
109 int expiration_count() const { return expiration_count_; }
110
111 // Returns the timer's options.
112 const TimerOptions& options() const { return options_; }
113
114 // Returns the name of the timer.
115 absl::string_view name() const { return name_; }
116
117 // Indicates if this timer is currently running.
118 bool is_running() const { return is_running_; }
119
120 private:
121 friend class TimerManager;
122 using UnregisterHandler = std::function<void()>;
Victor Boivie5d3bda52021-04-12 21:59:19 +0200123 Timer(TimerID id,
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200124 absl::string_view name,
125 OnExpired on_expired,
126 UnregisterHandler unregister,
127 std::unique_ptr<Timeout> timeout,
128 const TimerOptions& options);
129
130 // Called by TimerManager. Will trigger the callback and increment
131 // `expiration_count`. The timer will automatically be restarted at the
132 // duration as decided by the backoff algorithm, unless the
133 // `TimerOptions::max_restarts` has been reached and then it will be stopped
134 // and `is_running()` will return false.
Victor Boivie5d3bda52021-04-12 21:59:19 +0200135 void Trigger(TimerGeneration generation);
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200136
Victor Boivie5d3bda52021-04-12 21:59:19 +0200137 const TimerID id_;
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200138 const std::string name_;
139 const TimerOptions options_;
140 const OnExpired on_expired_;
141 const UnregisterHandler unregister_handler_;
142 const std::unique_ptr<Timeout> timeout_;
143
144 DurationMs duration_;
145
Victor Boivie5d3bda52021-04-12 21:59:19 +0200146 // Increased on each start, and is matched on Trigger, to avoid races. And by
147 // race, meaning that a timeout - which may be evaluated/expired on a
148 // different thread while this thread has stopped that timer already. Note
149 // that the entire socket is not thread-safe, so `TimerManager::HandleTimeout`
150 // is never executed concurrently with any timer starting/stopping.
151 //
152 // This will wrap around after 4 billion timer restarts, and if it wraps
153 // around, it would just trigger _this_ timer in advance (but it's hard to
154 // restart it 4 billion times within its duration).
155 TimerGeneration generation_ = TimerGeneration(0);
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200156 bool is_running_ = false;
157 // Incremented each time time has expired and reset when stopped or restarted.
158 int expiration_count_ = 0;
159};
160
161// Creates and manages timers.
162class TimerManager {
163 public:
164 explicit TimerManager(
165 std::function<std::unique_ptr<Timeout>()> create_timeout)
166 : create_timeout_(std::move(create_timeout)) {}
167
168 // Creates a timer with name `name` that will expire (when started) after
169 // `options.duration` and call `on_expired`. There are more `options` that
170 // affects the behavior. Note that timers are created initially stopped.
171 std::unique_ptr<Timer> CreateTimer(absl::string_view name,
172 Timer::OnExpired on_expired,
173 const TimerOptions& options);
174
175 void HandleTimeout(TimeoutID timeout_id);
176
177 private:
178 const std::function<std::unique_ptr<Timeout>()> create_timeout_;
Victor Boivie5d3bda52021-04-12 21:59:19 +0200179 std::unordered_map<TimerID, Timer*, TimerID::Hasher> timers_;
180 TimerID next_id_ = TimerID(0);
Victor Boivie6fa0cfa2021-03-30 22:54:41 +0200181};
182
183} // namespace dcsctp
184
185#endif // NET_DCSCTP_TIMER_TIMER_H_