summaryrefslogtreecommitdiffstats
path: root/base/timer.h
blob: 95cae12b1d455a255e21ebe968df1d4763c0b84d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// OneShotTimer and RepeatingTimer provide a simple timer API.  As the names
// suggest, OneShotTimer calls you back once after a time delay expires.
// RepeatingTimer on the other hand calls you back periodically with the
// prescribed time interval.
//
// OneShotTimer and RepeatingTimer both cancel the timer when they go out of
// scope, which makes it easy to ensure that you do not get called when your
// object has gone out of scope.  Just instantiate a OneShotTimer or
// RepeatingTimer as a member variable of the class for which you wish to
// receive timer events.
//
// Sample RepeatingTimer usage:
//
//   class MyClass {
//    public:
//     void StartDoingStuff() {
//       timer_.Start(FROM_HERE, TimeDelta::FromSeconds(1),
//                    this, &MyClass::DoStuff);
//     }
//     void StopDoingStuff() {
//       timer_.Stop();
//     }
//    private:
//     void DoStuff() {
//       // This method is called every second to do stuff.
//       ...
//     }
//     base::RepeatingTimer<MyClass> timer_;
//   };
//
// Both OneShotTimer and RepeatingTimer also support a Reset method, which
// allows you to easily defer the timer event until the timer delay passes once
// again.  So, in the above example, if 0.5 seconds have already passed,
// calling Reset on timer_ would postpone DoStuff by another 1 second.  In
// other words, Reset is shorthand for calling Stop and then Start again with
// the same arguments.
//
// NOTE: These APIs are not thread safe. Always call from the same thread.

#ifndef BASE_TIMER_H_
#define BASE_TIMER_H_

// IMPORTANT: If you change timer code, make sure that all tests (including
// disabled ones) from timer_unittests.cc pass locally. Some are disabled
// because they're flaky on the buildbot, but when you run them locally you
// should be able to tell the difference.

#include "base/base_export.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/time.h"

class MessageLoop;

namespace base {

class BaseTimerTaskInternal;

//-----------------------------------------------------------------------------
// This class wraps MessageLoop::PostDelayedTask to manage delayed and repeating
// tasks. It must be destructed on the same thread that starts tasks. There are
// DCHECKs in place to verify this.
//
class BASE_EXPORT Timer {
 public:
  // Construct a timer in repeating or one-shot mode. Start or SetTaskInfo must
  // be called later to set task info. |retain_user_task| determines whether the
  // user_task is retained or reset when it runs or stops.
  Timer(bool retain_user_task, bool is_repeating);

  // Construct a timer with retained task info.
  Timer(const tracked_objects::Location& posted_from,
        TimeDelta delay,
        const base::Closure& user_task,
        bool is_repeating);

  virtual ~Timer();

  // Returns true if the timer is running (i.e., not stopped).
  bool IsRunning() const {
    return is_running_;
  }

  // Returns the current delay for this timer.
  TimeDelta GetCurrentDelay() const {
    return delay_;
  }

  // Start the timer to run at the given |delay| from now. If the timer is
  // already running, it will be replaced to call the given |user_task|.
  void Start(const tracked_objects::Location& posted_from,
             TimeDelta delay,
             const base::Closure& user_task);

  // Call this method to stop and cancel the timer.  It is a no-op if the timer
  // is not running.
  void Stop();

  // Call this method to reset the timer delay. The user_task_ must be set. If
  // the timer is not running, this will start it by posting a task.
  void Reset();

  const base::Closure& user_task() const { return user_task_; }
  const TimeTicks& desired_run_time() const { return desired_run_time_; }

 protected:
  // Used to initiate a new delayed task.  This has the side-effect of disabling
  // scheduled_task_ if it is non-null.
  void SetTaskInfo(const tracked_objects::Location& posted_from,
                   TimeDelta delay,
                   const base::Closure& user_task);

 private:
  friend class BaseTimerTaskInternal;

  // Allocates a new scheduled_task_ and posts it on the current MessageLoop
  // with the given |delay|. scheduled_task_ must be NULL. scheduled_run_time_
  // and desired_run_time_ are reset to Now() + delay.
  void PostNewScheduledTask(TimeDelta delay);

