forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtimer.h
392 lines (319 loc) · 14.1 KB
/
timer.h
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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
// 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, RepeatingTimer and RetainingOneShotTimer 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.
// RetainingOneShotTimer doesn't repeat the task itself like RepeatingTimer, but
// retains the given task after the time out. You can restart it with Reset
// again without giving new task to Start.
//
// All of OneShotTimer, RepeatingTimer and RetainingOneShotTimer 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
// timer 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 timer_;
// };
//
// Timers 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.
//
// These APIs are not thread safe. When a method is called (except the
// constructor), all further method calls must be on the same sequence until
// Stop().
//
// By default, the scheduled tasks will be run on the same sequence that the
// Timer was *started on*. To mock time in unit tests, some old tests used
// SetTaskRunner() to schedule the delay on a test-controlled TaskRunner. The
// modern and preferred approach to mock time is to use TaskEnvironment's
// MOCK_TIME mode.
#ifndef BASE_TIMER_TIMER_H_
#define BASE_TIMER_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 <memory>
#include "base/base_export.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/sequence_checker_impl.h"
#include "base/sequenced_task_runner.h"
#include "base/time/time.h"
namespace base {
class TickClock;
namespace internal {
class BaseTimerTaskInternal;
//-----------------------------------------------------------------------------
// This class wraps TaskRunner::PostDelayedTask to manage delayed and repeating
// tasks. See meta comment above for thread-safety requirements.
// Do not use this class directly. Use one of OneShotTimer, RepeatingTimer or
// RetainingOneShotTimer.
//
class BASE_EXPORT TimerBase {
public:
// Constructs a timer. Start must be called later to set task info.
// If |tick_clock| is provided, it is used instead of TimeTicks::Now() to get
// TimeTicks when scheduling tasks.
TimerBase();
explicit TimerBase(const TickClock* tick_clock);
// Construct a timer with task info.
// If |tick_clock| is provided, it is used instead of TimeTicks::Now() to get
// TimeTicks when scheduling tasks.
TimerBase(const Location& posted_from, TimeDelta delay);
TimerBase(const Location& posted_from,
TimeDelta delay,
const TickClock* tick_clock);
virtual ~TimerBase();
// Returns true if the timer is running (i.e., not stopped).
bool IsRunning() const;
// Returns the current delay for this timer.
TimeDelta GetCurrentDelay() const;
// Sets the task runner on which the delayed task should be scheduled when
// this Timer is running. This method can only be called while this Timer
// isn't running. This is an alternative (old) approach to mock time in tests.
// The modern and preferred approach is to use
// TaskEnvironment::TimeSource::MOCK_TIME. To avoid racy usage of Timer,
// |task_runner| must run tasks on the same sequence which this Timer is bound
// to (started from). TODO(gab): Migrate all callers to
// TaskEnvironment::TimeSource::MOCK_TIME.
virtual void SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner);
// Call this method to stop and cancel the timer. It is a no-op if the timer
// is not running.
virtual void Stop();
// Stop running task (if any) and abandon scheduled task (if any).
void AbandonAndStop() {
AbandonScheduledTask();
Stop();
// No more member accesses here: |this| could be deleted at this point.
}
// 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.
virtual void Reset();
const TimeTicks& desired_run_time() const { return desired_run_time_; }
protected:
virtual void OnStop() = 0;
virtual void RunUserTask() = 0;
// Returns the current tick count.
TimeTicks Now() const;
void set_desired_run_time(TimeTicks desired) { desired_run_time_ = desired; }
void set_is_running(bool running) { is_running_ = running; }
const Location& posted_from() const { return posted_from_; }
// The task runner on which the task should be scheduled. If it is null, the
// task runner for the current sequence will be used.
scoped_refptr<SequencedTaskRunner> task_runner_;
// Timer isn't thread-safe and must only be used on its origin sequence
// (sequence on which it was started). Once fully Stop()'ed it may be
// destroyed or restarted on another sequence.
SequenceChecker origin_sequence_checker_;
// Allocates a new |scheduled_task_| and posts it on the current sequence 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);
void StartInternal(const Location& posted_from, TimeDelta delay);
private:
friend class BaseTimerTaskInternal;
// Returns the task runner on which the task should be scheduled. If the
// corresponding |task_runner_| field is null, the task runner for the current
// sequence is returned.
scoped_refptr<SequencedTaskRunner> GetTaskRunner();
// Disable |scheduled_task_| and abandon it so that it no longer refers back
// to this object.
void AbandonScheduledTask();
// Called by BaseTimerTaskInternal when the delayed task fires.
void RunScheduledTask();
// When non-null, the |scheduled_task_| was posted to call RunScheduledTask()
// at |scheduled_run_time_|.
BaseTimerTaskInternal* scheduled_task_;
// Location in user code.
Location posted_from_;
// Delay requested by user.
TimeDelta delay_;
// The time at which |scheduled_task_| is expected to fire. This time can be a
// "zero" TimeTicks if the task must be run immediately.
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 delayed queues with orphaned tasks when the user code
// excessively Stops and Starts the timer. This time can be a "zero" TimeTicks
// if the task must be run immediately.
TimeTicks desired_run_time_;
// The tick clock used to calculate the run time for scheduled tasks.
const TickClock* const tick_clock_;
// If true, |user_task_| is scheduled to run sometime in the future.
bool is_running_;
DISALLOW_COPY_AND_ASSIGN(TimerBase);
};
} // namespace internal
//-----------------------------------------------------------------------------
// A simple, one-shot timer. See usage notes at the top of the file.
class BASE_EXPORT OneShotTimer : public internal::TimerBase {
public:
OneShotTimer();
explicit OneShotTimer(const TickClock* tick_clock);
~OneShotTimer() override;
// 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|.
virtual void Start(const Location& posted_from,
TimeDelta delay,
OnceClosure user_task);
// 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
// |receiver->*method|.
template <class Receiver>
void Start(const Location& posted_from,
TimeDelta delay,
Receiver* receiver,
void (Receiver::*method)()) {
Start(posted_from, delay, BindOnce(method, Unretained(receiver)));
}
// Run the scheduled task immediately, and stop the timer. The timer needs to
// be running.
void FireNow();
private:
void OnStop() final;
void RunUserTask() final;
OnceClosure user_task_;
DISALLOW_COPY_AND_ASSIGN(OneShotTimer);
};
//-----------------------------------------------------------------------------
// A simple, repeating timer. See usage notes at the top of the file.
class BASE_EXPORT RepeatingTimer : public internal::TimerBase {
public:
RepeatingTimer();
explicit RepeatingTimer(const TickClock* tick_clock);
~RepeatingTimer() override;
RepeatingTimer(const Location& posted_from,
TimeDelta delay,
RepeatingClosure user_task);
RepeatingTimer(const Location& posted_from,
TimeDelta delay,
RepeatingClosure user_task,
const TickClock* tick_clock);
// 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|.
virtual void Start(const Location& posted_from,
TimeDelta delay,
RepeatingClosure user_task);
// 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
// |receiver->*method|.
template <class Receiver>
void Start(const Location& posted_from,
TimeDelta delay,
Receiver* receiver,
void (Receiver::*method)()) {
Start(posted_from, delay, BindRepeating(method, Unretained(receiver)));
}
const RepeatingClosure& user_task() const { return user_task_; }
private:
// Mark this final, so that the destructor can call this safely.
void OnStop() final;
void RunUserTask() override;
RepeatingClosure user_task_;
DISALLOW_COPY_AND_ASSIGN(RepeatingTimer);
};
//-----------------------------------------------------------------------------
// A simple, one-shot timer with the retained user_task which is reused for
// multiple invocations of Start(). See usage notes at the top of the file.
class BASE_EXPORT RetainingOneShotTimer : public internal::TimerBase {
public:
RetainingOneShotTimer();
explicit RetainingOneShotTimer(const TickClock* tick_clock);
~RetainingOneShotTimer() override;
RetainingOneShotTimer(const Location& posted_from,
TimeDelta delay,
RepeatingClosure user_task);
RetainingOneShotTimer(const Location& posted_from,
TimeDelta delay,
RepeatingClosure user_task,
const TickClock* tick_clock);
// 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|.
virtual void Start(const Location& posted_from,
TimeDelta delay,
RepeatingClosure user_task);
// 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
// |receiver->*method|.
template <class Receiver>
void Start(const Location& posted_from,
TimeDelta delay,
Receiver* receiver,
void (Receiver::*method)()) {
Start(posted_from, delay, BindRepeating(method, Unretained(receiver)));
}
const RepeatingClosure& user_task() const { return user_task_; }
protected:
void set_user_task(const RepeatingClosure& task) { user_task_ = task; }
private:
// Mark this final, so that the destructor can call this safely.
void OnStop() final;
void RunUserTask() override;
RepeatingClosure user_task_;
DISALLOW_COPY_AND_ASSIGN(RetainingOneShotTimer);
};
//-----------------------------------------------------------------------------
// A Delay timer is like The Button from Lost. Once started, you have to keep
// calling Reset otherwise it will call the given method on the sequence it was
// initially Reset() from.
//
// 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.
class DelayTimer {
public:
template <class Receiver>
DelayTimer(const Location& posted_from,
TimeDelta delay,
Receiver* receiver,
void (Receiver::*method)())
: DelayTimer(posted_from, delay, receiver, method, nullptr) {}
template <class Receiver>
DelayTimer(const Location& posted_from,
TimeDelta delay,
Receiver* receiver,
void (Receiver::*method)(),
const TickClock* tick_clock)
: timer_(posted_from,
delay,
BindRepeating(method, Unretained(receiver)),
tick_clock) {}
void Reset() { timer_.Reset(); }
private:
RetainingOneShotTimer timer_;
DISALLOW_COPY_AND_ASSIGN(DelayTimer);
};
} // namespace base
#endif // BASE_TIMER_TIMER_H_