summaryrefslogtreecommitdiff
path: root/components/timers/alarm_timer_chromeos.cc
blob: ae14870c56558fe17047bfaf842a9f31332451bf (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
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
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
// Copyright 2014 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.

#include "components/timers/alarm_timer_chromeos.h"

#include <stdint.h>
#include <sys/timerfd.h>
#include <utility>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/pending_task.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread.h"
#include "base/trace_event/trace_event.h"

namespace timers {
namespace {
// This class represents the IO thread that the AlarmTimer::Delegate may use for
// watching file descriptors if it gets called from a thread that does not have
// a MessageLoopForIO.  It is a lazy global instance because it may not always
// be necessary.
class RtcAlarmIOThread : public base::Thread {
 public:
  RtcAlarmIOThread() : Thread("RTC Alarm IO Thread") {
    CHECK(
        StartWithOptions(base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
  }
  ~RtcAlarmIOThread() override { Stop(); }
};

base::LazyInstance<RtcAlarmIOThread> g_io_thread = LAZY_INSTANCE_INITIALIZER;

}  // namespace

// Watches a MessageLoop and runs a callback if that MessageLoop will be
// destroyed.
class AlarmTimer::MessageLoopObserver
    : public base::MessageLoop::DestructionObserver {
 public:
  // Constructs a MessageLoopObserver that will observe |message_loop| and will
  // call |on_will_be_destroyed_callback| when |message_loop| is about to be
  // destroyed.
  MessageLoopObserver(base::MessageLoop* message_loop,
                      base::Closure on_will_be_destroyed_callback)
      : message_loop_(message_loop),
        on_will_be_destroyed_callback_(on_will_be_destroyed_callback) {
    DCHECK(message_loop_);
    message_loop_->AddDestructionObserver(this);
  }

  ~MessageLoopObserver() override {
    // If |message_loop_| was destroyed, then this class will have already
    // unregistered itself.  Doing it again will trigger a warning.
    if (message_loop_)
      message_loop_->RemoveDestructionObserver(this);
  }

  // base::MessageLoop::DestructionObserver override.
  void WillDestroyCurrentMessageLoop() override {
    message_loop_->RemoveDestructionObserver(this);
    message_loop_ = NULL;

    on_will_be_destroyed_callback_.Run();
  }

 private:
  // The MessageLoop that this class should watch.  Is a weak pointer.
  base::MessageLoop* message_loop_;

  // The callback to run when |message_loop_| will be destroyed.
  base::Closure on_will_be_destroyed_callback_;

  DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver);
};

// This class manages a Real Time Clock (RTC) alarm, a feature that is available
// from linux version 3.11 onwards.  It creates a file descriptor for the RTC
// alarm timer and then watches that file descriptor to see when it can be read
// without blocking, indicating that the timer has fired.
//
// A major problem for this class is that watching file descriptors is only
// available on a MessageLoopForIO but there is no guarantee the timer is going
// to be created on one.  To get around this, the timer has a dedicated thread
// with a MessageLoopForIO that posts tasks back to the thread that started the
// timer.
class AlarmTimer::Delegate
    : public base::RefCountedThreadSafe<AlarmTimer::Delegate>,
      public base::MessageLoopForIO::Watcher {
 public:
  // Construct a Delegate for the AlarmTimer.  It should be safe to call
  // |on_timer_fired_callback| multiple times.
  explicit Delegate(base::Closure on_timer_fired_callback);

  // Returns true if the system timer managed by this delegate is capable of
  // waking the system from suspend.
  bool CanWakeFromSuspend();

  // Resets the timer to fire after |delay| has passed.  Cancels any
  // pre-existing delay.
  void Reset(base::TimeDelta delay);

  // Stops the currently running timer.  It should be safe to call this even if
  // the timer is not running.
  void Stop();

  // Sets a hook that will be called when the timer fires and a task has been
  // queued on |origin_task_runner_|.  Used by tests to wait until a task is
  // pending in the MessageLoop.
  void SetTimerFiredCallbackForTest(base::Closure test_callback);

  // base::MessageLoopForIO::Watcher overrides.
  void OnFileCanReadWithoutBlocking(int fd) override;
  void OnFileCanWriteWithoutBlocking(int fd) override;

 private:
  friend class base::RefCountedThreadSafe<Delegate>;
  ~Delegate() override;

  // Actually performs the system calls to set up the timer.  This must be
  // called on a MessageLoopForIO.
  void ResetImpl(base::TimeDelta delay, int reset_sequence_number);

  // Callback that is run when the timer fires.  Must be run on
  // |origin_task_runner_|.
  void OnTimerFired(int reset_sequence_number);

  // File descriptor associated with the alarm timer.
  int alarm_fd_;

  // Task runner which initially started the timer.
  scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;

  // Callback that should be run when the timer fires.
  base::Closure on_timer_fired_callback_;

  // Hook used by tests to be notified when the timer has fired and a task has
  // been queued in the MessageLoop.
  base::Closure on_timer_fired_callback_for_test_;

  // Manages watching file descriptors.
  scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> fd_watcher_;

  // The sequence numbers of the last Reset() call handled respectively on
  // |origin_task_runner_| and on the MessageLoopForIO used for watching the
  // timer file descriptor.  Note that these can be the same MessageLoop.
  // OnTimerFired() runs |on_timer_fired_callback_| only if the sequence number
  // it receives from the MessageLoopForIO matches
  // |origin_reset_sequence_number_|.
  int origin_reset_sequence_number_;
  int io_reset_sequence_number_;

  DISALLOW_COPY_AND_ASSIGN(Delegate);
};

AlarmTimer::Delegate::Delegate(base::Closure on_timer_fired_callback)
    : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
      on_timer_fired_callback_(on_timer_fired_callback),
      origin_reset_sequence_number_(0),
      io_reset_sequence_number_(0) {
  // The call to timerfd_create above may fail.  This is the only indication
  // that CLOCK_REALTIME_ALARM is not supported on this system.
  DPLOG_IF(INFO, (alarm_fd_ == -1))
      << "CLOCK_REALTIME_ALARM not supported on this system";
}

AlarmTimer::Delegate::~Delegate() {
  if (alarm_fd_ != -1)
    close(alarm_fd_);
}

bool AlarmTimer::Delegate::CanWakeFromSuspend() {
  return alarm_fd_ != -1;
}

void AlarmTimer::Delegate::Reset(base::TimeDelta delay) {
  // Get a task runner for the current message loop.  When the timer fires, we
  // will
  // post tasks to this proxy to let the parent timer know.
  origin_task_runner_ = base::ThreadTaskRunnerHandle::Get();

  // Increment the sequence number.  Used to invalidate any events that have
  // been queued but not yet run since the last time Reset() was called.
  origin_reset_sequence_number_++;

  // Calling timerfd_settime with a zero delay actually clears the timer so if
  // the user has requested a zero delay timer, we need to handle it
  // differently.  We queue the task here but we still go ahead and call
  // timerfd_settime with the zero delay anyway to cancel any previous delay
  // that might have been programmed.
  if (delay <= base::TimeDelta::FromMicroseconds(0)) {
    // The timerfd_settime documentation is vague on what happens when it is
    // passed a negative delay.  We can sidestep the issue by ensuring that
    // the delay is 0.
    delay = base::TimeDelta::FromMicroseconds(0);
    origin_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
                   origin_reset_sequence_number_));
  }

  // Run ResetImpl() on a MessageLoopForIO.
  if (base::MessageLoopForIO::IsCurrent()) {
    ResetImpl(delay, origin_reset_sequence_number_);
  } else {
    g_io_thread.Pointer()->task_runner()->PostTask(
        FROM_HERE,
        base::Bind(&Delegate::ResetImpl, scoped_refptr<Delegate>(this), delay,
                   origin_reset_sequence_number_));
  }
}

void AlarmTimer::Delegate::Stop() {
  // Stop the RTC from a MessageLoopForIO.
  if (!base::MessageLoopForIO::IsCurrent()) {
    g_io_thread.Pointer()->task_runner()->PostTask(
        FROM_HERE, base::Bind(&Delegate::Stop, scoped_refptr<Delegate>(this)));
    return;
  }

  // Stop watching for events.
  fd_watcher_.reset();

  // Now clear the timer.
  DCHECK_NE(alarm_fd_, -1);
  itimerspec blank_time;
  memset(&blank_time, 0, sizeof(blank_time));
  if (timerfd_settime(alarm_fd_, 0, &blank_time, NULL) < 0)
    PLOG(ERROR) << "Unable to clear alarm time.  Timer may still fire.";
}

void AlarmTimer::Delegate::OnFileCanReadWithoutBlocking(int fd) {
  DCHECK_EQ(alarm_fd_, fd);

  // Read from the fd to ack the event.
  char val[sizeof(uint64_t)];
  if (!base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t)))
    PLOG(DFATAL) << "Unable to read from timer file descriptor.";

  // Make sure that the parent timer is informed on the proper message loop.
  if (origin_task_runner_->RunsTasksOnCurrentThread()) {
    OnTimerFired(io_reset_sequence_number_);
  } else {
    origin_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
                   io_reset_sequence_number_));
  }
}