  // Disable scheduled_task_ and abandon it so that it no longer refers back to
  // this object.
  void AbandonScheduledTask();

  // Called by BaseTimerTaskInternal when the MessageLoop runs it.
  void RunScheduledTask();

  // Stop running task (if any) and abandon scheduled task (if any).
  void StopAndAbandon() {
    Stop();
    AbandonScheduledTask();
  }

  // When non-NULL, the scheduled_task_ is waiting in the MessageLoop to call
  // RunScheduledTask() at scheduled_run_time_.
  BaseTimerTaskInternal* scheduled_task_;

  // Location in user code.
  tracked_objects::Location posted_from_;
  // Delay requested by user.
  TimeDelta delay_;
  // user_task_ is what the user wants to be run at desired_run_time_.
  base::Closure user_task_;

  // The estimated time that the MessageLoop will run the scheduled_task_ that
  // will call RunScheduledTask().
  TimeTicks scheduled_run_time_;

  // The desired run time of user_task_. The user may update this at any time,
  // even if their previous request has not run yet. If desired_run_time_ is
  // greater than scheduled_run_time_, a continuation task will be posted to
  // wait for the remaining time. This allows us to reuse the pending task so as
  // not to flood the MessageLoop with orphaned tasks when the user code
  // excessively Stops and Starts the timer.
  TimeTicks desired_run_time_;

  // Thread ID of current MessageLoop for verifying single-threaded usage.
  int thread_id_;

  // Repeating timers automatically post the task again before calling the task
  // callback.
  const bool is_repeating_;

  // If true, hold on to the user_task_ closure object for reuse.
  const bool retain_user_task_;

  // If true, user_task_ is scheduled to run sometime in the future.
  bool is_running_;

  DISALLOW_COPY_AND_ASSIGN(Timer);
};

//-----------------------------------------------------------------------------
// This class is an implementation detail of OneShotTimer and RepeatingTimer.
// Please do not use this class directly.
template <class Receiver, bool kIsRepeating>
class BaseTimerMethodPointer : public Timer {
 public:
  typedef void (Receiver::*ReceiverMethod)();

  // This is here to work around the fact that Timer::Start is "hidden" by the
  // Start definition below, rather than being overloaded.
  // TODO(tim): We should remove uses of BaseTimerMethodPointer::Start below
  // and convert callers to use the base::Closure version in Timer::Start,
  // see bug 148832.
  using Timer::Start;

  BaseTimerMethodPointer() : Timer(kIsRepeating, kIsRepeating) {}

  // Start the timer to run at the given |delay| from now. If the timer is
  // already running, it will be replaced to call a task formed from
  // |reviewer->*method|.
  void Start(const tracked_objects::Location& posted_from,
             TimeDelta delay,
             Receiver* receiver,
             ReceiverMethod method) {
    Timer::Start(posted_from, delay,
                 base::Bind(method, base::Unretained(receiver)));
  }
};

//-----------------------------------------------------------------------------
// A simple, one-shot timer.  See usage notes at the top of the file.
template <class Receiver>
class OneShotTimer : public BaseTimerMethodPointer<Receiver, false> {};

//-----------------------------------------------------------------------------
// A simple, repeating timer.  See usage notes at the top of the file.
template <class Receiver>
class RepeatingTimer : public BaseTimerMethodPointer<Receiver, true> {};

//-----------------------------------------------------------------------------
// A Delay timer is like The Button from Lost. Once started, you have to keep
// calling Reset otherwise it will call the given method in the MessageLoop
// thread.
//
// Once created, it is inactive until Reset is called. Once |delay| seconds have
// passed since the last call to Reset, the callback is made. Once the callback
// has been made, it's inactive until Reset is called again.
//
// If destroyed, the timeout is canceled and will not occur even if already
// inflight.
template <class Receiver>
class DelayTimer : protected Timer {
 public:
  typedef void (Receiver::*ReceiverMethod)();

  DelayTimer(const tracked_objects::Location& posted_from,
             TimeDelta delay,
             Receiver* receiver,
             ReceiverMethod method)
      : Timer(posted_from, delay,
              base::Bind(method, base::Unretained(receiver)),
              false) {}

  void Reset() { Timer::Reset(); }
};

}  // namespace base

#endif  // BASE_TIMER_H_