1 // Copyright (c) 2012 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 "base/message_loop/message_pump_android.h"
6
7 #include <android/looper.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <jni.h>
11 #include <sys/eventfd.h>
12 #include <sys/syscall.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <utility>
16
17 #include "base/android/jni_android.h"
18 #include "base/android/scoped_java_ref.h"
19 #include "base/callback_helpers.h"
20 #include "base/lazy_instance.h"
21 #include "base/logging.h"
22 #include "base/run_loop.h"
23
24 // Android stripped sys/timerfd.h out of their platform headers, so we have to
25 // use syscall to make use of timerfd. Once the min API level is 20, we can
26 // directly use timerfd.h.
27 #ifndef __NR_timerfd_create
28 #error "Unable to find syscall for __NR_timerfd_create"
29 #endif
30
31 #ifndef TFD_TIMER_ABSTIME
32 #define TFD_TIMER_ABSTIME (1 << 0)
33 #endif
34
35 using base::android::JavaParamRef;
36 using base::android::ScopedJavaLocalRef;
37
38 namespace base {
39
40 namespace {
41
42 // See sys/timerfd.h
timerfd_create(int clockid,int flags)43 int timerfd_create(int clockid, int flags) {
44 return syscall(__NR_timerfd_create, clockid, flags);
45 }
46
47 // See sys/timerfd.h
timerfd_settime(int ufc,int flags,const struct itimerspec * utmr,struct itimerspec * otmr)48 int timerfd_settime(int ufc,
49 int flags,
50 const struct itimerspec* utmr,
51 struct itimerspec* otmr) {
52 return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
53 }
54
NonDelayedLooperCallback(int fd,int events,void * data)55 int NonDelayedLooperCallback(int fd, int events, void* data) {
56 if (events & ALOOPER_EVENT_HANGUP)
57 return 0;
58
59 DCHECK(events & ALOOPER_EVENT_INPUT);
60 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
61 pump->OnNonDelayedLooperCallback();
62 return 1; // continue listening for events
63 }
64
DelayedLooperCallback(int fd,int events,void * data)65 int DelayedLooperCallback(int fd, int events, void* data) {
66 if (events & ALOOPER_EVENT_HANGUP)
67 return 0;
68
69 DCHECK(events & ALOOPER_EVENT_INPUT);
70 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
71 pump->OnDelayedLooperCallback();
72 return 1; // continue listening for events
73 }
74
75 } // namespace
76
MessagePumpForUI()77 MessagePumpForUI::MessagePumpForUI() {
78 // The Android native ALooper uses epoll to poll our file descriptors and wake
79 // us up. We use a simple level-triggered eventfd to signal that non-delayed
80 // work is available, and a timerfd to signal when delayed work is ready to
81 // be run.
82 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
83 CHECK_NE(non_delayed_fd_, -1);
84 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
85
86 // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
87 // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
88 // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
89 // fine.
90 delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC);
91 CHECK_NE(delayed_fd_, -1);
92
93 looper_ = ALooper_prepare(0);
94 DCHECK(looper_);
95 // Add a reference to the looper so it isn't deleted on us.
96 ALooper_acquire(looper_);
97 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
98 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
99 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
100 &DelayedLooperCallback, reinterpret_cast<void*>(this));
101 }
102
~MessagePumpForUI()103 MessagePumpForUI::~MessagePumpForUI() {
104 DCHECK_EQ(ALooper_forThread(), looper_);
105 ALooper_removeFd(looper_, non_delayed_fd_);
106 ALooper_removeFd(looper_, delayed_fd_);
107 ALooper_release(looper_);
108 looper_ = nullptr;
109
110 close(non_delayed_fd_);
111 close(delayed_fd_);
112 }
113
OnDelayedLooperCallback()114 void MessagePumpForUI::OnDelayedLooperCallback() {
115 if (ShouldQuit())
116 return;
117
118 // Clear the fd.
119 uint64_t value;
120 int ret = read(delayed_fd_, &value, sizeof(value));
121 DCHECK_GE(ret, 0);
122 delayed_scheduled_time_ = base::TimeTicks();
123
124 base::TimeTicks next_delayed_work_time;
125 delegate_->DoDelayedWork(&next_delayed_work_time);
126 if (!next_delayed_work_time.is_null()) {
127 ScheduleDelayedWork(next_delayed_work_time);
128 }
129 if (ShouldQuit())
130 return;
131 // We may be idle now, so pump the loop to find out.
132 ScheduleWork();
133 }
134
OnNonDelayedLooperCallback()135 void MessagePumpForUI::OnNonDelayedLooperCallback() {
136 base::TimeTicks next_delayed_work_time;
137 bool did_any_work = false;
138
139 // Runs all native tasks scheduled to run, scheduling delayed work if
140 // necessary.
141 while (true) {
142 bool did_work_this_loop = false;
143 if (ShouldQuit())
144 return;
145 did_work_this_loop = delegate_->DoWork();
146 if (ShouldQuit())
147 return;
148
149 did_work_this_loop |= delegate_->DoDelayedWork(&next_delayed_work_time);
150
151 did_any_work |= did_work_this_loop;
152
153 // If we didn't do any work, we're out of native tasks to run, and we should
154 // return control to the looper to run Java tasks.
155 if (!did_work_this_loop)
156 break;
157 }
158 // If we did any work, return control to the looper to run java tasks before
159 // we call DoIdleWork(). We haven't cleared the fd yet, so we'll get woken up
160 // again soon to check for idle-ness.
161 if (did_any_work)
162 return;
163 if (ShouldQuit())
164 return;
165
166 // Read the file descriptor, resetting its contents to 0 and reading back the
167 // stored value.
168 // See http://man7.org/linux/man-pages/man2/eventfd.2.html
169 uint64_t value = 0;
170 int ret = read(non_delayed_fd_, &value, sizeof(value));
171 DCHECK_GE(ret, 0);
172
173 // If we read a value > 1, it means we lost the race to clear the fd before a
174 // new task was posted. This is okay, we can just re-schedule work.
175 if (value > 1) {
176 ScheduleWork();
177 } else {
178 // At this point, the java looper might not be idle - it's impossible to
179 // know pre-Android-M, so we may end up doing Idle work while java tasks are
180 // still queued up. Note that this won't cause us to fail to run java tasks
181 // using QuitWhenIdle, as the JavaHandlerThread will finish running all
182 // currently scheduled tasks before it quits. Also note that we can't just
183 // add an idle callback to the java looper, as that will fire even if native
184 // tasks are still queued up.
185 DoIdleWork();
186 if (!next_delayed_work_time.is_null()) {
187 ScheduleDelayedWork(next_delayed_work_time);
188 }
189 }
190 }
191
DoIdleWork()192 void MessagePumpForUI::DoIdleWork() {
193 if (delegate_->DoIdleWork()) {
194 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
195 // the loop here because we may in fact be idle after doing idle work
196 // without any new tasks being queued.
197 ScheduleWork();
198 }
199 }
200
Run(Delegate * delegate)201 void MessagePumpForUI::Run(Delegate* delegate) {
202 DCHECK(IsTestImplementation());
203 // This function is only called in tests. We manually pump the native looper
204 // which won't run any java tasks.
205 quit_ = false;
206
207 SetDelegate(delegate);
208
209 // Pump the loop once in case we're starting off idle as ALooper_pollOnce will
210 // never return in that case.
211 ScheduleWork();
212 while (true) {
213 // Waits for either the delayed, or non-delayed fds to be signalled, calling
214 // either OnDelayedLooperCallback, or OnNonDelayedLooperCallback,
215 // respectively. This uses Android's Looper implementation, which is based
216 // off of epoll.
217 ALooper_pollOnce(-1, nullptr, nullptr, nullptr);
218 if (quit_)
219 break;
220 }
221 }
222
Attach(Delegate * delegate)223 void MessagePumpForUI::Attach(Delegate* delegate) {
224 DCHECK(!quit_);
225
226 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
227 // can't use Run() like we do on other platforms or we would prevent Java
228 // tasks from running. Instead we create and initialize a run loop here, then
229 // return control back to the Looper.
230
231 SetDelegate(delegate);
232 run_loop_ = std::make_unique<RunLoop>();
233 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
234 // return true (it only returns false if the RunLoop has been Quit already).
235 if (!run_loop_->BeforeRun())
236 NOTREACHED();
237 }
238
Quit()239 void MessagePumpForUI::Quit() {
240 if (quit_)
241 return;
242
243 quit_ = true;
244
245 int64_t value;
246 // Clear any pending timer.
247 read(delayed_fd_, &value, sizeof(value));
248 // Clear the eventfd.
249 read(non_delayed_fd_, &value, sizeof(value));
250
251 if (run_loop_) {
252 run_loop_->AfterRun();
253 run_loop_ = nullptr;
254 }
255 if (on_quit_callback_) {
256 std::move(on_quit_callback_).Run();
257 }
258 }
259
ScheduleWork()260 void MessagePumpForUI::ScheduleWork() {
261 if (ShouldQuit())
262 return;
263
264 // Write (add) 1 to the eventfd. This tells the Looper to wake up and call our
265 // callback, allowing us to run tasks. This also allows us to detect, when we
266 // clear the fd, whether additional work was scheduled after we finished
267 // performing work, but before we cleared the fd, as we'll read back >=2
268 // instead of 1 in that case.
269 // See the eventfd man pages
270 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
271 // the read and write APIs for this file descriptor work, specifically without
272 // EFD_SEMAPHORE.
273 uint64_t value = 1;
274 int ret = write(non_delayed_fd_, &value, sizeof(value));
275 DCHECK_GE(ret, 0);
276 }
277
ScheduleDelayedWork(const TimeTicks & delayed_work_time)278 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
279 if (ShouldQuit())
280 return;
281
282 if (!delayed_scheduled_time_.is_null() &&
283 delayed_work_time >= delayed_scheduled_time_) {
284 return;
285 }
286
287 DCHECK(!delayed_work_time.is_null());
288 delayed_scheduled_time_ = delayed_work_time;
289 int64_t nanos = delayed_work_time.since_origin().InNanoseconds();
290 struct itimerspec ts;
291 ts.it_interval.tv_sec = 0; // Don't repeat.
292 ts.it_interval.tv_nsec = 0;
293 ts.it_value.tv_sec = nanos / TimeTicks::kNanosecondsPerSecond;
294 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
295
296 int ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
297 DCHECK_GE(ret, 0);
298 }
299
QuitWhenIdle(base::OnceClosure callback)300 void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
301 DCHECK(!on_quit_callback_);
302 DCHECK(run_loop_);
303 on_quit_callback_ = std::move(callback);
304 run_loop_->QuitWhenIdle();
305 // Pump the loop in case we're already idle.
306 ScheduleWork();
307 }
308
IsTestImplementation() const309 bool MessagePumpForUI::IsTestImplementation() const {
310 return false;
311 }
312
313 } // namespace base
314