void AlarmTimer::Delegate::OnFileCanWriteWithoutBlocking(int /* fd */) {
  NOTREACHED();
}

void AlarmTimer::Delegate::SetTimerFiredCallbackForTest(
    base::Closure test_callback) {
  on_timer_fired_callback_for_test_ = test_callback;
}

void AlarmTimer::Delegate::ResetImpl(base::TimeDelta delay,
                                     int reset_sequence_number) {
  DCHECK(base::MessageLoopForIO::IsCurrent());
  DCHECK_NE(alarm_fd_, -1);

  // Store the sequence number in the IO thread variable.  When the timer
  // fires, we will bind this value to the OnTimerFired callback to ensure
  // that we do the right thing if the timer gets reset.
  io_reset_sequence_number_ = reset_sequence_number;

  // If we were already watching the fd, this will stop watching it.
  fd_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);

  // Start watching the fd to see when the timer fires.
  if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
          alarm_fd_, false, base::MessageLoopForIO::WATCH_READ,
          fd_watcher_.get(), this)) {
    LOG(ERROR) << "Error while attempting to watch file descriptor for RTC "
               << "alarm.  Timer will not fire.";
  }

  // Actually set the timer.  This will also clear the pre-existing timer, if
  // any.
  itimerspec alarm_time;
  memset(&alarm_time, 0, sizeof(alarm_time));
  alarm_time.it_value.tv_sec = delay.InSeconds();
  alarm_time.it_value.tv_nsec =
      (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) *
      base::Time::kNanosecondsPerMicrosecond;
  if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0)
    PLOG(ERROR) << "Error while setting alarm time.  Timer will not fire";
}

