[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | // OneShotTimer and RepeatingTimer provide a simple timer API. As the names |
| 6 | // suggest, OneShotTimer calls you back once after a time delay expires. |
| 7 | // RepeatingTimer on the other hand calls you back periodically with the |
| 8 | // prescribed time interval. |
| 9 | // |
| 10 | // OneShotTimer and RepeatingTimer both cancel the timer when they go out of |
| 11 | // scope, which makes it easy to ensure that you do not get called when your |
| 12 | // object has gone out of scope. Just instantiate a OneShotTimer or |
| 13 | // RepeatingTimer as a member variable of the class for which you wish to |
| 14 | // receive timer events. |
| 15 | // |
| 16 | // Sample RepeatingTimer usage: |
| 17 | // |
| 18 | // class MyClass { |
| 19 | // public: |
| 20 | // void StartDoingStuff() { |
| 21 | // timer_.Start(FROM_HERE, TimeDelta::FromSeconds(1), |
| 22 | // this, &MyClass::DoStuff); |
| 23 | // } |
| 24 | // void StopDoingStuff() { |
| 25 | // timer_.Stop(); |
| 26 | // } |
| 27 | // private: |
| 28 | // void DoStuff() { |
| 29 | // // This method is called every second to do stuff. |
| 30 | // ... |
| 31 | // } |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 32 | // base::RepeatingTimer timer_; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 33 | // }; |
| 34 | // |
| 35 | // Both OneShotTimer and RepeatingTimer also support a Reset method, which |
| 36 | // allows you to easily defer the timer event until the timer delay passes once |
| 37 | // again. So, in the above example, if 0.5 seconds have already passed, |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 38 | // calling Reset on |timer_| would postpone DoStuff by another 1 second. In |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 39 | // other words, Reset is shorthand for calling Stop and then Start again with |
| 40 | // the same arguments. |
| 41 | // |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 42 | // These APIs are not thread safe. All methods must be called from the same |
| 43 | // sequence (not necessarily the construction sequence), except for the |
| 44 | // destructor and SetTaskRunner() which may be called from any sequence when the |
| 45 | // timer is not running (i.e. when Start() has never been called or Stop() has |
| 46 | // been called since the last Start()). By default, the scheduled tasks will be |
| 47 | // run on the same sequence that the Timer was *started on*, but this can be |
| 48 | // changed *prior* to Start() via SetTaskRunner(). |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 49 | |
| 50 | #ifndef BASE_TIMER_TIMER_H_ |
| 51 | #define BASE_TIMER_TIMER_H_ |
| 52 | |
| 53 | // IMPORTANT: If you change timer code, make sure that all tests (including |
| 54 | // disabled ones) from timer_unittests.cc pass locally. Some are disabled |
| 55 | // because they're flaky on the buildbot, but when you run them locally you |
| 56 | // should be able to tell the difference. |
| 57 | |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 58 | #include <memory> |
| 59 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 60 | #include "base/base_export.h" |
| 61 | #include "base/bind.h" |
| 62 | #include "base/bind_helpers.h" |
| 63 | #include "base/callback.h" |
| 64 | #include "base/location.h" |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 65 | #include "base/macros.h" |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 66 | #include "base/sequence_checker_impl.h" |
| 67 | #include "base/sequenced_task_runner.h" |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 68 | #include "base/time/time.h" |
| 69 | |
| 70 | namespace base { |
| 71 | |
| 72 | class BaseTimerTaskInternal; |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 73 | class TickClock; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 74 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 75 | // TODO(gab): Removing this fwd-decl causes IWYU failures in other headers, |
| 76 | // remove it in a follow- up CL. |
| 77 | class SingleThreadTaskRunner; |
| 78 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 79 | //----------------------------------------------------------------------------- |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 80 | // This class wraps TaskRunner::PostDelayedTask to manage delayed and repeating |
| 81 | // tasks. See meta comment above for thread-safety requirements. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 82 | // |
| 83 | class BASE_EXPORT Timer { |
| 84 | public: |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 85 | // Construct a timer in repeating or one-shot mode. Start must be called later |
| 86 | // to set task info. |retain_user_task| determines whether the user_task is |
| 87 | // retained or reset when it runs or stops. If |tick_clock| is provided, it is |
| 88 | // used instead of TimeTicks::Now() to get TimeTicks when scheduling tasks. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 89 | Timer(bool retain_user_task, bool is_repeating); |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 90 | Timer(bool retain_user_task, bool is_repeating, TickClock* tick_clock); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 91 | |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 92 | // Construct a timer with retained task info. If |tick_clock| is provided, it |
| 93 | // is used instead of TimeTicks::Now() to get TimeTicks when scheduling tasks. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 94 | Timer(const tracked_objects::Location& posted_from, |
| 95 | TimeDelta delay, |
| 96 | const base::Closure& user_task, |
| 97 | bool is_repeating); |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 98 | Timer(const tracked_objects::Location& posted_from, |
| 99 | TimeDelta delay, |
| 100 | const base::Closure& user_task, |
| 101 | bool is_repeating, |
| 102 | TickClock* tick_clock); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 103 | |
| 104 | virtual ~Timer(); |
| 105 | |
| 106 | // Returns true if the timer is running (i.e., not stopped). |
[email protected] | 9df013b | 2014-03-13 22:04:39 | [diff] [blame] | 107 | virtual bool IsRunning() const; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 108 | |
| 109 | // Returns the current delay for this timer. |
[email protected] | 9df013b | 2014-03-13 22:04:39 | [diff] [blame] | 110 | virtual TimeDelta GetCurrentDelay() const; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 111 | |
petrcermak | 7652da6d | 2014-11-06 02:17:57 | [diff] [blame] | 112 | // Set the task runner on which the task should be scheduled. This method can |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 113 | // only be called before any tasks have been scheduled. If |task_runner| runs |
| 114 | // tasks on a different sequence than the sequence owning this Timer, |
| 115 | // |user_task_| will be posted to it when the Timer fires (note that this |
| 116 | // means |user_task_| can run after ~Timer() and should support that). |
| 117 | void SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner); |
petrcermak | 7652da6d | 2014-11-06 02:17:57 | [diff] [blame] | 118 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 119 | // Start the timer to run at the given |delay| from now. If the timer is |
| 120 | // already running, it will be replaced to call the given |user_task|. |
[email protected] | 9df013b | 2014-03-13 22:04:39 | [diff] [blame] | 121 | virtual void Start(const tracked_objects::Location& posted_from, |
[email protected] | d76fad6 | 2014-05-23 21:09:20 | [diff] [blame] | 122 | TimeDelta delay, |
| 123 | const base::Closure& user_task); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 124 | |
| 125 | // Call this method to stop and cancel the timer. It is a no-op if the timer |
| 126 | // is not running. |
[email protected] | 9df013b | 2014-03-13 22:04:39 | [diff] [blame] | 127 | virtual void Stop(); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 128 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 129 | // Call this method to reset the timer delay. The |user_task_| must be set. If |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 130 | // the timer is not running, this will start it by posting a task. |
[email protected] | 9df013b | 2014-03-13 22:04:39 | [diff] [blame] | 131 | virtual void Reset(); |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 132 | |
| 133 | const base::Closure& user_task() const { return user_task_; } |
| 134 | const TimeTicks& desired_run_time() const { return desired_run_time_; } |
| 135 | |
| 136 | protected: |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 137 | // Returns the current tick count. |
| 138 | TimeTicks Now() const; |
| 139 | |
chirantan | 26436e40 | 2014-10-24 19:44:42 | [diff] [blame] | 140 | void set_user_task(const Closure& task) { user_task_ = task; } |
| 141 | void set_desired_run_time(TimeTicks desired) { desired_run_time_ = desired; } |
| 142 | void set_is_running(bool running) { is_running_ = running; } |
| 143 | |
| 144 | const tracked_objects::Location& posted_from() const { return posted_from_; } |
[email protected] | 9df013b | 2014-03-13 22:04:39 | [diff] [blame] | 145 | bool retain_user_task() const { return retain_user_task_; } |
| 146 | bool is_repeating() const { return is_repeating_; } |
chirantan | 26436e40 | 2014-10-24 19:44:42 | [diff] [blame] | 147 | bool is_running() const { return is_running_; } |
[email protected] | 9df013b | 2014-03-13 22:04:39 | [diff] [blame] | 148 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 149 | private: |
| 150 | friend class BaseTimerTaskInternal; |
| 151 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 152 | // Allocates a new |scheduled_task_| and posts it on the current sequence with |
| 153 | // the given |delay|. |scheduled_task_| must be null. |scheduled_run_time_| |
| 154 | // and |desired_run_time_| are reset to Now() + delay. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 155 | void PostNewScheduledTask(TimeDelta delay); |
| 156 | |
jsbell | 763cd40 | 2015-12-17 00:38:27 | [diff] [blame] | 157 | // Returns the task runner on which the task should be scheduled. If the |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 158 | // corresponding |task_runner_| field is null, the task runner for the current |
| 159 | // sequence is returned. |
| 160 | scoped_refptr<SequencedTaskRunner> GetTaskRunner(); |
petrcermak | 7652da6d | 2014-11-06 02:17:57 | [diff] [blame] | 161 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 162 | // Disable |scheduled_task_| and abandon it so that it no longer refers back |
| 163 | // to this object. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 164 | void AbandonScheduledTask(); |
| 165 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 166 | // Called by BaseTimerTaskInternal when the delayed task fires. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 167 | void RunScheduledTask(); |
| 168 | |
| 169 | // Stop running task (if any) and abandon scheduled task (if any). |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 170 | void AbandonAndStop() { |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 171 | AbandonScheduledTask(); |
pkotwicz | 4a286ed | 2017-01-17 20:45:26 | [diff] [blame] | 172 | |
| 173 | Stop(); |
| 174 | // No more member accesses here: |this| could be deleted at this point. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 175 | } |
| 176 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 177 | // When non-null, the |scheduled_task_| was posted to call RunScheduledTask() |
| 178 | // at |scheduled_run_time_|. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 179 | BaseTimerTaskInternal* scheduled_task_; |
| 180 | |
petrcermak | 7652da6d | 2014-11-06 02:17:57 | [diff] [blame] | 181 | // The task runner on which the task should be scheduled. If it is null, the |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 182 | // task runner for the current sequence will be used. |
| 183 | scoped_refptr<SequencedTaskRunner> task_runner_; |
petrcermak | 7652da6d | 2014-11-06 02:17:57 | [diff] [blame] | 184 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 185 | // Location in user code. |
| 186 | tracked_objects::Location posted_from_; |
| 187 | // Delay requested by user. |
| 188 | TimeDelta delay_; |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 189 | // |user_task_| is what the user wants to be run at |desired_run_time_|. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 190 | base::Closure user_task_; |
| 191 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 192 | // The time at which |scheduled_task_| is expected to fire. This time can be a |
| 193 | // "zero" TimeTicks if the task must be run immediately. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 194 | TimeTicks scheduled_run_time_; |
| 195 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 196 | // The desired run time of |user_task_|. The user may update this at any time, |
| 197 | // even if their previous request has not run yet. If |desired_run_time_| is |
| 198 | // greater than |scheduled_run_time_|, a continuation task will be posted to |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 199 | // wait for the remaining time. This allows us to reuse the pending task so as |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 200 | // not to flood the delayed queues with orphaned tasks when the user code |
[email protected] | d0451935 | 2013-12-04 20:05:45 | [diff] [blame] | 201 | // excessively Stops and Starts the timer. This time can be a "zero" TimeTicks |
| 202 | // if the task must be run immediately. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 203 | TimeTicks desired_run_time_; |
| 204 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 205 | // Timer isn't thread-safe and must only be used on its origin sequence |
| 206 | // (sequence on which it was started). |
| 207 | SequenceChecker origin_sequence_checker_; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 208 | |
| 209 | // Repeating timers automatically post the task again before calling the task |
| 210 | // callback. |
| 211 | const bool is_repeating_; |
| 212 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 213 | // If true, hold on to the |user_task_| closure object for reuse. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 214 | const bool retain_user_task_; |
| 215 | |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 216 | // The tick clock used to calculate the run time for scheduled tasks. |
| 217 | TickClock* const tick_clock_; |
| 218 | |
gab | 4e844bb | 2017-06-01 16:23:36 | [diff] [blame^] | 219 | // If true, |user_task_| is scheduled to run sometime in the future. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 220 | bool is_running_; |
| 221 | |
| 222 | DISALLOW_COPY_AND_ASSIGN(Timer); |
| 223 | }; |
| 224 | |
| 225 | //----------------------------------------------------------------------------- |
| 226 | // This class is an implementation detail of OneShotTimer and RepeatingTimer. |
| 227 | // Please do not use this class directly. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 228 | class BaseTimerMethodPointer : public Timer { |
| 229 | public: |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 230 | // This is here to work around the fact that Timer::Start is "hidden" by the |
| 231 | // Start definition below, rather than being overloaded. |
| 232 | // TODO(tim): We should remove uses of BaseTimerMethodPointer::Start below |
| 233 | // and convert callers to use the base::Closure version in Timer::Start, |
| 234 | // see bug 148832. |
| 235 | using Timer::Start; |
| 236 | |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 237 | enum RepeatMode { ONE_SHOT, REPEATING }; |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 238 | BaseTimerMethodPointer(RepeatMode mode, TickClock* tick_clock) |
| 239 | : Timer(mode == REPEATING, mode == REPEATING, tick_clock) {} |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 240 | |
| 241 | // Start the timer to run at the given |delay| from now. If the timer is |
| 242 | // already running, it will be replaced to call a task formed from |
| 243 | // |reviewer->*method|. |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 244 | template <class Receiver> |
| 245 | void Start(const tracked_objects::Location& posted_from, |
| 246 | TimeDelta delay, |
| 247 | Receiver* receiver, |
| 248 | void (Receiver::*method)()) { |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 249 | Timer::Start(posted_from, delay, |
| 250 | base::Bind(method, base::Unretained(receiver))); |
| 251 | } |
| 252 | }; |
| 253 | |
| 254 | //----------------------------------------------------------------------------- |
| 255 | // A simple, one-shot timer. See usage notes at the top of the file. |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 256 | class OneShotTimer : public BaseTimerMethodPointer { |
| 257 | public: |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 258 | OneShotTimer() : OneShotTimer(nullptr) {} |
| 259 | explicit OneShotTimer(TickClock* tick_clock) |
| 260 | : BaseTimerMethodPointer(ONE_SHOT, tick_clock) {} |
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. |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 265 | class RepeatingTimer : public BaseTimerMethodPointer { |
| 266 | public: |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 267 | RepeatingTimer() : RepeatingTimer(nullptr) {} |
| 268 | explicit RepeatingTimer(TickClock* tick_clock) |
| 269 | : BaseTimerMethodPointer(REPEATING, tick_clock) {} |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 270 | }; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 271 | |
| 272 | //----------------------------------------------------------------------------- |
| 273 | // 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^] | 274 | // calling Reset otherwise it will call the given method on the sequence it was |
| 275 | // initially Reset() from. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 276 | // |
| 277 | // Once created, it is inactive until Reset is called. Once |delay| seconds have |
| 278 | // passed since the last call to Reset, the callback is made. Once the callback |
| 279 | // has been made, it's inactive until Reset is called again. |
| 280 | // |
| 281 | // If destroyed, the timeout is canceled and will not occur even if already |
| 282 | // inflight. |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 283 | class DelayTimer : protected Timer { |
| 284 | public: |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 285 | template <class Receiver> |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 286 | DelayTimer(const tracked_objects::Location& posted_from, |
| 287 | TimeDelta delay, |
| 288 | Receiver* receiver, |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 289 | void (Receiver::*method)()) |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 290 | : DelayTimer(posted_from, delay, receiver, method, nullptr) {} |
| 291 | |
| 292 | template <class Receiver> |
| 293 | DelayTimer(const tracked_objects::Location& posted_from, |
| 294 | TimeDelta delay, |
| 295 | Receiver* receiver, |
| 296 | void (Receiver::*method)(), |
| 297 | TickClock* tick_clock) |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 298 | : Timer(posted_from, |
| 299 | delay, |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 300 | base::Bind(method, base::Unretained(receiver)), |
jameswest | 6e0c8d2 | 2016-11-15 05:19:54 | [diff] [blame] | 301 | false, |
| 302 | tick_clock) {} |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 303 | |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 304 | void Reset() override; |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 305 | }; |
| 306 | |
danakj | 8c3eb80 | 2015-09-24 07:53:00 | [diff] [blame] | 307 | // This class has a templated method so it can not be exported without failing |
| 308 | // to link in MSVC. But clang-plugin does not allow inline definitions of |
| 309 | // virtual methods, so the inline definition lives in the header file here |
| 310 | // to satisfy both. |
| 311 | inline void DelayTimer::Reset() { |
| 312 | Timer::Reset(); |
| 313 | } |
| 314 | |
[email protected] | 89bf27e | 2013-06-27 18:04:56 | [diff] [blame] | 315 | } // namespace base |
| 316 | |
| 317 | #endif // BASE_TIMER_TIMER_H_ |