Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 5 | // A "timer" takes care of invoking a callback in the future, once or |
| 6 | // repeatedly. The callback is invoked: |
| 7 | // - OneShotTimer: Once after a `TimeDelta` delay has elapsed. |
| 8 | // - RetainingOneShotTimer: Same as OneShotTimer, but the callback is retained |
| 9 | // after being executed, allowing another invocation to be scheduled with |
| 10 | // Reset() without specifying the callback again. |
| 11 | // - DeadlineTimer: Once at the specified `TimeTicks` time. |
| 12 | // - RepeatingTimer: Repeatedly, with a specified `TimeDelta` delay before the |
| 13 | // first invocation and between invocations. |
Etienne Pierre-doray | c765461 | 2022-06-08 22:23:32 | [diff] [blame] | 14 | // - MetronomeTimer: Repeatedly, with a specified `TimeDelta` delay between the |
| 15 | // beginning of each invocations such that a constant phase is respected. |
| 16 | // (Retaining)OneShotTimer and RepeatingTimer automatically apply some leeway to |
| 17 | // the delay whereas DeadlineTimer and MetronomeTimer allow more control over |
| 18 | // the requested time. As a result, the former are generally more |
| 19 | // power-efficient. |
| 20 | // Prefer using (Retaining)OneShotTimer and RepeatingTimer because they |
| 21 | // automatically apply some leeway to the delay which enables power-efficient |
| 22 | // scheduling. |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 23 | |
| 24 | // Scheduled invocations can be cancelled with Stop() or by deleting the |
| 25 | // Timer. The latter makes it easy to ensure that an object is not accessed by a |
| 26 | // Timer after it has been deleted: just make the Timer a member of the object |
| 27 | // which receives Timer events (see example below). |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 28 | // |
| 29 | // Sample RepeatingTimer usage: |
| 30 | // |
| 31 | // class MyClass { |
| 32 | // public: |
| 33 | // void StartDoingStuff() { |
Hong Xu | a6f5112 | 2022-04-20 20:10:22 | [diff] [blame] | 34 | // timer_.Start(FROM_HERE, base::Seconds(1), |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 35 | // this, &MyClass::DoStuff); |
Hong Xu | a6f5112 | 2022-04-20 20:10:22 | [diff] [blame] | 36 | // // Alternative form if the callback is not bound to `this` or |
| 37 | // // requires arguments: |
| 38 | // // timer_.Start(FROM_HERE, base::Seconds(1), |
| 39 | // // base::BindRepeating(&MyFunction, 42)); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 40 | // } |
| 41 | // void StopDoingStuff() { |
| 42 | // timer_.Stop(); |
| 43 | // } |
| 44 | // private: |
| 45 | // void DoStuff() { |
| 46 | // // This method is called every second to do stuff. |
| 47 | // ... |
| 48 | // } |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 49 | // base::RepeatingTimer timer_; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 50 | // }; |
| 51 | // |
Gabriel Charette | e92ccc0 | 2019-01-19 14:13:05 | [diff] [blame] | 52 | // These APIs are not thread safe. When a method is called (except the |
| 53 | // constructor), all further method calls must be on the same sequence until |
Patrick Monette | c4d306d | 2021-06-23 19:15:25 | [diff] [blame] | 54 | // Stop(). Once stopped, it may be destroyed or restarted on another sequence. |
ossu | 87340af | 2017-07-06 09:02:15 | [diff] [blame] | 55 | // |
| 56 | // By default, the scheduled tasks will be run on the same sequence that the |
Gabriel Charette | e92ccc0 | 2019-01-19 14:13:05 | [diff] [blame] | 57 | // Timer was *started on*. To mock time in unit tests, some old tests used |
| 58 | // SetTaskRunner() to schedule the delay on a test-controlled TaskRunner. The |
Gabriel Charette | 694c3c33 | 2019-08-19 14:53:05 | [diff] [blame] | 59 | // modern and preferred approach to mock time is to use TaskEnvironment's |
Gabriel Charette | e92ccc0 | 2019-01-19 14:13:05 | [diff] [blame] | 60 | // MOCK_TIME mode. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 61 | |
| 62 | #ifndef BASE_TIMER_TIMER_H_ |
| 63 | #define BASE_TIMER_TIMER_H_ |
| 64 | |
| 65 | // IMPORTANT: If you change timer code, make sure that all tests (including |
| 66 | // disabled ones) from timer_unittests.cc pass locally. Some are disabled |
| 67 | // because they're flaky on the buildbot, but when you run them locally you |
| 68 | // should be able to tell the difference. |
| 69 | |
| 70 | #include "base/base_export.h" |
Tom Sepez | 04e98bf | 2024-10-25 18:19:31 | [diff] [blame] | 71 | #include "base/compiler_specific.h" |
Avi Drissman | 63e1f99 | 2023-01-13 18:54:43 | [diff] [blame] | 72 | #include "base/functional/bind.h" |
| 73 | #include "base/functional/callback.h" |
| 74 | #include "base/functional/callback_helpers.h" |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 75 | #include "base/location.h" |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 76 | #include "base/memory/raw_ptr.h" |
Patrick Monette | c4d306d | 2021-06-23 19:15:25 | [diff] [blame] | 77 | #include "base/sequence_checker.h" |
Patrick Monette | 6994b91f | 2021-11-01 19:19:04 | [diff] [blame] | 78 | #include "base/task/delayed_task_handle.h" |
Patrick Monette | 643cdf6 | 2021-10-15 19:13:42 | [diff] [blame] | 79 | #include "base/task/sequenced_task_runner.h" |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 80 | #include "base/time/time.h" |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 81 | #include "base/types/strong_alias.h" |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 82 | |
| 83 | namespace base { |
| 84 | |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 85 | class TickClock; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 86 | |
tzik | d93bb086 | 2018-07-19 11:54:14 | [diff] [blame] | 87 | namespace internal { |
| 88 | |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 89 | // This class wraps logic shared by all timers. |
tzik | d93bb086 | 2018-07-19 11:54:14 | [diff] [blame] | 90 | class BASE_EXPORT TimerBase { |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 91 | public: |
Peter Boström | 7319bbd | 2021-09-15 22:59:38 | [diff] [blame] | 92 | TimerBase(const TimerBase&) = delete; |
| 93 | TimerBase& operator=(const TimerBase&) = delete; |
| 94 | |
tzik | d93bb086 | 2018-07-19 11:54:14 | [diff] [blame] | 95 | virtual ~TimerBase(); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 96 | |
| 97 | // Returns true if the timer is running (i.e., not stopped). |
tzik | 84db891 | 2018-06-19 06:08:54 | [diff] [blame] | 98 | bool IsRunning() const; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 99 | |
Gabriel Charette | e92ccc0 | 2019-01-19 14:13:05 | [diff] [blame] | 100 | // Sets the task runner on which the delayed task should be scheduled when |
| 101 | // this Timer is running. This method can only be called while this Timer |
Etienne Pierre-doray | 5321eb0 | 2021-12-09 18:05:49 | [diff] [blame] | 102 | // isn't running. If this is used to mock time in tests, the modern and |
| 103 | // preferred approach is to use TaskEnvironment::TimeSource::MOCK_TIME. To |
| 104 | // avoid racy usage of Timer, |task_runner| must run tasks on the same |
| 105 | // sequence which this Timer is bound to (started from). TODO(gab): Migrate |
| 106 | // callers using this as a test seam to |
Gabriel Charette | 694c3c33 | 2019-08-19 14:53:05 | [diff] [blame] | 107 | // TaskEnvironment::TimeSource::MOCK_TIME. |
tzik | 84db891 | 2018-06-19 06:08:54 | [diff] [blame] | 108 | virtual void SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner); |
petrcermak | 7652da6d | 2014-11-06 02:17:57 | [diff] [blame] | 109 | |
Hong Xu | 4b10581 | 2022-08-09 14:49:42 | [diff] [blame] | 110 | // Call this method to stop the timer and cancel all previously scheduled |
| 111 | // tasks. It is a no-op if the timer is not running. |
[email protected] | 9df013b | 2014-03-13 22:04:39 | [diff] [blame] | 112 | virtual void Stop(); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 113 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 114 | protected: |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 115 | // Constructs a timer. Start must be called later to set task info. |
| 116 | explicit TimerBase(const Location& posted_from = Location()); |
| 117 | |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 118 | virtual void OnStop() = 0; |
| 119 | |
Etienne Pierre-doray | 9c3d17d8 | 2022-05-12 22:05:29 | [diff] [blame] | 120 | // Disables the scheduled task and abandons it so that it no longer refers |
| 121 | // back to this object. |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 122 | void AbandonScheduledTask(); |
| 123 | |
| 124 | // Returns the task runner on which the task should be scheduled. If the |
| 125 | // corresponding |task_runner_| field is null, the task runner for the current |
| 126 | // sequence is returned. |
| 127 | scoped_refptr<SequencedTaskRunner> GetTaskRunner(); |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 128 | |
tzik | f550a61 | 2018-07-12 02:25:34 | [diff] [blame] | 129 | // The task runner on which the task should be scheduled. If it is null, the |
| 130 | // task runner for the current sequence will be used. |
| 131 | scoped_refptr<SequencedTaskRunner> task_runner_; |
| 132 | |
Patrick Monette | c4d306d | 2021-06-23 19:15:25 | [diff] [blame] | 133 | // Timer isn't thread-safe and while it is running, it must only be used on |
| 134 | // the same sequence until fully Stop()'ed. Once stopped, it may be destroyed |
| 135 | // or restarted on another sequence. |
| 136 | SEQUENCE_CHECKER(sequence_checker_); |
tzik | f550a61 | 2018-07-12 02:25:34 | [diff] [blame] | 137 | |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 138 | // Location in user code. |
| 139 | Location posted_from_ GUARDED_BY_CONTEXT(sequence_checker_); |
| 140 | |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 141 | // The handle to the posted delayed task. |
| 142 | DelayedTaskHandle delayed_task_handle_ GUARDED_BY_CONTEXT(sequence_checker_); |
Etienne Pierre-doray | f1685686 | 2022-06-10 18:49:02 | [diff] [blame] | 143 | |
| 144 | // Callback invoked when the timer is ready. This is saved as a member to |
| 145 | // avoid rebinding every time the Timer fires. Lazy initialized the first time |
| 146 | // the Timer is started. |
| 147 | RepeatingClosure timer_callback_; |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 148 | }; |
| 149 | |
| 150 | //----------------------------------------------------------------------------- |
| 151 | // This class wraps logic shared by (Retaining)OneShotTimer and RepeatingTimer. |
| 152 | class BASE_EXPORT DelayTimerBase : public TimerBase { |
| 153 | public: |
| 154 | DelayTimerBase(const DelayTimerBase&) = delete; |
| 155 | DelayTimerBase& operator=(const DelayTimerBase&) = delete; |
| 156 | |
| 157 | ~DelayTimerBase() override; |
| 158 | |
| 159 | // Returns the current delay for this timer. |
| 160 | TimeDelta GetCurrentDelay() const; |
| 161 | |
| 162 | // Call this method to reset the timer delay. The user task must be set. If |
| 163 | // the timer is not running, this will start it by posting a task. |
| 164 | virtual void Reset(); |
| 165 | |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 166 | TimeTicks desired_run_time() const { |
| 167 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 168 | return desired_run_time_; |
| 169 | } |
| 170 | |
| 171 | protected: |
| 172 | // Constructs a timer. Start must be called later to set task info. |
| 173 | // If |tick_clock| is provided, it is used instead of TimeTicks::Now() to get |
| 174 | // TimeTicks when scheduling tasks. |
| 175 | explicit DelayTimerBase(const TickClock* tick_clock = nullptr); |
| 176 | |
| 177 | // Construct a timer with task info. |
| 178 | // If |tick_clock| is provided, it is used instead of TimeTicks::Now() to get |
| 179 | // TimeTicks when scheduling tasks. |
| 180 | DelayTimerBase(const Location& posted_from, |
| 181 | TimeDelta delay, |
| 182 | const TickClock* tick_clock = nullptr); |
| 183 | |
Etienne Pierre-doray | 9c3d17d8 | 2022-05-12 22:05:29 | [diff] [blame] | 184 | virtual void RunUserTask() = 0; |
| 185 | |
Patrick Monette | 57d1c18d | 2021-04-22 00:50:44 | [diff] [blame] | 186 | // Schedules |OnScheduledTaskInvoked()| to run on the current sequence with |
Etienne Pierre-doray | 8c6860d | 2023-03-22 20:18:49 | [diff] [blame] | 187 | // the given |delay|. |desired_run_time_| is reset to Now() + delay. |
Patrick Monette | 57d1c18d | 2021-04-22 00:50:44 | [diff] [blame] | 188 | void ScheduleNewTask(TimeDelta delay); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 189 | |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 190 | void StartInternal(const Location& posted_from, TimeDelta delay); |
| 191 | |
| 192 | private: |
Patrick Monette | 478af57 | 2022-01-20 20:34:15 | [diff] [blame] | 193 | // DCHECKs that the user task is not null. Used to diagnose a recurring bug |
| 194 | // where Reset() is called on a OneShotTimer that has already fired. |
| 195 | virtual void EnsureNonNullUserTask() = 0; |
| 196 | |
Patrick Monette | 400b0514 | 2021-04-21 22:28:35 | [diff] [blame] | 197 | // Returns the current tick count. |
| 198 | TimeTicks Now() const; |
| 199 | |
Patrick Monette | 57d1c18d | 2021-04-22 00:50:44 | [diff] [blame] | 200 | // Called when the scheduled task is invoked. Will run the |user_task| if the |
| 201 | // timer is still running and |desired_run_time_| was reached. |
Etienne Pierre-doray | 9c3d17d8 | 2022-05-12 22:05:29 | [diff] [blame] | 202 | void OnScheduledTaskInvoked(); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 203 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 204 | // Delay requested by user. |
Patrick Monette | c4d306d | 2021-06-23 19:15:25 | [diff] [blame] | 205 | TimeDelta delay_ GUARDED_BY_CONTEXT(sequence_checker_); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 206 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame] | 207 | // The desired run time of |user_task_|. The user may update this at any time, |
Etienne Pierre-doray | 8c6860d | 2023-03-22 20:18:49 | [diff] [blame] | 208 | // even if their previous request has not run yet. This time can be a "zero" |
| 209 | // TimeTicks if the task must be run immediately. |
Patrick Monette | c4d306d | 2021-06-23 19:15:25 | [diff] [blame] | 210 | TimeTicks desired_run_time_ GUARDED_BY_CONTEXT(sequence_checker_); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 211 | |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 212 | // The tick clock used to calculate the run time for scheduled tasks. |
Keishi Hattori | 0e45c02 | 2021-11-27 09:25:52 | [diff] [blame] | 213 | const raw_ptr<const TickClock> tick_clock_ |
| 214 | GUARDED_BY_CONTEXT(sequence_checker_); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 215 | }; |
| 216 | |
tzik | d93bb086 | 2018-07-19 11:54:14 | [diff] [blame] | 217 | } // namespace internal |
| 218 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 219 | //----------------------------------------------------------------------------- |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 220 | // A simple, one-shot timer. See usage notes at the top of the file. |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 221 | class BASE_EXPORT OneShotTimer : public internal::DelayTimerBase { |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 222 | public: |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 223 | OneShotTimer(); |
| 224 | explicit OneShotTimer(const TickClock* tick_clock); |
Peter Boström | 7319bbd | 2021-09-15 22:59:38 | [diff] [blame] | 225 | |
| 226 | OneShotTimer(const OneShotTimer&) = delete; |
| 227 | OneShotTimer& operator=(const OneShotTimer&) = delete; |
| 228 | |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 229 | ~OneShotTimer() override; |
| 230 | |
| 231 | // Start the timer to run at the given |delay| from now. If the timer is |
| 232 | // already running, it will be replaced to call the given |user_task|. |
| 233 | virtual void Start(const Location& posted_from, |
| 234 | TimeDelta delay, |
| 235 | OnceClosure user_task); |
| 236 | |
| 237 | // Start the timer to run at the given |delay| from now. If the timer is |
| 238 | // already running, it will be replaced to call a task formed from |
Ming-Chuan Lin | 1b43b40 | 2020-02-27 17:53:39 | [diff] [blame] | 239 | // |receiver->*method|. |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 240 | template <class Receiver> |
| 241 | void Start(const Location& posted_from, |
| 242 | TimeDelta delay, |
| 243 | Receiver* receiver, |
| 244 | void (Receiver::*method)()) { |
Peter Kasting | a253f75 | 2025-01-31 18:57:26 | [diff] [blame] | 245 | // Explicitly qualify calls, in case this is used inside Blink (which has |
| 246 | // similar methods in WTF). |
| 247 | Start(posted_from, delay, |
| 248 | ::base::BindOnce(method, ::base::Unretained(receiver))); |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 249 | } |
tzik | f550a61 | 2018-07-12 02:25:34 | [diff] [blame] | 250 | |
| 251 | // Run the scheduled task immediately, and stop the timer. The timer needs to |
| 252 | // be running. |
Shintaro Kawamura | ccefc5a | 2023-05-08 16:46:18 | [diff] [blame] | 253 | virtual void FireNow(); |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 254 | |
| 255 | private: |
| 256 | void OnStop() final; |
| 257 | void RunUserTask() final; |
Patrick Monette | 478af57 | 2022-01-20 20:34:15 | [diff] [blame] | 258 | void EnsureNonNullUserTask() final; |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 259 | |
| 260 | OnceClosure user_task_; |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 261 | }; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 262 | |
| 263 | //----------------------------------------------------------------------------- |
| 264 | // A simple, repeating timer. See usage notes at the top of the file. |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 265 | class BASE_EXPORT RepeatingTimer : public internal::DelayTimerBase { |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 266 | public: |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 267 | RepeatingTimer(); |
| 268 | explicit RepeatingTimer(const TickClock* tick_clock); |
Peter Boström | 7319bbd | 2021-09-15 22:59:38 | [diff] [blame] | 269 | |
| 270 | RepeatingTimer(const RepeatingTimer&) = delete; |
| 271 | RepeatingTimer& operator=(const RepeatingTimer&) = delete; |
| 272 | |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 273 | ~RepeatingTimer() override; |
tzik | 524daae9 | 2018-07-05 02:52:02 | [diff] [blame] | 274 | |
| 275 | RepeatingTimer(const Location& posted_from, |
| 276 | TimeDelta delay, |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 277 | RepeatingClosure user_task); |
tzik | 524daae9 | 2018-07-05 02:52:02 | [diff] [blame] | 278 | RepeatingTimer(const Location& posted_from, |
| 279 | TimeDelta delay, |
| 280 | RepeatingClosure user_task, |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 281 | const TickClock* tick_clock); |
| 282 | |
| 283 | // Start the timer to run at the given |delay| from now. If the timer is |
| 284 | // already running, it will be replaced to call the given |user_task|. |
| 285 | virtual void Start(const Location& posted_from, |
| 286 | TimeDelta delay, |
| 287 | RepeatingClosure user_task); |
| 288 | |
| 289 | // Start the timer to run at the given |delay| from now. If the timer is |
| 290 | // already running, it will be replaced to call a task formed from |
Ming-Chuan Lin | 1b43b40 | 2020-02-27 17:53:39 | [diff] [blame] | 291 | // |receiver->*method|. |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 292 | template <class Receiver> |
| 293 | void Start(const Location& posted_from, |
| 294 | TimeDelta delay, |
| 295 | Receiver* receiver, |
| 296 | void (Receiver::*method)()) { |
| 297 | Start(posted_from, delay, BindRepeating(method, Unretained(receiver))); |
| 298 | } |
| 299 | |
Tom Sepez | 04e98bf | 2024-10-25 18:19:31 | [diff] [blame] | 300 | const RepeatingClosure& user_task() const LIFETIME_BOUND { |
| 301 | return user_task_; |
| 302 | } |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 303 | |
| 304 | private: |
| 305 | // Mark this final, so that the destructor can call this safely. |
| 306 | void OnStop() final; |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 307 | void RunUserTask() override; |
Patrick Monette | 478af57 | 2022-01-20 20:34:15 | [diff] [blame] | 308 | void EnsureNonNullUserTask() final; |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 309 | |
| 310 | RepeatingClosure user_task_; |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 311 | }; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 312 | |
| 313 | //----------------------------------------------------------------------------- |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 314 | // A simple, one-shot timer with the retained |user_task| which is reused when |
| 315 | // Reset() is invoked. See usage notes at the top of the file. |
| 316 | class BASE_EXPORT RetainingOneShotTimer : public internal::DelayTimerBase { |
tzik | 57c66e3 | 2018-07-02 08:02:57 | [diff] [blame] | 317 | public: |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 318 | RetainingOneShotTimer(); |
| 319 | explicit RetainingOneShotTimer(const TickClock* tick_clock); |
Peter Boström | 7319bbd | 2021-09-15 22:59:38 | [diff] [blame] | 320 | |
| 321 | RetainingOneShotTimer(const RetainingOneShotTimer&) = delete; |
| 322 | RetainingOneShotTimer& operator=(const RetainingOneShotTimer&) = delete; |
| 323 | |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 324 | ~RetainingOneShotTimer() override; |
tzik | 524daae9 | 2018-07-05 02:52:02 | [diff] [blame] | 325 | |
| 326 | RetainingOneShotTimer(const Location& posted_from, |
| 327 | TimeDelta delay, |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 328 | RepeatingClosure user_task); |
tzik | 524daae9 | 2018-07-05 02:52:02 | [diff] [blame] | 329 | RetainingOneShotTimer(const Location& posted_from, |
| 330 | TimeDelta delay, |
| 331 | RepeatingClosure user_task, |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 332 | const TickClock* tick_clock); |
| 333 | |
| 334 | // Start the timer to run at the given |delay| from now. If the timer is |
| 335 | // already running, it will be replaced to call the given |user_task|. |
| 336 | virtual void Start(const Location& posted_from, |
| 337 | TimeDelta delay, |
| 338 | RepeatingClosure user_task); |
| 339 | |
| 340 | // Start the timer to run at the given |delay| from now. If the timer is |
| 341 | // already running, it will be replaced to call a task formed from |
Ming-Chuan Lin | 1b43b40 | 2020-02-27 17:53:39 | [diff] [blame] | 342 | // |receiver->*method|. |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 343 | template <class Receiver> |
| 344 | void Start(const Location& posted_from, |
| 345 | TimeDelta delay, |
| 346 | Receiver* receiver, |
| 347 | void (Receiver::*method)()) { |
| 348 | Start(posted_from, delay, BindRepeating(method, Unretained(receiver))); |
| 349 | } |
| 350 | |
Tom Sepez | 04e98bf | 2024-10-25 18:19:31 | [diff] [blame] | 351 | const RepeatingClosure& user_task() const LIFETIME_BOUND { |
| 352 | return user_task_; |
| 353 | } |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 354 | |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 355 | private: |
| 356 | // Mark this final, so that the destructor can call this safely. |
| 357 | void OnStop() final; |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 358 | void RunUserTask() override; |
Patrick Monette | 478af57 | 2022-01-20 20:34:15 | [diff] [blame] | 359 | void EnsureNonNullUserTask() final; |
tzik | f3336c9 | 2018-07-25 03:15:50 | [diff] [blame] | 360 | |
| 361 | RepeatingClosure user_task_; |
tzik | 57c66e3 | 2018-07-02 08:02:57 | [diff] [blame] | 362 | }; |
| 363 | |
| 364 | //----------------------------------------------------------------------------- |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 365 | // A Delay timer is like The Button from Lost. Once started, you have to keep |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame] | 366 | // calling Reset otherwise it will call the given method on the sequence it was |
| 367 | // initially Reset() from. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 368 | // |
| 369 | // Once created, it is inactive until Reset is called. Once |delay| seconds have |
| 370 | // passed since the last call to Reset, the callback is made. Once the callback |
| 371 | // has been made, it's inactive until Reset is called again. |
| 372 | // |
| 373 | // If destroyed, the timeout is canceled and will not occur even if already |
| 374 | // inflight. |
tzik | 32c3bcd | 2018-07-09 04:39:38 | [diff] [blame] | 375 | class DelayTimer { |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 376 | public: |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 377 | template <class Receiver> |
Brett Wilson | 8e88b31 | 2017-09-12 05:22:16 | [diff] [blame] | 378 | DelayTimer(const Location& posted_from, |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 379 | TimeDelta delay, |
| 380 | Receiver* receiver, |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 381 | void (Receiver::*method)()) |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 382 | : DelayTimer(posted_from, delay, receiver, method, nullptr) {} |
| 383 | |
| 384 | template <class Receiver> |
Brett Wilson | 8e88b31 | 2017-09-12 05:22:16 | [diff] [blame] | 385 | DelayTimer(const Location& posted_from, |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 386 | TimeDelta delay, |
| 387 | Receiver* receiver, |
| 388 | void (Receiver::*method)(), |
Greg Thompson | aa48ce8d | 2018-04-03 06:11:43 | [diff] [blame] | 389 | const TickClock* tick_clock) |
tzik | 32c3bcd | 2018-07-09 04:39:38 | [diff] [blame] | 390 | : timer_(posted_from, |
| 391 | delay, |
| 392 | BindRepeating(method, Unretained(receiver)), |
| 393 | tick_clock) {} |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 394 | |
Peter Boström | 75cd3c0 | 2021-09-28 15:23:18 | [diff] [blame] | 395 | DelayTimer(const DelayTimer&) = delete; |
| 396 | DelayTimer& operator=(const DelayTimer&) = delete; |
| 397 | |
tzik | 32c3bcd | 2018-07-09 04:39:38 | [diff] [blame] | 398 | void Reset() { timer_.Reset(); } |
| 399 | |
| 400 | private: |
| 401 | RetainingOneShotTimer timer_; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 402 | }; |
| 403 | |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 404 | //----------------------------------------------------------------------------- |
| 405 | // A one-shot timer that attempts to run |user_task| some time near specified |
| 406 | // deadline. See usage notes at the top of the file. |
| 407 | class BASE_EXPORT DeadlineTimer : public internal::TimerBase { |
| 408 | public: |
| 409 | DeadlineTimer(); |
| 410 | ~DeadlineTimer() override; |
| 411 | |
| 412 | DeadlineTimer(const DeadlineTimer&) = delete; |
| 413 | DeadlineTimer& operator=(const DeadlineTimer&) = delete; |
| 414 | |
Etienne Pierre-doray | a3f3827 | 2022-12-08 22:10:51 | [diff] [blame] | 415 | // Start the timer to run |user_task| near the specified |deadline| following |
| 416 | // |delay_policy| If the timer is already running, it will be replaced to call |
| 417 | // the given |user_task|. |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 418 | void Start(const Location& posted_from, |
| 419 | TimeTicks deadline, |
| 420 | OnceClosure user_task, |
Etienne Pierre-doray | a3f3827 | 2022-12-08 22:10:51 | [diff] [blame] | 421 | subtle::DelayPolicy delay_policy = |
| 422 | subtle::DelayPolicy::kFlexiblePreferEarly); |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 423 | |
| 424 | // Start the timer to run |user_task| near the specified |deadline|. If the |
| 425 | // timer is already running, it will be replaced to call a task formed from |
| 426 | // |receiver->*method|. |
| 427 | template <class Receiver> |
| 428 | void Start(const Location& posted_from, |
| 429 | TimeTicks deadline, |
| 430 | Receiver* receiver, |
| 431 | void (Receiver::*method)(), |
Etienne Pierre-doray | a3f3827 | 2022-12-08 22:10:51 | [diff] [blame] | 432 | subtle::DelayPolicy delay_policy = |
| 433 | subtle::DelayPolicy::kFlexiblePreferEarly) { |
| 434 | Start(posted_from, deadline, BindOnce(method, Unretained(receiver)), |
| 435 | delay_policy); |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 436 | } |
| 437 | |
| 438 | protected: |
| 439 | void OnStop() override; |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 440 | |
| 441 | // Schedules |OnScheduledTaskInvoked()| to run on the current sequence at |
| 442 | // the given |deadline|. |
| 443 | void ScheduleNewTask(TimeTicks deadline, subtle::DelayPolicy delay_policy); |
| 444 | |
| 445 | private: |
| 446 | // Called when the scheduled task is invoked to run the |user_task|. |
Etienne Pierre-doray | 9c3d17d8 | 2022-05-12 22:05:29 | [diff] [blame] | 447 | void OnScheduledTaskInvoked(); |
Etienne Pierre-doray | 1a0edc7 | 2022-01-14 15:11:44 | [diff] [blame] | 448 | |
| 449 | OnceClosure user_task_; |
| 450 | }; |
| 451 | |
Etienne Pierre-doray | c765461 | 2022-06-08 22:23:32 | [diff] [blame] | 452 | //----------------------------------------------------------------------------- |
| 453 | // Repeatedly invokes a callback, waiting for a precise delay between the |
| 454 | // beginning of each invocation. See usage notes at the top of the file. |
| 455 | class BASE_EXPORT MetronomeTimer : public internal::TimerBase { |
| 456 | public: |
| 457 | MetronomeTimer(); |
| 458 | ~MetronomeTimer() override; |
| 459 | |
| 460 | MetronomeTimer(const MetronomeTimer&) = delete; |
| 461 | MetronomeTimer& operator=(const MetronomeTimer&) = delete; |
| 462 | |
| 463 | MetronomeTimer(const Location& posted_from, |
| 464 | TimeDelta interval, |
| 465 | RepeatingClosure user_task, |
| 466 | TimeTicks phase = TimeTicks()); |
| 467 | |
| 468 | // Start the timer to repeatedly run |user_task| at the specified |interval|; |
| 469 | // If not specified, the phase is up to the scheduler, otherwise each |
| 470 | // invocation starts as close as possible to `phase + n * delay` for some |
| 471 | // integer n. If the timer is already running, it will be replaced to call the |
| 472 | // given |user_task|. |
| 473 | void Start(const Location& posted_from, |
| 474 | TimeDelta interval, |
| 475 | RepeatingClosure user_task, |
| 476 | TimeTicks phase = TimeTicks()); |
| 477 | |
| 478 | // Same as the previous overload, except that the user task is specified by |
| 479 | // `receiver` and `method`. |
| 480 | template <class Receiver> |
| 481 | void Start(const Location& posted_from, |
| 482 | TimeDelta interval, |
| 483 | Receiver* receiver, |
| 484 | void (Receiver::*method)(), |
| 485 | TimeTicks phase = TimeTicks()) { |
Francois Doray | f8b6e45a | 2023-02-17 17:32:17 | [diff] [blame] | 486 | Start(posted_from, interval, BindRepeating(method, Unretained(receiver)), |
| 487 | phase); |
Etienne Pierre-doray | c765461 | 2022-06-08 22:23:32 | [diff] [blame] | 488 | } |
| 489 | |
| 490 | // Call this method to reset the timer delay. The user task must be set. If |
| 491 | // the timer is not running, this will start it by posting a task. |
| 492 | void Reset(); |
| 493 | |
| 494 | protected: |
| 495 | void OnStop() override; |
| 496 | |
| 497 | // Schedules |OnScheduledTaskInvoked()| to run on the current sequence at |
| 498 | // the next tick. |
| 499 | void ScheduleNewTask(); |
| 500 | |
| 501 | private: |
| 502 | // Called when the scheduled task is invoked to run the |user_task|. |
| 503 | void OnScheduledTaskInvoked(); |
| 504 | |
| 505 | TimeDelta interval_; |
| 506 | RepeatingClosure user_task_; |
| 507 | TimeTicks phase_; |
| 508 | }; |
| 509 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 510 | } // namespace base |
| 511 | |
| 512 | #endif // BASE_TIMER_TIMER_H_ |