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

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: clean up comments Created 5 years, 10 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
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_proxy.h"
11 #include "base/pending_task.h" 16 #include "base/pending_task.h"
12 #include "components/timers/rtc_alarm.h" 17 #include "base/threading/thread.h"
13 18
14 namespace timers { 19 namespace timers {
20 namespace {
21 class RtcAlarmIOThread : public base::Thread {
22 public:
23 RtcAlarmIOThread() : Thread("RTC Alarm IO Thread") {
24 CHECK(
25 StartWithOptions(base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
26 }
27 ~RtcAlarmIOThread() override {}
28 };
29
30 base::LazyInstance<RtcAlarmIOThread> g_io_thread = LAZY_INSTANCE_INITIALIZER;
31
32 } // namespace
33
34 // This class manages a Real Time Clock (RTC) alarm, a feature that is available
35 // from linux version 3.11 onwards. It creates a file descriptor for the RTC
36 // alarm timer and then watches that file descriptor to see when it can be read
37 // without blocking, indicating that the timer has fired.
38 //
39 // A major problem for this class is that watching file descriptors is only
40 // available on a MessageLoopForIO but there is no guarantee the timer is going
41 // to be created on one. To get around this, the timer has a dedicated thread
42 // with a MessageLoopForIO that posts tasks back to the thread that started the
43 // timer.
44 class AlarmTimer::Delegate
45 : public base::RefCountedThreadSafe<AlarmTimer::Delegate>,
46 public base::MessageLoopForIO::Watcher {
47 public:
48 // Construct a Delegate for the AlarmTimer. |parent| should be a valid
49 // WeakPtr to the parent AlarmTimer.
50 explicit Delegate(base::WeakPtr<AlarmTimer> parent)
51 : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
52 parent_(parent),
53 origin_reset_sequence_number_(0),
54 io_reset_sequence_number_(0) {
55 DCHECK(parent_);
56 }
57
58 // Returns true if the system timer managed by this delegate is capable of
59 // waking the system from suspend.
60 bool CanWakeFromSuspend() { return alarm_fd_ != -1; }
61
62 // Resets the timer to fire after |delay| has passed. Cancels any
63 // pre-existing delay.
64 void Reset(base::TimeDelta delay) {
gromer 2015/02/10 23:47:10 https://engdoc.corp.google.com/eng/doc/devguide/cp
Chirantan Ekbote 2015/02/12 01:49:59 Done.
65 // Get a proxy for the current message loop. When the timer fires, we will
66 // post tasks to this proxy to let the parent timer know.
67 origin_message_loop_ = base::MessageLoopProxy::current();
68
69 // Increment the sequence number. Used to invalidate any events that have
70 // been queued but not yet run since the last time Reset() was called.
71 origin_reset_sequence_number_++;
72
73 // Calling timerfd_settime with a zero delay actually clears the timer so if
74 // the user has requested a zero delay timer, we need to handle it
75 // differently. We queue the task here but we still go ahead and call
76 // timerfd_settime with the zero delay anyway to cancel any previous delay
77 // that might have been programmed.
78 if (delay <= base::TimeDelta::FromMicroseconds(0)) {
79 // The timerfd_settime documentation is vague on what happens when it is
80 // passed a negative delay. We can sidestep the issue by ensuring that
81 // the delay is 0.
82 delay = base::TimeDelta::FromMicroseconds(0);
83 origin_message_loop_->PostTask(
84 FROM_HERE,
85 base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
86 origin_reset_sequence_number_));
87 }
88
89 // Run ResetImpl() on a MessageLoopForIO.
90 if (!base::MessageLoopForIO::IsCurrent()) {
91 g_io_thread.Pointer()->task_runner()->PostTask(
92 FROM_HERE,
93 base::Bind(&Delegate::ResetImpl, scoped_refptr<Delegate>(this), delay,
94 origin_reset_sequence_number_));
95 } else {
96 ResetImpl(delay, origin_reset_sequence_number_);
97 }
98 }
99
100 // Stops the currently running timer. It should be safe to call this even if
101 // the timer is not running.
102 void Stop() {
103 // Stop the RTC from a MessageLoopForIO.
104 if (!base::MessageLoopForIO::IsCurrent()) {
105 g_io_thread.Pointer()->task_runner()->PostTask(
106 FROM_HERE,
107 base::Bind(&Delegate::Stop, scoped_refptr<Delegate>(this)));
108 return;
109 }
110
111 // Stop watching for events.
112 fd_watcher_.reset();
113
114 // Now clear the timer.
115 DCHECK_NE(alarm_fd_, -1);
116 itimerspec blank_time = {};
117 timerfd_settime(alarm_fd_, 0, &blank_time, NULL);
118 }
119
120 // base::MessageLoopForIO::Watcher overrides.
121 void OnFileCanReadWithoutBlocking(int fd) override {
122 DCHECK_EQ(alarm_fd_, fd);
123
124 // Read from the fd to ack the event.
125 char val[sizeof(uint64_t)];
126 base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t));
127
128 // Make sure that the parent timer is informed on the proper message loop.
129 if (origin_message_loop_->RunsTasksOnCurrentThread()) {
130 OnTimerFired(io_reset_sequence_number_);
131 } else {
132 origin_message_loop_->PostTask(
133 FROM_HERE,
134 base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
135 io_reset_sequence_number_));
136 }
137 }
138
139 void OnFileCanWriteWithoutBlocking(int fd) override { NOTREACHED(); }
140
141 private:
142 friend class base::RefCountedThreadSafe<Delegate>;
143 ~Delegate() override {
144 if (alarm_fd_ != -1)
145 close(alarm_fd_);
146 }
147
148 // Actually performs the system calls to set up the timer. This must be
149 // called on a MessageLoopForIO.
150 void ResetImpl(base::TimeDelta delay, int reset_sequence_number) {
151 DCHECK(base::MessageLoopForIO::IsCurrent());
152 DCHECK_NE(alarm_fd_, -1);
153
154 // Store the sequence number in the IO thread variable. When the timer
155 // fires, we will bind this value to the OnTimerFired callback to ensure
156 // that we do the right thing if the timer gets reset.
157 io_reset_sequence_number_ = reset_sequence_number;
158
159 // If we were already watching the fd, this will stop watching it.
160 fd_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
161
162 // Start watching the fd to see when the timer fires.
163 if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
164 alarm_fd_, false, base::MessageLoopForIO::WATCH_READ,
165 fd_watcher_.get(), this)) {
166 LOG(ERROR) << "Error while attempting to watch file descriptor for RTC "
167 << "alarm. Timer will not fire.";
168 }
169
170 // Actually set the timer. This will also clear the pre-existing timer, if
171 // any.
172 itimerspec alarm_time = {};
173 alarm_time.it_value.tv_sec = delay.InSeconds();
174 alarm_time.it_value.tv_nsec =
175 (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) *
176 base::Time::kNanosecondsPerMicrosecond;
177 if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0)
178 PLOG(ERROR) << "Error while setting alarm time. Timer will not fire";
179 }
180
181 // Callback that is run when the timer fires. Must be run on
182 // |origin_message_loop_|.
183 void OnTimerFired(int reset_sequence_number) {
184 DCHECK(origin_message_loop_->RunsTasksOnCurrentThread());
185
186 // Check to make sure that the timer was not reset in the time between when
187 // this task was queued to run and now. If it was reset, then don't do
188 // anything.
189 if (reset_sequence_number != origin_reset_sequence_number_)
190 return;
191
192 if (parent_)
193 parent_->OnTimerFired();
194 }
195
196 // File descriptor associated with the alarm timer.
197 int alarm_fd_;
198
199 // Message loop which initially started the timer.
200 scoped_refptr<base::MessageLoopProxy> origin_message_loop_;
201
202 // The parent timer that should be informed when the timer fires. This class
203 // may end up outliving the parent so it needs to ensure that the reference is
204 // valid before trying to call it.
205 base::WeakPtr<AlarmTimer> parent_;
206
207 // Manages watching file descriptors.
208 scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> fd_watcher_;
209
210 // The sequence number of the last Reset() call handled by the origin thread
gromer 2015/02/10 23:47:10 I have to read a long way into this sentence to fi
Chirantan Ekbote 2015/02/12 01:49:59 Done.
211 // where this class lives and the IO thread used for watching the timer fd,
212 // respectively. Note that these can be the same thread. OnTimerFired()
213 // propagates the timer fired event to |parent_| only if the sequence number
214 // it receives from the IO thread matches |origin_reset_sequence_number_|.
215 int origin_reset_sequence_number_;
216 int io_reset_sequence_number_;
217
218 DISALLOW_COPY_AND_ASSIGN(Delegate);
219 };
15 220
16 AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating) 221 AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating)
17 : base::Timer(retain_user_task, is_repeating), 222 : base::Timer(retain_user_task, is_repeating),
18 delegate_(new RtcAlarm()),
19 can_wake_from_suspend_(false), 223 can_wake_from_suspend_(false),
20 origin_message_loop_(NULL), 224 origin_message_loop_(NULL),
21 weak_factory_(this) { 225 weak_factory_(this) {
22 can_wake_from_suspend_ = delegate_->Init(weak_factory_.GetWeakPtr()); 226 delegate_ =
227 make_scoped_refptr(new AlarmTimer::Delegate(weak_factory_.GetWeakPtr()));
228 can_wake_from_suspend_ = delegate_->CanWakeFromSuspend();
23 } 229 }
24 230
25 AlarmTimer::AlarmTimer(const tracked_objects::Location& posted_from, 231 AlarmTimer::AlarmTimer(const tracked_objects::Location& posted_from,
26 base::TimeDelta delay, 232 base::TimeDelta delay,
27 const base::Closure& user_task, 233 const base::Closure& user_task,
28 bool is_repeating) 234 bool is_repeating)
29 : base::Timer(posted_from, delay, user_task, is_repeating), 235 : base::Timer(posted_from, delay, user_task, is_repeating),
30 delegate_(new RtcAlarm()),
31 can_wake_from_suspend_(false), 236 can_wake_from_suspend_(false),
32 origin_message_loop_(NULL), 237 origin_message_loop_(NULL),
33 weak_factory_(this) { 238 weak_factory_(this) {
34 can_wake_from_suspend_ = delegate_->Init(weak_factory_.GetWeakPtr()); 239 delegate_ =
240 make_scoped_refptr(new AlarmTimer::Delegate(weak_factory_.GetWeakPtr()));
241 can_wake_from_suspend_ = delegate_->CanWakeFromSuspend();
35 } 242 }
36 243
37 AlarmTimer::~AlarmTimer() { 244 AlarmTimer::~AlarmTimer() {
38 Stop(); 245 Stop();
39 } 246 }
40 247
41 void AlarmTimer::OnTimerFired() { 248 void AlarmTimer::OnTimerFired() {
42 if (!base::Timer::IsRunning()) 249 if (!base::Timer::IsRunning())
43 return; 250 return;
44 251
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 301
95 // Make sure that the timer will stop if the underlying message loop is 302 // Make sure that the timer will stop if the underlying message loop is
96 // destroyed. 303 // destroyed.
97 if (!origin_message_loop_) { 304 if (!origin_message_loop_) {
98 origin_message_loop_ = base::MessageLoop::current(); 305 origin_message_loop_ = base::MessageLoop::current();
99 origin_message_loop_->AddDestructionObserver(this); 306 origin_message_loop_->AddDestructionObserver(this);
100 } 307 }
101 308
102 // Set up the pending task. 309 // Set up the pending task.
103 if (base::Timer::GetCurrentDelay() > base::TimeDelta::FromMicroseconds(0)) { 310 if (base::Timer::GetCurrentDelay() > base::TimeDelta::FromMicroseconds(0)) {
104 base::Timer::set_desired_run_time( 311 base::Timer::set_desired_run_time(base::TimeTicks::Now() +
105 base::TimeTicks::Now() + base::Timer::GetCurrentDelay()); 312 base::Timer::GetCurrentDelay());
106 pending_task_.reset(new base::PendingTask(base::Timer::posted_from(), 313 pending_task_.reset(new base::PendingTask(
107 base::Timer::user_task(), 314 base::Timer::posted_from(), base::Timer::user_task(),
108 base::Timer::desired_run_time(), 315 base::Timer::desired_run_time(), true /* nestable */));
109 true /* nestable */));
110 } else { 316 } else {
111 base::Timer::set_desired_run_time(base::TimeTicks()); 317 base::Timer::set_desired_run_time(base::TimeTicks());
112 pending_task_.reset(new base::PendingTask(base::Timer::posted_from(), 318 pending_task_.reset(new base::PendingTask(base::Timer::posted_from(),
113 base::Timer::user_task())); 319 base::Timer::user_task()));
114 } 320 }
115 base::MessageLoop::current()->task_annotator()->DidQueueTask( 321 base::MessageLoop::current()->task_annotator()->DidQueueTask(
116 "AlarmTimer::Reset", *pending_task_); 322 "AlarmTimer::Reset", *pending_task_);
117 323
118 // Now start up the timer. 324 // Now start up the timer.
119 delegate_->Reset(base::Timer::GetCurrentDelay()); 325 delegate_->Reset(base::Timer::GetCurrentDelay());
120 base::Timer::set_is_running(true); 326 base::Timer::set_is_running(true);
121 } 327 }
122 328
123 void AlarmTimer::WillDestroyCurrentMessageLoop() { 329 void AlarmTimer::WillDestroyCurrentMessageLoop() {
124 Stop(); 330 Stop();
125 } 331 }
126 332
127 } // namespace timers 333 } // namespace timers
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698