void AlarmTimer::Delegate::OnTimerFired(int reset_sequence_number) {
  DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());

  // If a test wants to be notified when this function is about to run, then
  // re-queue this task in the MessageLoop and run the test's callback.
  if (!on_timer_fired_callback_for_test_.is_null()) {
    origin_task_runner_->PostTask(
        FROM_HERE,
        base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
                   reset_sequence_number));

    on_timer_fired_callback_for_test_.Run();
    on_timer_fired_callback_for_test_.Reset();
    return;
  }

  // Check to make sure that the timer was not reset in the time between when
  // this task was queued to run and now.  If it was reset, then don't do
  // anything.
  if (reset_sequence_number != origin_reset_sequence_number_)
    return;

  on_timer_fired_callback_.Run();
}

AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating)
    : base::Timer(retain_user_task, is_repeating),
      can_wake_from_suspend_(false),
      origin_message_loop_(NULL),
      weak_factory_(this) {
  Init();
}

AlarmTimer::AlarmTimer(const tracked_objects::Location& posted_from,
                       base::TimeDelta delay,
                       const base::Closure& user_task,
                       bool is_repeating)
    : base::Timer(posted_from, delay, user_task, is_repeating),
      can_wake_from_suspend_(false),
      origin_message_loop_(NULL),
      weak_factory_(this) {
  Init();
}

AlarmTimer::~AlarmTimer() {
  Stop();
}

void AlarmTimer::SetTimerFiredCallbackForTest(base::Closure test_callback) {
  delegate_->SetTimerFiredCallbackForTest(test_callback);
}

