forked from sanyaade-mobiledev/chromium.src
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrtc_alarm.cc
186 lines (150 loc) · 5.9 KB
/
rtc_alarm.cc
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
// 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/rtc_alarm.h"
#include <sys/timerfd.h>
#include "base/bind.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_proxy.h"
#include "base/threading/thread.h"
namespace timers {
namespace {
// Helper class to ensure that the IO thread we will use for watching file
// descriptors is started only once.
class IOThreadStartHelper {
public:
IOThreadStartHelper() : thread_(new base::Thread("RTC Alarm IO Thread")) {
CHECK(thread_->StartWithOptions(
base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
}
~IOThreadStartHelper() {}
base::Thread& operator*() const { return *thread_.get(); }
base::Thread* operator->() const { return thread_.get(); }
private:
scoped_ptr<base::Thread> thread_;
};
base::LazyInstance<IOThreadStartHelper> g_io_thread = LAZY_INSTANCE_INITIALIZER;
} // namespace
RtcAlarm::RtcAlarm()
: alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
origin_event_id_(0),
io_event_id_(0) {
}
RtcAlarm::~RtcAlarm() {
if (alarm_fd_ != -1)
close(alarm_fd_);
}
bool RtcAlarm::Init(base::WeakPtr<AlarmTimer> parent) {
parent_ = parent;
return alarm_fd_ != -1;
}
void RtcAlarm::Stop() {
// Make sure that we stop the RTC from a MessageLoopForIO.
if (!base::MessageLoopForIO::IsCurrent()) {
g_io_thread.Get()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&RtcAlarm::Stop, scoped_refptr<RtcAlarm>(this)));
return;
}
// Stop watching for events.
fd_watcher_.reset();
// Now clear the timer.
DCHECK_NE(alarm_fd_, -1);
itimerspec blank_time = {};
timerfd_settime(alarm_fd_, 0, &blank_time, NULL);
}
void RtcAlarm::Reset(base::TimeDelta delay) {
// Get a proxy for the current message loop. When the timer fires, we will
// post tasks to this proxy to let the parent timer know.
origin_message_loop_ = base::MessageLoopProxy::current();
// Increment the event id. Used to invalidate any events that have been
// queued but not yet run since the last time Reset() was called.
origin_event_id_++;
// 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_message_loop_->PostTask(FROM_HERE,
base::Bind(&RtcAlarm::OnTimerFired,
scoped_refptr<RtcAlarm>(this),
origin_event_id_));
}
// Make sure that we are running on a MessageLoopForIO.
if (!base::MessageLoopForIO::IsCurrent()) {
g_io_thread.Get()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&RtcAlarm::ResetImpl,
scoped_refptr<RtcAlarm>(this),
delay,
origin_event_id_));
return;
}
ResetImpl(delay, origin_event_id_);
}
void RtcAlarm::OnFileCanReadWithoutBlocking(int fd) {
DCHECK_EQ(alarm_fd_, fd);
// Read from the fd to ack the event.
char val[sizeof(uint64_t)];
base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t));
// Make sure that the parent timer is informed on the proper message loop.
if (origin_message_loop_->RunsTasksOnCurrentThread()) {
OnTimerFired(io_event_id_);
return;
}
origin_message_loop_->PostTask(FROM_HERE,
base::Bind(&RtcAlarm::OnTimerFired,
scoped_refptr<RtcAlarm>(this),
io_event_id_));
}
void RtcAlarm::OnFileCanWriteWithoutBlocking(int fd) {
NOTREACHED();
}
void RtcAlarm::ResetImpl(base::TimeDelta delay, int event_id) {
DCHECK(base::MessageLoopForIO::IsCurrent());
DCHECK_NE(alarm_fd_, -1);
// Store the event id 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_event_id_ = event_id;
// 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 = {};
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 RtcAlarm::OnTimerFired(int event_id) {
DCHECK(origin_message_loop_->RunsTasksOnCurrentThread());
// 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 (event_id != origin_event_id_)
return;
if (parent_)
parent_->OnTimerFired();
}
} // namespace timers