Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(534)

Side by Side Diff: components/timers/alarm_timer.cc

Issue 706993003: C++ readability review (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address comments Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/timers/alarm_timer.h ('k') | components/timers/alarm_timer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/timers/alarm_timer.h" 5 #include "components/timers/alarm_timer.h"
6 6
7 #include <sys/timerfd.h>
8
7 #include "base/bind.h" 9 #include "base/bind.h"
8 #include "base/bind_helpers.h" 10 #include "base/bind_helpers.h"
9 #include "base/files/file_util.h" 11 #include "base/files/file_util.h"
12 #include "base/lazy_instance.h"
10 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/pending_task.h" 17 #include "base/pending_task.h"
12 #include "components/timers/rtc_alarm.h" 18 #include "base/threading/thread.h"
13 19
14 namespace timers { 20 namespace timers {
21 namespace {
22 // This class represents the IO thread that the AlarmTimer::Delegate may use for
23 // watching file descriptors if it gets called from a thread that does not have
24 // a MessageLoopForIO. It is a lazy global instance because it may not always
25 // be necessary.
26 class RtcAlarmIOThread : public base::Thread {
27 public:
28 RtcAlarmIOThread() : Thread("RTC Alarm IO Thread") {
29 CHECK(
30 StartWithOptions(base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
31 }
32 ~RtcAlarmIOThread() override { Stop(); }
33 };
34
35 base::LazyInstance<RtcAlarmIOThread> g_io_thread = LAZY_INSTANCE_INITIALIZER;
36
37 } // namespace
38
39 // Watches a MessageLoop and runs a callback if that MessageLoop will be
40 // destroyed.
41 class AlarmTimer::MessageLoopObserver
42 : public base::MessageLoop::DestructionObserver {
43 public:
44 // Constructs a MessageLoopObserver that will observe |message_loop| and will
45 // call |on_will_be_destroyed_callback| when |message_loop| is about to be
46 // destroyed.
47 MessageLoopObserver(base::MessageLoop* message_loop,
48 base::Closure on_will_be_destroyed_callback)
49 : message_loop_(message_loop),
50 on_will_be_destroyed_callback_(on_will_be_destroyed_callback) {
51 DCHECK(message_loop_);
52 message_loop_->AddDestructionObserver(this);
53 }
54
55 ~MessageLoopObserver() override {
56 // If |message_loop_| was destroyed, then this class will have already
57 // unregistered itself. Doing it again will trigger a warning.
58 if (message_loop_)
59 message_loop_->RemoveDestructionObserver(this);
60 }
61
62 // base::MessageLoop::DestructionObserver override.
63 void WillDestroyCurrentMessageLoop() override {
64 message_loop_->RemoveDestructionObserver(this);
65 message_loop_ = NULL;
66
67 on_will_be_destroyed_callback_.Run();
68 }
69
70 private:
71 // The MessageLoop that this class should watch. Is a weak pointer.
72 base::MessageLoop* message_loop_;
73
74 // The callback to run when |message_loop_| will be destroyed.
75 base::Closure on_will_be_destroyed_callback_;
76
77 DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver);
78 };
79
80 // This class manages a Real Time Clock (RTC) alarm, a feature that is available
81 // from linux version 3.11 onwards. It creates a file descriptor for the RTC
82 // alarm timer and then watches that file descriptor to see when it can be read
83 // without blocking, indicating that the timer has fired.
84 //
85 // A major problem for this class is that watching file descriptors is only
86 // available on a MessageLoopForIO but there is no guarantee the timer is going
87 // to be created on one. To get around this, the timer has a dedicated thread
88 // with a MessageLoopForIO that posts tasks back to the thread that started the
89 // timer.
90 class AlarmTimer::Delegate
91 : public base::RefCountedThreadSafe<AlarmTimer::Delegate>,
92 public base::MessageLoopForIO::Watcher {
93 public:
94 // Construct a Delegate for the AlarmTimer. |parent| should be a valid
95 // WeakPtr to the parent AlarmTimer.
96 explicit Delegate(base::Closure on_timer_fired_callback);
97
98 // Returns true if the system timer managed by this delegate is capable of
99 // waking the system from suspend.
100 bool CanWakeFromSuspend();
101
102 // Resets the timer to fire after |delay| has passed. Cancels any
103 // pre-existing delay.
104 void Reset(base::TimeDelta delay);
105
106 // Stops the currently running timer. It should be safe to call this even if
107 // the timer is not running.
108 void Stop();
109
110 // base::MessageLoopForIO::Watcher overrides.
111 void OnFileCanReadWithoutBlocking(int fd) override;
112 void OnFileCanWriteWithoutBlocking(int fd) override;
113
114 private:
115 friend class base::RefCountedThreadSafe<Delegate>;
116 ~Delegate() override;
117
118 // Actually performs the system calls to set up the timer. This must be
119 // called on a MessageLoopForIO.
120 void ResetImpl(base::TimeDelta delay, int reset_sequence_number);
121
122 // Callback that is run when the timer fires. Must be run on
123 // |origin_message_loop_|.
124 void OnTimerFired(int reset_sequence_number);
125
126 // File descriptor associated with the alarm timer.
127 int alarm_fd_;
128
129 // Message loop which initially started the timer.
130 scoped_refptr<base::MessageLoopProxy> origin_message_loop_;
131
132 // Callback that should be run when the timer fires.
133 base::Closure on_timer_fired_callback_;
134
135 // Manages watching file descriptors.
136 scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> fd_watcher_;
137
138 // The sequence numbers of the last Reset() call handled respectively on
139 // |origin_message_loop_| and on the MessageLoopForIO used for watching the
140 // timer file descriptor. Note that these can be the same MessageLoop.
141 // OnTimerFired() runs |on_timer_fired_callback_| only if the sequence number
142 // it receives from the MessageLoopForIO matches
143 // |origin_reset_sequence_number_|.
144 int origin_reset_sequence_number_;
145 int io_reset_sequence_number_;
146
147 DISALLOW_COPY_AND_ASSIGN(Delegate);
148 };
149
150 // Construct a Delegate for the AlarmTimer. |parent| should be a valid
151 // WeakPtr to the parent AlarmTimer.
152 AlarmTimer::Delegate::Delegate(base::Closure on_timer_fired_callback)
153 : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
154 on_timer_fired_callback_(on_timer_fired_callback),
155 origin_reset_sequence_number_(0),
156 io_reset_sequence_number_(0) {
157 }
158
159 AlarmTimer::Delegate::~Delegate() {
160 if (alarm_fd_ != -1)
161 close(alarm_fd_);
162 }
163
164 bool AlarmTimer::Delegate::CanWakeFromSuspend() {
165 return alarm_fd_ != -1;
166 }
167
168 void AlarmTimer::Delegate::Reset(base::TimeDelta delay) {
169 // Get a proxy for the current message loop. When the timer fires, we will
170 // post tasks to this proxy to let the parent timer know.
171 origin_message_loop_ = base::MessageLoopProxy::current();
172
173 // Increment the sequence number. Used to invalidate any events that have
174 // been queued but not yet run since the last time Reset() was called.
175 origin_reset_sequence_number_++;
176
177 // Calling timerfd_settime with a zero delay actually clears the timer so if
178 // the user has requested a zero delay timer, we need to handle it
179 // differently. We queue the task here but we still go ahead and call
180 // timerfd_settime with the zero delay anyway to cancel any previous delay
181 // that might have been programmed.
182 if (delay <= base::TimeDelta::FromMicroseconds(0)) {
183 // The timerfd_settime documentation is vague on what happens when it is
184 // passed a negative delay. We can sidestep the issue by ensuring that
185 // the delay is 0.
186 delay = base::TimeDelta::FromMicroseconds(0);
187 origin_message_loop_->PostTask(
188 FROM_HERE,
189 base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
190 origin_reset_sequence_number_));
191 }
192
193 // Run ResetImpl() on a MessageLoopForIO.
194 if (base::MessageLoopForIO::IsCurrent()) {
195 ResetImpl(delay, origin_reset_sequence_number_);
196 } else {
197 g_io_thread.Pointer()->task_runner()->PostTask(
198 FROM_HERE,
199 base::Bind(&Delegate::ResetImpl, scoped_refptr<Delegate>(this), delay,
200 origin_reset_sequence_number_));
201 }
202 }
203
204 void AlarmTimer::Delegate::Stop() {
205 // Stop the RTC from a MessageLoopForIO.
206 if (!base::MessageLoopForIO::IsCurrent()) {
207 g_io_thread.Pointer()->task_runner()->PostTask(
208 FROM_HERE, base::Bind(&Delegate::Stop, scoped_refptr<Delegate>(this)));
209 return;
210 }
211
212 // Stop watching for events.
213 fd_watcher_.reset();
214
215 // Now clear the timer.
216 DCHECK_NE(alarm_fd_, -1);
217 itimerspec blank_time = {};
218 if (timerfd_settime(alarm_fd_, 0, &blank_time, NULL) < 0)
219 PLOG(ERROR) << "Unable to clear alarm time. Timer may still fire.";
220 }
221
222 void AlarmTimer::Delegate::OnFileCanReadWithoutBlocking(int fd) {
223 DCHECK_EQ(alarm_fd_, fd);
224
225 // Read from the fd to ack the event.
226 char val[sizeof(uint64_t)];
227 const bool result = base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t));
228 DCHECK(result) << "Unable to read from timer file descriptor.";
Daniel Erat 2015/03/14 14:02:33 PLOG(DFATAL) is probably better here (FATAL in deb
Chirantan Ekbote 2015/04/07 01:16:06 Done.
229
230 // Make sure that the parent timer is informed on the proper message loop.
231 if (origin_message_loop_->RunsTasksOnCurrentThread()) {
232 OnTimerFired(io_reset_sequence_number_);
233 } else {
234 origin_message_loop_->PostTask(
235 FROM_HERE,
236 base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
237 io_reset_sequence_number_));
238 }
239 }
240
241 void AlarmTimer::Delegate::OnFileCanWriteWithoutBlocking(int fd) {
242 NOTREACHED();
243 }
244
245 void AlarmTimer::Delegate::ResetImpl(base::TimeDelta delay,
246 int reset_sequence_number) {
247 DCHECK(base::MessageLoopForIO::IsCurrent());
248 DCHECK_NE(alarm_fd_, -1);
249
250 // Store the sequence number in the IO thread variable. When the timer
251 // fires, we will bind this value to the OnTimerFired callback to ensure
252 // that we do the right thing if the timer gets reset.
253 io_reset_sequence_number_ = reset_sequence_number;
254
255 // If we were already watching the fd, this will stop watching it.
256 fd_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
257
258 // Start watching the fd to see when the timer fires.
259 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
260 alarm_fd_, false, base::MessageLoopForIO::WATCH_READ,
261 fd_watcher_.get(), this)) {
262 LOG(ERROR) << "Error while attempting to watch file descriptor for RTC "
263 << "alarm. Timer will not fire.";
264 }
265
266 // Actually set the timer. This will also clear the pre-existing timer, if
267 // any.
268 itimerspec alarm_time = {};
269 alarm_time.it_value.tv_sec = delay.InSeconds();
270 alarm_time.it_value.tv_nsec =
271 (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) *
272 base::Time::kNanosecondsPerMicrosecond;
273 if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0)
274 PLOG(ERROR) << "Error while setting alarm time. Timer will not fire";
275 }
276
277 void AlarmTimer::Delegate::OnTimerFired(int reset_sequence_number) {
278 DCHECK(origin_message_loop_->RunsTasksOnCurrentThread());
279
280 // Check to make sure that the timer was not reset in the time between when
281 // this task was queued to run and now. If it was reset, then don't do
282 // anything.
283 if (reset_sequence_number != origin_reset_sequence_number_)
284 return;
285
286 on_timer_fired_callback_.Run();
287 }
15 288
16 AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating) 289 AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating)
17 : base::Timer(retain_user_task, is_repeating), 290 : base::Timer(retain_user_task, is_repeating),
18 delegate_(new RtcAlarm()),
19 can_wake_from_suspend_(false), 291 can_wake_from_suspend_(false),
20 origin_message_loop_(NULL), 292 origin_message_loop_(NULL),
21 weak_factory_(this) { 293 weak_factory_(this) {
22 can_wake_from_suspend_ = delegate_->Init(weak_factory_.GetWeakPtr()); 294 Init();
23 } 295 }
24 296
25 AlarmTimer::AlarmTimer(const tracked_objects::Location& posted_from, 297 AlarmTimer::AlarmTimer(const tracked_objects::Location& posted_from,
26 base::TimeDelta delay, 298 base::TimeDelta delay,
27 const base::Closure& user_task, 299 const base::Closure& user_task,
28 bool is_repeating) 300 bool is_repeating)
29 : base::Timer(posted_from, delay, user_task, is_repeating), 301 : base::Timer(posted_from, delay, user_task, is_repeating),
30 delegate_(new RtcAlarm()),
31 can_wake_from_suspend_(false), 302 can_wake_from_suspend_(false),
32 origin_message_loop_(NULL), 303 origin_message_loop_(NULL),
33 weak_factory_(this) { 304 weak_factory_(this) {
34 can_wake_from_suspend_ = delegate_->Init(weak_factory_.GetWeakPtr()); 305 Init();
35 } 306 }
36 307
37 AlarmTimer::~AlarmTimer() { 308 AlarmTimer::~AlarmTimer() {
38 Stop(); 309 Stop();
39 } 310 }
40 311
41 void AlarmTimer::OnTimerFired() { 312 void AlarmTimer::Init() {
42 if (!base::Timer::IsRunning()) 313 delegate_ = make_scoped_refptr(new AlarmTimer::Delegate(
43 return; 314 base::Bind(&AlarmTimer::OnTimerFired, weak_factory_.GetWeakPtr())));
44 315 can_wake_from_suspend_ = delegate_->CanWakeFromSuspend();
45 DCHECK(pending_task_.get());
46
47 // Take ownership of the pending user task, which is going to be cleared by
48 // the Stop() or Reset() functions below.
49 scoped_ptr<base::PendingTask> pending_user_task(pending_task_.Pass());
50
51 // Re-schedule or stop the timer as requested.
52 if (base::Timer::is_repeating())
53 Reset();
54 else
55 Stop();
56
57 // Now run the user task.
58 base::MessageLoop::current()->task_annotator()->RunTask(
59 "AlarmTimer::Reset", "AlarmTimer::OnTimerFired", *pending_user_task);
60 } 316 }
61 317
62 void AlarmTimer::Stop() { 318 void AlarmTimer::Stop() {
63 if (!can_wake_from_suspend_) { 319 if (!can_wake_from_suspend_) {
64 base::Timer::Stop(); 320 base::Timer::Stop();
65 return; 321 return;
66 } 322 }
67 323
68 // Clear the running flag, stop the delegate, and delete the pending task. 324 // Clear the running flag, stop the delegate, and delete the pending task.
69 base::Timer::set_is_running(false); 325 base::Timer::set_is_running(false);
70 delegate_->Stop(); 326 delegate_->Stop();
71 pending_task_.reset(); 327 pending_task_.reset();
72 328
73 // Stop is called when the AlarmTimer is destroyed so we need to remove 329 // Stop watching |origin_message_loop_|.
74 // ourselves as a MessageLoop::DestructionObserver to prevent a segfault 330 origin_message_loop_ = NULL;
75 // later. 331 message_loop_observer_.reset();
76 if (origin_message_loop_) {
77 origin_message_loop_->RemoveDestructionObserver(this);
78 origin_message_loop_ = NULL;
79 }
80 332
81 if (!base::Timer::retain_user_task()) 333 if (!base::Timer::retain_user_task())
82 base::Timer::set_user_task(base::Closure()); 334 base::Timer::set_user_task(base::Closure());
83 } 335 }
84 336
85 void AlarmTimer::Reset() { 337 void AlarmTimer::Reset() {
86 if (!can_wake_from_suspend_) { 338 if (!can_wake_from_suspend_) {
87 base::Timer::Reset(); 339 base::Timer::Reset();
88 return; 340 return;
89 } 341 }
90 342
91 DCHECK(!base::Timer::user_task().is_null()); 343 DCHECK(!base::Timer::user_task().is_null());
92 DCHECK(!origin_message_loop_ || 344 DCHECK(!origin_message_loop_ ||
93 origin_message_loop_->task_runner()->RunsTasksOnCurrentThread()); 345 origin_message_loop_->task_runner()->RunsTasksOnCurrentThread());
94 346
95 // Make sure that the timer will stop if the underlying message loop is 347 // Make sure that the timer will stop if the underlying message loop is
96 // destroyed. 348 // destroyed.
97 if (!origin_message_loop_) { 349 if (!origin_message_loop_) {
98 origin_message_loop_ = base::MessageLoop::current(); 350 origin_message_loop_ = base::MessageLoop::current();
99 origin_message_loop_->AddDestructionObserver(this); 351 message_loop_observer_.reset(new MessageLoopObserver(
352 origin_message_loop_,
353 base::Bind(&AlarmTimer::WillDestroyCurrentMessageLoop,
354 weak_factory_.GetWeakPtr())));
100 } 355 }
101 356
102 // Set up the pending task. 357 // Set up the pending task.
103 if (base::Timer::GetCurrentDelay() > base::TimeDelta::FromMicroseconds(0)) { 358 if (base::Timer::GetCurrentDelay() > base::TimeDelta::FromMicroseconds(0)) {
104 base::Timer::set_desired_run_time( 359 base::Timer::set_desired_run_time(base::TimeTicks::Now() +
105 base::TimeTicks::Now() + base::Timer::GetCurrentDelay()); 360 base::Timer::GetCurrentDelay());
106 pending_task_.reset(new base::PendingTask(base::Timer::posted_from(), 361 pending_task_.reset(new base::PendingTask(
107 base::Timer::user_task(), 362 base::Timer::posted_from(), base::Timer::user_task(),
108 base::Timer::desired_run_time(), 363 base::Timer::desired_run_time(), true /* nestable */));
109 true /* nestable */));
110 } else { 364 } else {
111 base::Timer::set_desired_run_time(base::TimeTicks()); 365 base::Timer::set_desired_run_time(base::TimeTicks());
112 pending_task_.reset(new base::PendingTask(base::Timer::posted_from(), 366 pending_task_.reset(new base::PendingTask(base::Timer::posted_from(),
113 base::Timer::user_task())); 367 base::Timer::user_task()));
114 } 368 }
115 base::MessageLoop::current()->task_annotator()->DidQueueTask( 369 base::MessageLoop::current()->task_annotator()->DidQueueTask(
116 "AlarmTimer::Reset", *pending_task_); 370 "AlarmTimer::Reset", *pending_task_);
117 371
118 // Now start up the timer. 372 // Now start up the timer.
119 delegate_->Reset(base::Timer::GetCurrentDelay()); 373 delegate_->Reset(base::Timer::GetCurrentDelay());
120 base::Timer::set_is_running(true); 374 base::Timer::set_is_running(true);
121 } 375 }
122 376
123 void AlarmTimer::WillDestroyCurrentMessageLoop() { 377 void AlarmTimer::WillDestroyCurrentMessageLoop() {
124 Stop(); 378 Stop();
125 } 379 }
126 380
381 void AlarmTimer::OnTimerFired() {
382 if (!base::Timer::IsRunning())
383 return;
384
385 DCHECK(pending_task_.get());
386
387 // Take ownership of the pending user task, which is going to be cleared by
388 // the Stop() or Reset() functions below.
389 scoped_ptr<base::PendingTask> pending_user_task(pending_task_.Pass());
390
391 // Re-schedule or stop the timer as requested.
392 if (base::Timer::is_repeating())
393 Reset();
394 else
395 Stop();
396
397 // Now run the user task.
398 base::MessageLoop::current()->task_annotator()->RunTask(
399 "AlarmTimer::Reset", "AlarmTimer::OnTimerFired", *pending_user_task);
400 }
401
402 OneShotAlarmTimer::OneShotAlarmTimer() : AlarmTimer(false, false) {
403 }
404
405 OneShotAlarmTimer::~OneShotAlarmTimer() {
406 }
407
408 RepeatingAlarmTimer::RepeatingAlarmTimer() : AlarmTimer(true, true) {
409 }
410
411 RepeatingAlarmTimer::RepeatingAlarmTimer(
412 const tracked_objects::Location& posted_from,
413 base::TimeDelta delay,
414 const base::Closure& user_task)
415 : AlarmTimer(posted_from, delay, user_task, true) {
416 }
417
418 RepeatingAlarmTimer::~RepeatingAlarmTimer() {
419 }
420
421 SimpleAlarmTimer::SimpleAlarmTimer() : AlarmTimer(true, false) {
422 }
423
424 SimpleAlarmTimer::SimpleAlarmTimer(const tracked_objects::Location& posted_from,
425 base::TimeDelta delay,
426 const base::Closure& user_task)
427 : AlarmTimer(posted_from, delay, user_task, false) {
428 }
429
430 SimpleAlarmTimer::~SimpleAlarmTimer() {
431 }
432
127 } // namespace timers 433 } // namespace timers
OLDNEW
« no previous file with comments | « components/timers/alarm_timer.h ('k') | components/timers/alarm_timer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698