void AlarmTimer::Init() {
  delegate_ = make_scoped_refptr(new AlarmTimer::Delegate(
      base::Bind(&AlarmTimer::OnTimerFired, weak_factory_.GetWeakPtr())));
  can_wake_from_suspend_ = delegate_->CanWakeFromSuspend();
}

void AlarmTimer::Stop() {
  if (!base::Timer::is_running())
    return;

  if (!can_wake_from_suspend_) {
    base::Timer::Stop();
    return;
  }

  // Clear the running flag, stop the delegate, and delete the pending task.
  base::Timer::set_is_running(false);
  delegate_->Stop();
  pending_task_.reset();

  // Stop watching |origin_message_loop_|.
  origin_message_loop_ = NULL;
  message_loop_observer_.reset();

  if (!base::Timer::retain_user_task())
    base::Timer::set_user_task(base::Closure());
}

void AlarmTimer::Reset() {
  if (!can_wake_from_suspend_) {
    base::Timer::Reset();
    return;
  }

  DCHECK(!base::Timer::user_task().is_null());
  DCHECK(!origin_message_loop_ ||
         origin_message_loop_->task_runner()->RunsTasksOnCurrentThread());

  // Make sure that the timer will stop if the underlying message loop is
  // destroyed.
  if (!origin_message_loop_) {
    origin_message_loop_ = base::MessageLoop::current();
    message_loop_observer_.reset(new MessageLoopObserver(
        origin_message_loop_,
        base::Bind(&AlarmTimer::WillDestroyCurrentMessageLoop,
                   weak_factory_.GetWeakPtr())));
  }

  // Set up the pending task.
  if (base::Timer::GetCurrentDelay() > base::TimeDelta::FromMicroseconds(0)) {
    base::Timer::set_desired_run_time(base::TimeTicks::Now() +
                                      base::Timer::GetCurrentDelay());
    pending_task_.reset(new base::PendingTask(
        base::Timer::posted_from(), base::Timer::user_task(),
        base::Timer::desired_run_time(), true /* nestable */));
  } else {
    base::Timer::set_desired_run_time(base::TimeTicks());
    pending_task_.reset(new base::PendingTask(base::Timer::posted_from(),
                                              base::Timer::user_task()));
  }
  base::MessageLoop::current()->task_annotator()->DidQueueTask(
      "AlarmTimer::Reset", *pending_task_);

  // Now start up the timer.
  delegate_->Reset(base::Timer::GetCurrentDelay());
  base::Timer::set_is_running(true);
}

void AlarmTimer::WillDestroyCurrentMessageLoop() {
  Stop();
}

void AlarmTimer::OnTimerFired() {
  if (!base::Timer::IsRunning())
    return;

  DCHECK(pending_task_.get());

  // Take ownership of the pending user task, which is going to be cleared by
  // the Stop() or Reset() functions below.
  scoped_ptr<base::PendingTask> pending_user_task(std::move(pending_task_));

  // Re-schedule or stop the timer as requested.
  if (base::Timer::is_repeating())
    Reset();
  else
    Stop();

  TRACE_TASK_EXECUTION("AlarmTimer::OnTimerFired", *pending_user_task);

  // Now run the user task.
  base::MessageLoop::current()->task_annotator()->RunTask("AlarmTimer::Reset",
                                                          *pending_user_task);
}

OneShotAlarmTimer::OneShotAlarmTimer() : AlarmTimer(false, false) {
}

OneShotAlarmTimer::~OneShotAlarmTimer() {
}

RepeatingAlarmTimer::RepeatingAlarmTimer() : AlarmTimer(true, true) {
}

RepeatingAlarmTimer::RepeatingAlarmTimer(
    const tracked_objects::Location& posted_from,
    base::TimeDelta delay,
    const base::Closure& user_task)
    : AlarmTimer(posted_from, delay, user_task, true) {
}

RepeatingAlarmTimer::~RepeatingAlarmTimer() {
}

SimpleAlarmTimer::SimpleAlarmTimer() : AlarmTimer(true, false) {
}

SimpleAlarmTimer::SimpleAlarmTimer(const tracked_objects::Location& posted_from,
                                   base::TimeDelta delay,
                                   const base::Closure& user_task)
    : AlarmTimer(posted_from, delay, user_task, false) {
}

SimpleAlarmTimer::~SimpleAlarmTimer() {
}

}  // namespace timers