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