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

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

Issue 706993003: C++ readability review (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address comments Created 5 years, 8 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_chromeos.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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "components/timers/alarm_timer_chromeos.h"
6
7 #include <sys/timerfd.h>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/files/file_util.h"
12 #include "base/lazy_instance.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"
17 #include "base/pending_task.h"
18 #include "base/threading/thread.h"
19
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. It should be safe to call
95 // |on_timer_fired_callback| multiple times.
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 // Sets a hook that will be called when the timer fires and a task has been
111 // queued on |origin_message_loop_|. Used by tests to wait until a task is
112 // pending in the MessageLoop.
113 void SetTimerFiredCallbackForTest(base::Closure test_callback);
114
115 // base::MessageLoopForIO::Watcher overrides.
116 void OnFileCanReadWithoutBlocking(int fd) override;
117 void OnFileCanWriteWithoutBlocking(int fd) override;
118
119 private:
120 friend class base::RefCountedThreadSafe<Delegate>;
121 ~Delegate() override;
122
123 // Actually performs the system calls to set up the timer. This must be
124 // called on a MessageLoopForIO.
125 void ResetImpl(base::TimeDelta delay, int reset_sequence_number);
126
127 // Callback that is run when the timer fires. Must be run on
128 // |origin_message_loop_|.
129 void OnTimerFired(int reset_sequence_number);
130
131 // File descriptor associated with the alarm timer.
132 int alarm_fd_;
133
134 // Message loop which initially started the timer.
135 scoped_refptr<base::MessageLoopProxy> origin_message_loop_;
136
137 // Callback that should be run when the timer fires.
138 base::Closure on_timer_fired_callback_;
139
140 // Hook used by tests to be notified when the timer has fired and a task has
141 // been queued in the MessageLoop.
142 base::Closure on_timer_fired_callback_for_test_;
143
144 // Manages watching file descriptors.
145 scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> fd_watcher_;
146
147 // The sequence numbers of the last Reset() call handled respectively on
148 // |origin_message_loop_| and on the MessageLoopForIO used for watching the
149 // timer file descriptor. Note that these can be the same MessageLoop.
150 // OnTimerFired() runs |on_timer_fired_callback_| only if the sequence number
151 // it receives from the MessageLoopForIO matches
152 // |origin_reset_sequence_number_|.
153 int origin_reset_sequence_number_;
154 int io_reset_sequence_number_;
155
156 DISALLOW_COPY_AND_ASSIGN(Delegate);
157 };
158
159 AlarmTimer::Delegate::Delegate(base::Closure on_timer_fired_callback)
160 : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
161 on_timer_fired_callback_(on_timer_fired_callback),
162 origin_reset_sequence_number_(0),
163 io_reset_sequence_number_(0) {
164 // The call to timerfd_create above may fail. This is the only indication
165 // that CLOCK_REALTIME_ALARM is not supported on this system.
166 DPLOG_IF(INFO, (alarm_fd_ == -1))
167 << "CLOCK_REALTIME_ALARM not supported on this system";
168 }
169
170 AlarmTimer::Delegate::~Delegate() {
171 if (alarm_fd_ != -1)
172 close(alarm_fd_);
173 }
174
175 bool AlarmTimer::Delegate::CanWakeFromSuspend() {
176 return alarm_fd_ != -1;
177 }
178
179 void AlarmTimer::Delegate::Reset(base::TimeDelta delay) {
180 // Get a proxy for the current message loop. When the timer fires, we will
181 // post tasks to this proxy to let the parent timer know.
182 origin_message_loop_ = base::MessageLoopProxy::current();
183
184 // Increment the sequence number. Used to invalidate any events that have
185 // been queued but not yet run since the last time Reset() was called.
186 origin_reset_sequence_number_++;
187
188 // Calling timerfd_settime with a zero delay actually clears the timer so if
189 // the user has requested a zero delay timer, we need to handle it
190 // differently. We queue the task here but we still go ahead and call
191 // timerfd_settime with the zero delay anyway to cancel any previous delay
192 // that might have been programmed.
193 if (delay <= base::TimeDelta::FromMicroseconds(0)) {
194 // The timerfd_settime documentation is vague on what happens when it is
195 // passed a negative delay. We can sidestep the issue by ensuring that
196 // the delay is 0.
197 delay = base::TimeDelta::FromMicroseconds(0);
198 origin_message_loop_->PostTask(
199 FROM_HERE,
200 base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
201 origin_reset_sequence_number_));
202 }
203
204 // Run ResetImpl() on a MessageLoopForIO.
205 if (base::MessageLoopForIO::IsCurrent()) {
206 ResetImpl(delay, origin_reset_sequence_number_);
207 } else {
208 g_io_thread.Pointer()->task_runner()->PostTask(
209 FROM_HERE,
210 base::Bind(&Delegate::ResetImpl, scoped_refptr<Delegate>(this), delay,
211 origin_reset_sequence_number_));
212 }
213 }
214
215 void AlarmTimer::Delegate::Stop() {
216 // Stop the RTC from a MessageLoopForIO.
217 if (!base::MessageLoopForIO::IsCurrent()) {
218 g_io_thread.Pointer()->task_runner()->PostTask(
219 FROM_HERE, base::Bind(&Delegate::Stop, scoped_refptr<Delegate>(this)));
220 return;
221 }
222
223 // Stop watching for events.
224 fd_watcher_.reset();
225
226 // Now clear the timer.
227 DCHECK_NE(alarm_fd_, -1);
228 itimerspec blank_time = {};
229 if (timerfd_settime(alarm_fd_, 0, &blank_time, NULL) < 0)
230 PLOG(ERROR) << "Unable to clear alarm time. Timer may still fire.";
231 }
232
233 void AlarmTimer::Delegate::OnFileCanReadWithoutBlocking(int fd) {
234 DCHECK_EQ(alarm_fd_, fd);
235
236 // Read from the fd to ack the event.
237 char val[sizeof(uint64_t)];
238 if (!base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t)))
239 PLOG(DFATAL) << "Unable to read from timer file descriptor.";
240
241 // Make sure that the parent timer is informed on the proper message loop.
242 if (origin_message_loop_->RunsTasksOnCurrentThread()) {
243 OnTimerFired(io_reset_sequence_number_);
244 } else {
245 origin_message_loop_->PostTask(
246 FROM_HERE,
247 base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
248 io_reset_sequence_number_));
249 }
250 }
251
252 void AlarmTimer::Delegate::OnFileCanWriteWithoutBlocking(int fd) {
253 NOTREACHED();
254 }
255
256 void AlarmTimer::Delegate::SetTimerFiredCallbackForTest(
257 base::Closure test_callback) {
258 on_timer_fired_callback_for_test_ = test_callback;
259 }
260
261 void AlarmTimer::Delegate::ResetImpl(base::TimeDelta delay,
262 int reset_sequence_number) {
263 DCHECK(base::MessageLoopForIO::IsCurrent());
264 DCHECK_NE(alarm_fd_, -1);
265
266 // Store the sequence number in the IO thread variable. When the timer
267 // fires, we will bind this value to the OnTimerFired callback to ensure
268 // that we do the right thing if the timer gets reset.
269 io_reset_sequence_number_ = reset_sequence_number;
270
271 // If we were already watching the fd, this will stop watching it.
272 fd_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
273
274 // Start watching the fd to see when the timer fires.
275 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
276 alarm_fd_, false, base::MessageLoopForIO::WATCH_READ,
277 fd_watcher_.get(), this)) {
278 LOG(ERROR) << "Error while attempting to watch file descriptor for RTC "
279 << "alarm. Timer will not fire.";
280 }
281
282 // Actually set the timer. This will also clear the pre-existing timer, if
283 // any.
284 itimerspec alarm_time = {};
285 alarm_time.it_value.tv_sec = delay.InSeconds();
286 alarm_time.it_value.tv_nsec =
287 (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) *
288 base::Time::kNanosecondsPerMicrosecond;
289 if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0)
290 PLOG(ERROR) << "Error while setting alarm time. Timer will not fire";
291 }
292
293 void AlarmTimer::Delegate::OnTimerFired(int reset_sequence_number) {
294 DCHECK(origin_message_loop_->RunsTasksOnCurrentThread());
295
296 // If a test wants to be notified when this function is about to run, then
297 // re-queue this task in the MessageLoop and run the test's callback.
298 if (!on_timer_fired_callback_for_test_.is_null()) {
299 origin_message_loop_->PostTask(
300 FROM_HERE,
301 base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
302 reset_sequence_number));
303
304 on_timer_fired_callback_for_test_.Run();
305 on_timer_fired_callback_for_test_.Reset();
306 return;
307 }
308
309 // Check to make sure that the timer was not reset in the time between when
310 // this task was queued to run and now. If it was reset, then don't do
311 // anything.
312 if (reset_sequence_number != origin_reset_sequence_number_)
313 return;
314
315 on_timer_fired_callback_.Run();
316 }
317
318 AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating)
319 : base::Timer(retain_user_task, is_repeating),
320 can_wake_from_suspend_(false),
321 origin_message_loop_(NULL),
322 weak_factory_(this) {
323 Init();
324 }
325
326 AlarmTimer::AlarmTimer(const tracked_objects::Location& posted_from,
327 base::TimeDelta delay,
328 const base::Closure& user_task,
329 bool is_repeating)
330 : base::Timer(posted_from, delay, user_task, is_repeating),
331 can_wake_from_suspend_(false),
332 origin_message_loop_(NULL),
333 weak_factory_(this) {
334 Init();
335 }
336
337 AlarmTimer::~AlarmTimer() {
338 Stop();
339 }
340
341 void AlarmTimer::SetTimerFiredCallbackForTest(base::Closure test_callback) {
342 delegate_->SetTimerFiredCallbackForTest(test_callback);
343 }
344
345 void AlarmTimer::Init() {
346 delegate_ = make_scoped_refptr(new AlarmTimer::Delegate(
347 base::Bind(&AlarmTimer::OnTimerFired, weak_factory_.GetWeakPtr())));
348 can_wake_from_suspend_ = delegate_->CanWakeFromSuspend();
349 }
350
351 void AlarmTimer::Stop() {
352 if (!can_wake_from_suspend_) {
353 base::Timer::Stop();
354 return;
355 }
356
357 // Clear the running flag, stop the delegate, and delete the pending task.
358 base::Timer::set_is_running(false);
359 delegate_->Stop();
360 pending_task_.reset();
361
362 // Stop watching |origin_message_loop_|.
363 origin_message_loop_ = NULL;
364 message_loop_observer_.reset();
365
366 if (!base::Timer::retain_user_task())
367 base::Timer::set_user_task(base::Closure());
368 }
369
370 void AlarmTimer::Reset() {
371 if (!can_wake_from_suspend_) {
372 base::Timer::Reset();
373 return;
374 }
375
376 DCHECK(!base::Timer::user_task().is_null());
377 DCHECK(!origin_message_loop_ ||
378 origin_message_loop_->task_runner()->RunsTasksOnCurrentThread());
379
380 // Make sure that the timer will stop if the underlying message loop is
381 // destroyed.
382 if (!origin_message_loop_) {
383 origin_message_loop_ = base::MessageLoop::current();
384 message_loop_observer_.reset(new MessageLoopObserver(
385 origin_message_loop_,
386 base::Bind(&AlarmTimer::WillDestroyCurrentMessageLoop,
387 weak_factory_.GetWeakPtr())));
388 }
389
390 // Set up the pending task.
391 if (base::Timer::GetCurrentDelay() > base::TimeDelta::FromMicroseconds(0)) {
392 base::Timer::set_desired_run_time(base::TimeTicks::Now() +
393 base::Timer::GetCurrentDelay());
394 pending_task_.reset(new base::PendingTask(
395 base::Timer::posted_from(), base::Timer::user_task(),
396 base::Timer::desired_run_time(), true /* nestable */));
397 } else {
398 base::Timer::set_desired_run_time(base::TimeTicks());
399 pending_task_.reset(new base::PendingTask(base::Timer::posted_from(),
400 base::Timer::user_task()));
401 }
402 base::MessageLoop::current()->task_annotator()->DidQueueTask(
403 "AlarmTimer::Reset", *pending_task_);
404
405 // Now start up the timer.
406 delegate_->Reset(base::Timer::GetCurrentDelay());
407 base::Timer::set_is_running(true);
408 }
409
410 void AlarmTimer::WillDestroyCurrentMessageLoop() {
411 Stop();
412 }
413
414 void AlarmTimer::OnTimerFired() {
415 if (!base::Timer::IsRunning())
416 return;
417
418 DCHECK(pending_task_.get());
419
420 // Take ownership of the pending user task, which is going to be cleared by
421 // the Stop() or Reset() functions below.
422 scoped_ptr<base::PendingTask> pending_user_task(pending_task_.Pass());
423
424 // Re-schedule or stop the timer as requested.
425 if (base::Timer::is_repeating())
426 Reset();
427 else
428 Stop();
429
430 // Now run the user task.
431 base::MessageLoop::current()->task_annotator()->RunTask(
432 "AlarmTimer::Reset", "AlarmTimer::OnTimerFired", *pending_user_task);
433 }
434
435 OneShotAlarmTimer::OneShotAlarmTimer() : AlarmTimer(false, false) {
436 }
437
438 OneShotAlarmTimer::~OneShotAlarmTimer() {
439 }
440
441 RepeatingAlarmTimer::RepeatingAlarmTimer() : AlarmTimer(true, true) {
442 }
443
444 RepeatingAlarmTimer::RepeatingAlarmTimer(
445 const tracked_objects::Location& posted_from,
446 base::TimeDelta delay,
447 const base::Closure& user_task)
448 : AlarmTimer(posted_from, delay, user_task, true) {
449 }
450
451 RepeatingAlarmTimer::~RepeatingAlarmTimer() {
452 }
453
454 SimpleAlarmTimer::SimpleAlarmTimer() : AlarmTimer(true, false) {
455 }
456
457 SimpleAlarmTimer::SimpleAlarmTimer(const tracked_objects::Location& posted_from,
458 base::TimeDelta delay,
459 const base::Closure& user_task)
460 : AlarmTimer(posted_from, delay, user_task, false) {
461 }
462
463 SimpleAlarmTimer::~SimpleAlarmTimer() {
464 }
465
466 } // namespace timers
OLDNEW
« no previous file with comments | « components/timers/alarm_timer_chromeos.h ('k') | components/timers/alarm_timer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698