xref: /aosp_15_r20/external/webrtc/net/dcsctp/timer/timer.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker #ifndef NET_DCSCTP_TIMER_TIMER_H_
11*d9f75844SAndroid Build Coastguard Worker #define NET_DCSCTP_TIMER_TIMER_H_
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <stdint.h>
14*d9f75844SAndroid Build Coastguard Worker 
15*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
16*d9f75844SAndroid Build Coastguard Worker #include <functional>
17*d9f75844SAndroid Build Coastguard Worker #include <map>
18*d9f75844SAndroid Build Coastguard Worker #include <memory>
19*d9f75844SAndroid Build Coastguard Worker #include <string>
20*d9f75844SAndroid Build Coastguard Worker #include <utility>
21*d9f75844SAndroid Build Coastguard Worker 
22*d9f75844SAndroid Build Coastguard Worker #include "absl/strings/string_view.h"
23*d9f75844SAndroid Build Coastguard Worker #include "absl/types/optional.h"
24*d9f75844SAndroid Build Coastguard Worker #include "api/task_queue/task_queue_base.h"
25*d9f75844SAndroid Build Coastguard Worker #include "net/dcsctp/public/timeout.h"
26*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/strong_alias.h"
27*d9f75844SAndroid Build Coastguard Worker 
28*d9f75844SAndroid Build Coastguard Worker namespace dcsctp {
29*d9f75844SAndroid Build Coastguard Worker 
30*d9f75844SAndroid Build Coastguard Worker using TimerID = webrtc::StrongAlias<class TimerIDTag, uint32_t>;
31*d9f75844SAndroid Build Coastguard Worker using TimerGeneration = webrtc::StrongAlias<class TimerGenerationTag, uint32_t>;
32*d9f75844SAndroid Build Coastguard Worker 
33*d9f75844SAndroid Build Coastguard Worker enum class TimerBackoffAlgorithm {
34*d9f75844SAndroid Build Coastguard Worker   // The base duration will be used for any restart.
35*d9f75844SAndroid Build Coastguard Worker   kFixed,
36*d9f75844SAndroid Build Coastguard Worker   // An exponential backoff is used for restarts, with a 2x multiplier, meaning
37*d9f75844SAndroid Build Coastguard Worker   // that every restart will use a duration that is twice as long as the
38*d9f75844SAndroid Build Coastguard Worker   // previous.
39*d9f75844SAndroid Build Coastguard Worker   kExponential,
40*d9f75844SAndroid Build Coastguard Worker };
41*d9f75844SAndroid Build Coastguard Worker 
42*d9f75844SAndroid Build Coastguard Worker struct TimerOptions {
TimerOptionsTimerOptions43*d9f75844SAndroid Build Coastguard Worker   explicit TimerOptions(DurationMs duration)
44*d9f75844SAndroid Build Coastguard Worker       : TimerOptions(duration, TimerBackoffAlgorithm::kExponential) {}
TimerOptionsTimerOptions45*d9f75844SAndroid Build Coastguard Worker   TimerOptions(DurationMs duration, TimerBackoffAlgorithm backoff_algorithm)
46*d9f75844SAndroid Build Coastguard Worker       : TimerOptions(duration, backoff_algorithm, absl::nullopt) {}
TimerOptionsTimerOptions47*d9f75844SAndroid Build Coastguard Worker   TimerOptions(DurationMs duration,
48*d9f75844SAndroid Build Coastguard Worker                TimerBackoffAlgorithm backoff_algorithm,
49*d9f75844SAndroid Build Coastguard Worker                absl::optional<int> max_restarts)
50*d9f75844SAndroid Build Coastguard Worker       : TimerOptions(duration, backoff_algorithm, max_restarts, absl::nullopt) {
51*d9f75844SAndroid Build Coastguard Worker   }
TimerOptionsTimerOptions52*d9f75844SAndroid Build Coastguard Worker   TimerOptions(DurationMs duration,
53*d9f75844SAndroid Build Coastguard Worker                TimerBackoffAlgorithm backoff_algorithm,
54*d9f75844SAndroid Build Coastguard Worker                absl::optional<int> max_restarts,
55*d9f75844SAndroid Build Coastguard Worker                absl::optional<DurationMs> max_backoff_duration)
56*d9f75844SAndroid Build Coastguard Worker       : TimerOptions(duration,
57*d9f75844SAndroid Build Coastguard Worker                      backoff_algorithm,
58*d9f75844SAndroid Build Coastguard Worker                      max_restarts,
59*d9f75844SAndroid Build Coastguard Worker                      max_backoff_duration,
60*d9f75844SAndroid Build Coastguard Worker                      webrtc::TaskQueueBase::DelayPrecision::kLow) {}
TimerOptionsTimerOptions61*d9f75844SAndroid Build Coastguard Worker   TimerOptions(DurationMs duration,
62*d9f75844SAndroid Build Coastguard Worker                TimerBackoffAlgorithm backoff_algorithm,
63*d9f75844SAndroid Build Coastguard Worker                absl::optional<int> max_restarts,
64*d9f75844SAndroid Build Coastguard Worker                absl::optional<DurationMs> max_backoff_duration,
65*d9f75844SAndroid Build Coastguard Worker                webrtc::TaskQueueBase::DelayPrecision precision)
66*d9f75844SAndroid Build Coastguard Worker       : duration(duration),
67*d9f75844SAndroid Build Coastguard Worker         backoff_algorithm(backoff_algorithm),
68*d9f75844SAndroid Build Coastguard Worker         max_restarts(max_restarts),
69*d9f75844SAndroid Build Coastguard Worker         max_backoff_duration(max_backoff_duration),
70*d9f75844SAndroid Build Coastguard Worker         precision(precision) {}
71*d9f75844SAndroid Build Coastguard Worker 
72*d9f75844SAndroid Build Coastguard Worker   // The initial timer duration. Can be overridden with `set_duration`.
73*d9f75844SAndroid Build Coastguard Worker   const DurationMs duration;
74*d9f75844SAndroid Build Coastguard Worker   // If the duration should be increased (using exponential backoff) when it is
75*d9f75844SAndroid Build Coastguard Worker   // restarted. If not set, the same duration will be used.
76*d9f75844SAndroid Build Coastguard Worker   const TimerBackoffAlgorithm backoff_algorithm;
77*d9f75844SAndroid Build Coastguard Worker   // The maximum number of times that the timer will be automatically restarted,
78*d9f75844SAndroid Build Coastguard Worker   // or absl::nullopt if there is no limit.
79*d9f75844SAndroid Build Coastguard Worker   const absl::optional<int> max_restarts;
80*d9f75844SAndroid Build Coastguard Worker   // The maximum timeout value for exponential backoff.
81*d9f75844SAndroid Build Coastguard Worker   const absl::optional<DurationMs> max_backoff_duration;
82*d9f75844SAndroid Build Coastguard Worker   // The precision of the webrtc::TaskQueueBase used for scheduling.
83*d9f75844SAndroid Build Coastguard Worker   const webrtc::TaskQueueBase::DelayPrecision precision;
84*d9f75844SAndroid Build Coastguard Worker };
85*d9f75844SAndroid Build Coastguard Worker 
86*d9f75844SAndroid Build Coastguard Worker // A high-level timer (in contrast to the low-level `Timeout` class).
87*d9f75844SAndroid Build Coastguard Worker //
88*d9f75844SAndroid Build Coastguard Worker // Timers are started and can be stopped or restarted. When a timer expires,
89*d9f75844SAndroid Build Coastguard Worker // the provided `on_expired` callback will be triggered. A timer is
90*d9f75844SAndroid Build Coastguard Worker // automatically restarted, as long as the number of restarts is below the
91*d9f75844SAndroid Build Coastguard Worker // configurable `max_restarts` parameter. The `is_running` property can be
92*d9f75844SAndroid Build Coastguard Worker // queried to know if it's still running after having expired.
93*d9f75844SAndroid Build Coastguard Worker //
94*d9f75844SAndroid Build Coastguard Worker // When a timer is restarted, it will use a configurable `backoff_algorithm` to
95*d9f75844SAndroid Build Coastguard Worker // possibly adjust the duration of the next expiry. It is also possible to
96*d9f75844SAndroid Build Coastguard Worker // return a new base duration (which is the duration before it's adjusted by the
97*d9f75844SAndroid Build Coastguard Worker // backoff algorithm).
98*d9f75844SAndroid Build Coastguard Worker class Timer {
99*d9f75844SAndroid Build Coastguard Worker  public:
100*d9f75844SAndroid Build Coastguard Worker   // The maximum timer duration - one day.
101*d9f75844SAndroid Build Coastguard Worker   static constexpr DurationMs kMaxTimerDuration = DurationMs(24 * 3600 * 1000);
102*d9f75844SAndroid Build Coastguard Worker 
103*d9f75844SAndroid Build Coastguard Worker   // When expired, the timer handler can optionally return a new duration which
104*d9f75844SAndroid Build Coastguard Worker   // will be set as `duration` and used as base duration when the timer is
105*d9f75844SAndroid Build Coastguard Worker   // restarted and as input to the backoff algorithm.
106*d9f75844SAndroid Build Coastguard Worker   using OnExpired = std::function<absl::optional<DurationMs>()>;
107*d9f75844SAndroid Build Coastguard Worker 
108*d9f75844SAndroid Build Coastguard Worker   // TimerManager will have pointers to these instances, so they must not move.
109*d9f75844SAndroid Build Coastguard Worker   Timer(const Timer&) = delete;
110*d9f75844SAndroid Build Coastguard Worker   Timer& operator=(const Timer&) = delete;
111*d9f75844SAndroid Build Coastguard Worker 
112*d9f75844SAndroid Build Coastguard Worker   ~Timer();
113*d9f75844SAndroid Build Coastguard Worker 
114*d9f75844SAndroid Build Coastguard Worker   // Starts the timer if it's stopped or restarts the timer if it's already
115*d9f75844SAndroid Build Coastguard Worker   // running. The `expiration_count` will be reset.
116*d9f75844SAndroid Build Coastguard Worker   void Start();
117*d9f75844SAndroid Build Coastguard Worker 
118*d9f75844SAndroid Build Coastguard Worker   // Stops the timer. This can also be called when the timer is already stopped.
119*d9f75844SAndroid Build Coastguard Worker   // The `expiration_count` will be reset.
120*d9f75844SAndroid Build Coastguard Worker   void Stop();
121*d9f75844SAndroid Build Coastguard Worker 
122*d9f75844SAndroid Build Coastguard Worker   // Sets the base duration. The actual timer duration may be larger depending
123*d9f75844SAndroid Build Coastguard Worker   // on the backoff algorithm.
set_duration(DurationMs duration)124*d9f75844SAndroid Build Coastguard Worker   void set_duration(DurationMs duration) {
125*d9f75844SAndroid Build Coastguard Worker     duration_ = std::min(duration, kMaxTimerDuration);
126*d9f75844SAndroid Build Coastguard Worker   }
127*d9f75844SAndroid Build Coastguard Worker 
128*d9f75844SAndroid Build Coastguard Worker   // Retrieves the base duration. The actual timer duration may be larger
129*d9f75844SAndroid Build Coastguard Worker   // depending on the backoff algorithm.
duration()130*d9f75844SAndroid Build Coastguard Worker   DurationMs duration() const { return duration_; }
131*d9f75844SAndroid Build Coastguard Worker 
132*d9f75844SAndroid Build Coastguard Worker   // Returns the number of times the timer has expired.
expiration_count()133*d9f75844SAndroid Build Coastguard Worker   int expiration_count() const { return expiration_count_; }
134*d9f75844SAndroid Build Coastguard Worker 
135*d9f75844SAndroid Build Coastguard Worker   // Returns the timer's options.
options()136*d9f75844SAndroid Build Coastguard Worker   const TimerOptions& options() const { return options_; }
137*d9f75844SAndroid Build Coastguard Worker 
138*d9f75844SAndroid Build Coastguard Worker   // Returns the name of the timer.
name()139*d9f75844SAndroid Build Coastguard Worker   absl::string_view name() const { return name_; }
140*d9f75844SAndroid Build Coastguard Worker 
141*d9f75844SAndroid Build Coastguard Worker   // Indicates if this timer is currently running.
is_running()142*d9f75844SAndroid Build Coastguard Worker   bool is_running() const { return is_running_; }
143*d9f75844SAndroid Build Coastguard Worker 
144*d9f75844SAndroid Build Coastguard Worker  private:
145*d9f75844SAndroid Build Coastguard Worker   friend class TimerManager;
146*d9f75844SAndroid Build Coastguard Worker   using UnregisterHandler = std::function<void()>;
147*d9f75844SAndroid Build Coastguard Worker   Timer(TimerID id,
148*d9f75844SAndroid Build Coastguard Worker         absl::string_view name,
149*d9f75844SAndroid Build Coastguard Worker         OnExpired on_expired,
150*d9f75844SAndroid Build Coastguard Worker         UnregisterHandler unregister,
151*d9f75844SAndroid Build Coastguard Worker         std::unique_ptr<Timeout> timeout,
152*d9f75844SAndroid Build Coastguard Worker         const TimerOptions& options);
153*d9f75844SAndroid Build Coastguard Worker 
154*d9f75844SAndroid Build Coastguard Worker   // Called by TimerManager. Will trigger the callback and increment
155*d9f75844SAndroid Build Coastguard Worker   // `expiration_count`. The timer will automatically be restarted at the
156*d9f75844SAndroid Build Coastguard Worker   // duration as decided by the backoff algorithm, unless the
157*d9f75844SAndroid Build Coastguard Worker   // `TimerOptions::max_restarts` has been reached and then it will be stopped
158*d9f75844SAndroid Build Coastguard Worker   // and `is_running()` will return false.
159*d9f75844SAndroid Build Coastguard Worker   void Trigger(TimerGeneration generation);
160*d9f75844SAndroid Build Coastguard Worker 
161*d9f75844SAndroid Build Coastguard Worker   const TimerID id_;
162*d9f75844SAndroid Build Coastguard Worker   const std::string name_;
163*d9f75844SAndroid Build Coastguard Worker   const TimerOptions options_;
164*d9f75844SAndroid Build Coastguard Worker   const OnExpired on_expired_;
165*d9f75844SAndroid Build Coastguard Worker   const UnregisterHandler unregister_handler_;
166*d9f75844SAndroid Build Coastguard Worker   const std::unique_ptr<Timeout> timeout_;
167*d9f75844SAndroid Build Coastguard Worker 
168*d9f75844SAndroid Build Coastguard Worker   DurationMs duration_;
169*d9f75844SAndroid Build Coastguard Worker 
170*d9f75844SAndroid Build Coastguard Worker   // Increased on each start, and is matched on Trigger, to avoid races. And by
171*d9f75844SAndroid Build Coastguard Worker   // race, meaning that a timeout - which may be evaluated/expired on a
172*d9f75844SAndroid Build Coastguard Worker   // different thread while this thread has stopped that timer already. Note
173*d9f75844SAndroid Build Coastguard Worker   // that the entire socket is not thread-safe, so `TimerManager::HandleTimeout`
174*d9f75844SAndroid Build Coastguard Worker   // is never executed concurrently with any timer starting/stopping.
175*d9f75844SAndroid Build Coastguard Worker   //
176*d9f75844SAndroid Build Coastguard Worker   // This will wrap around after 4 billion timer restarts, and if it wraps
177*d9f75844SAndroid Build Coastguard Worker   // around, it would just trigger _this_ timer in advance (but it's hard to
178*d9f75844SAndroid Build Coastguard Worker   // restart it 4 billion times within its duration).
179*d9f75844SAndroid Build Coastguard Worker   TimerGeneration generation_ = TimerGeneration(0);
180*d9f75844SAndroid Build Coastguard Worker   bool is_running_ = false;
181*d9f75844SAndroid Build Coastguard Worker   // Incremented each time time has expired and reset when stopped or restarted.
182*d9f75844SAndroid Build Coastguard Worker   int expiration_count_ = 0;
183*d9f75844SAndroid Build Coastguard Worker };
184*d9f75844SAndroid Build Coastguard Worker 
185*d9f75844SAndroid Build Coastguard Worker // Creates and manages timers.
186*d9f75844SAndroid Build Coastguard Worker class TimerManager {
187*d9f75844SAndroid Build Coastguard Worker  public:
TimerManager(std::function<std::unique_ptr<Timeout> (webrtc::TaskQueueBase::DelayPrecision)> create_timeout)188*d9f75844SAndroid Build Coastguard Worker   explicit TimerManager(
189*d9f75844SAndroid Build Coastguard Worker       std::function<std::unique_ptr<Timeout>(
190*d9f75844SAndroid Build Coastguard Worker           webrtc::TaskQueueBase::DelayPrecision)> create_timeout)
191*d9f75844SAndroid Build Coastguard Worker       : create_timeout_(std::move(create_timeout)) {}
192*d9f75844SAndroid Build Coastguard Worker 
193*d9f75844SAndroid Build Coastguard Worker   // Creates a timer with name `name` that will expire (when started) after
194*d9f75844SAndroid Build Coastguard Worker   // `options.duration` and call `on_expired`. There are more `options` that
195*d9f75844SAndroid Build Coastguard Worker   // affects the behavior. Note that timers are created initially stopped.
196*d9f75844SAndroid Build Coastguard Worker   std::unique_ptr<Timer> CreateTimer(absl::string_view name,
197*d9f75844SAndroid Build Coastguard Worker                                      Timer::OnExpired on_expired,
198*d9f75844SAndroid Build Coastguard Worker                                      const TimerOptions& options);
199*d9f75844SAndroid Build Coastguard Worker 
200*d9f75844SAndroid Build Coastguard Worker   void HandleTimeout(TimeoutID timeout_id);
201*d9f75844SAndroid Build Coastguard Worker 
202*d9f75844SAndroid Build Coastguard Worker  private:
203*d9f75844SAndroid Build Coastguard Worker   const std::function<std::unique_ptr<Timeout>(
204*d9f75844SAndroid Build Coastguard Worker       webrtc::TaskQueueBase::DelayPrecision)>
205*d9f75844SAndroid Build Coastguard Worker       create_timeout_;
206*d9f75844SAndroid Build Coastguard Worker   std::map<TimerID, Timer*> timers_;
207*d9f75844SAndroid Build Coastguard Worker   TimerID next_id_ = TimerID(0);
208*d9f75844SAndroid Build Coastguard Worker };
209*d9f75844SAndroid Build Coastguard Worker 
210*d9f75844SAndroid Build Coastguard Worker }  // namespace dcsctp
211*d9f75844SAndroid Build Coastguard Worker 
212*d9f75844SAndroid Build Coastguard Worker #endif  // NET_DCSCTP_TIMER_TIMER_H_
213