blob: 70e72f0e0b5b8ec05aeb31ba705f70650f5406a9 [file] [log] [blame]
[email protected]449019ca2012-03-14 22:17:001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]61c86c62011-08-02 16:11:162// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]59e69e742013-06-18 20:27:525#include "base/message_loop/message_pump_android.h"
[email protected]61c86c62011-08-02 16:11:166
Michael Thiessend7ae7352018-07-10 00:57:137#include <android/looper.h>
8#include <errno.h>
9#include <fcntl.h>
[email protected]61c86c62011-08-02 16:11:1610#include <jni.h>
Michael Thiessend7ae7352018-07-10 00:57:1311#include <sys/eventfd.h>
12#include <sys/syscall.h>
13#include <sys/types.h>
14#include <unistd.h>
15#include <utility>
[email protected]61c86c62011-08-02 16:11:1616
17#include "base/android/jni_android.h"
[email protected]449019ca2012-03-14 22:17:0018#include "base/android/scoped_java_ref.h"
Michael Thiessend7ae7352018-07-10 00:57:1319#include "base/callback_helpers.h"
[email protected]449019ca2012-03-14 22:17:0020#include "base/lazy_instance.h"
[email protected]61c86c62011-08-02 16:11:1621#include "base/logging.h"
[email protected]8e937c1e2012-06-28 22:57:3022#include "base/run_loop.h"
Michael Thiessen6fd43902018-08-30 23:14:1523#include "build/build_config.h"
Michael Thiessend7ae7352018-07-10 00:57:1324
25// Android stripped sys/timerfd.h out of their platform headers, so we have to
26// use syscall to make use of timerfd. Once the min API level is 20, we can
27// directly use timerfd.h.
28#ifndef __NR_timerfd_create
29#error "Unable to find syscall for __NR_timerfd_create"
30#endif
31
32#ifndef TFD_TIMER_ABSTIME
33#define TFD_TIMER_ABSTIME (1 << 0)
34#endif
[email protected]61c86c62011-08-02 16:11:1635
torne86560112016-08-04 15:59:0436using base::android::JavaParamRef;
[email protected]449019ca2012-03-14 22:17:0037using base::android::ScopedJavaLocalRef;
[email protected]61c86c62011-08-02 16:11:1638
Michael Thiessen781ddeb2017-11-15 17:07:2339namespace base {
40
Michael Thiessend7ae7352018-07-10 00:57:1341namespace {
Michael Thiessen781ddeb2017-11-15 17:07:2342
Michael Thiessend7ae7352018-07-10 00:57:1343// See sys/timerfd.h
44int timerfd_create(int clockid, int flags) {
45 return syscall(__NR_timerfd_create, clockid, flags);
Michael Thiessen781ddeb2017-11-15 17:07:2346}
47
Michael Thiessend7ae7352018-07-10 00:57:1348// See sys/timerfd.h
49int timerfd_settime(int ufc,
50 int flags,
51 const struct itimerspec* utmr,
52 struct itimerspec* otmr) {
53 return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
54}
Michael Thiessen781ddeb2017-11-15 17:07:2355
Michael Thiessen6fd43902018-08-30 23:14:1556// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
57// into our code due to the inconsistent ABI on older Android OS versions.
58#if defined(ARCH_CPU_X86)
59#define STACK_ALIGN __attribute__((force_align_arg_pointer))
60#else
61#define STACK_ALIGN
62#endif
63
64STACK_ALIGN int NonDelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1365 if (events & ALOOPER_EVENT_HANGUP)
66 return 0;
67
68 DCHECK(events & ALOOPER_EVENT_INPUT);
69 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
70 pump->OnNonDelayedLooperCallback();
71 return 1; // continue listening for events
72}
73
Michael Thiessen6fd43902018-08-30 23:14:1574STACK_ALIGN int DelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1375 if (events & ALOOPER_EVENT_HANGUP)
76 return 0;
77
78 DCHECK(events & ALOOPER_EVENT_INPUT);
79 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
80 pump->OnDelayedLooperCallback();
81 return 1; // continue listening for events
82}
83
84} // namespace
85
86MessagePumpForUI::MessagePumpForUI() {
87 // The Android native ALooper uses epoll to poll our file descriptors and wake
88 // us up. We use a simple level-triggered eventfd to signal that non-delayed
89 // work is available, and a timerfd to signal when delayed work is ready to
90 // be run.
91 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
92 CHECK_NE(non_delayed_fd_, -1);
93 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
94
95 // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
96 // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
97 // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
98 // fine.
99 delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC);
100 CHECK_NE(delayed_fd_, -1);
101
102 looper_ = ALooper_prepare(0);
103 DCHECK(looper_);
104 // Add a reference to the looper so it isn't deleted on us.
105 ALooper_acquire(looper_);
106 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
107 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
108 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
109 &DelayedLooperCallback, reinterpret_cast<void*>(this));
110}
111
112MessagePumpForUI::~MessagePumpForUI() {
113 DCHECK_EQ(ALooper_forThread(), looper_);
114 ALooper_removeFd(looper_, non_delayed_fd_);
115 ALooper_removeFd(looper_, delayed_fd_);
116 ALooper_release(looper_);
117 looper_ = nullptr;
118
119 close(non_delayed_fd_);
120 close(delayed_fd_);
121}
122
123void MessagePumpForUI::OnDelayedLooperCallback() {
124 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01125 return;
126
Michael Thiessend7ae7352018-07-10 00:57:13127 // Clear the fd.
128 uint64_t value;
129 int ret = read(delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54130
131 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
132 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
133 // EAGAIN only happens if no timer has expired. Also according to the man page
134 // poll only returns readable when a timer has expired. So this function will
135 // only be called when a timer has expired, but reading reveals no timer has
136 // expired...
137 // Quit() and ScheduleDelayedWork() are the only other functions that touch
138 // the timerfd, and they both run on the same thread as this callback, so
139 // there are no obvious timing or multi-threading related issues.
140 DPCHECK(ret >= 0 || errno == EAGAIN);
141
Michael Thiessend7ae7352018-07-10 00:57:13142 delayed_scheduled_time_ = base::TimeTicks();
[email protected]61c86c62011-08-02 16:11:16143
[email protected]97532f3f2014-03-11 09:24:42144 base::TimeTicks next_delayed_work_time;
Michael Thiessen781ddeb2017-11-15 17:07:23145 delegate_->DoDelayedWork(&next_delayed_work_time);
Michael Thiessend7ae7352018-07-10 00:57:13146 if (!next_delayed_work_time.is_null()) {
Michael Thiessen781ddeb2017-11-15 17:07:23147 ScheduleDelayedWork(next_delayed_work_time);
Michael Thiessend7ae7352018-07-10 00:57:13148 }
149 if (ShouldQuit())
150 return;
151 // We may be idle now, so pump the loop to find out.
152 ScheduleWork();
153}
154
155void MessagePumpForUI::OnNonDelayedLooperCallback() {
156 base::TimeTicks next_delayed_work_time;
157 bool did_any_work = false;
158
159 // Runs all native tasks scheduled to run, scheduling delayed work if
160 // necessary.
161 while (true) {
162 bool did_work_this_loop = false;
163 if (ShouldQuit())
164 return;
165 did_work_this_loop = delegate_->DoWork();
166 if (ShouldQuit())
167 return;
168
169 did_work_this_loop |= delegate_->DoDelayedWork(&next_delayed_work_time);
170
171 did_any_work |= did_work_this_loop;
172
173 // If we didn't do any work, we're out of native tasks to run, and we should
174 // return control to the looper to run Java tasks.
175 if (!did_work_this_loop)
176 break;
177 }
178 // If we did any work, return control to the looper to run java tasks before
179 // we call DoIdleWork(). We haven't cleared the fd yet, so we'll get woken up
180 // again soon to check for idle-ness.
181 if (did_any_work)
182 return;
183 if (ShouldQuit())
184 return;
185
186 // Read the file descriptor, resetting its contents to 0 and reading back the
187 // stored value.
188 // See http://man7.org/linux/man-pages/man2/eventfd.2.html
189 uint64_t value = 0;
190 int ret = read(non_delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54191 DPCHECK(ret >= 0);
Michael Thiessend7ae7352018-07-10 00:57:13192
193 // If we read a value > 1, it means we lost the race to clear the fd before a
194 // new task was posted. This is okay, we can just re-schedule work.
195 if (value > 1) {
196 ScheduleWork();
197 } else {
198 // At this point, the java looper might not be idle - it's impossible to
199 // know pre-Android-M, so we may end up doing Idle work while java tasks are
200 // still queued up. Note that this won't cause us to fail to run java tasks
201 // using QuitWhenIdle, as the JavaHandlerThread will finish running all
202 // currently scheduled tasks before it quits. Also note that we can't just
203 // add an idle callback to the java looper, as that will fire even if native
204 // tasks are still queued up.
205 DoIdleWork();
206 if (!next_delayed_work_time.is_null()) {
207 ScheduleDelayedWork(next_delayed_work_time);
208 }
209 }
210}
211
212void MessagePumpForUI::DoIdleWork() {
213 if (delegate_->DoIdleWork()) {
214 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
215 // the loop here because we may in fact be idle after doing idle work
216 // without any new tasks being queued.
217 ScheduleWork();
218 }
[email protected]61c86c62011-08-02 16:11:16219}
220
[email protected]61c86c62011-08-02 16:11:16221void MessagePumpForUI::Run(Delegate* delegate) {
Michael Thiessend7ae7352018-07-10 00:57:13222 DCHECK(IsTestImplementation());
223 // This function is only called in tests. We manually pump the native looper
224 // which won't run any java tasks.
225 quit_ = false;
226
227 SetDelegate(delegate);
228
229 // Pump the loop once in case we're starting off idle as ALooper_pollOnce will
230 // never return in that case.
231 ScheduleWork();
232 while (true) {
233 // Waits for either the delayed, or non-delayed fds to be signalled, calling
234 // either OnDelayedLooperCallback, or OnNonDelayedLooperCallback,
235 // respectively. This uses Android's Looper implementation, which is based
236 // off of epoll.
237 ALooper_pollOnce(-1, nullptr, nullptr, nullptr);
238 if (quit_)
239 break;
240 }
[email protected]61c86c62011-08-02 16:11:16241}
242
Ran Ji3d6ec662018-07-09 21:18:30243void MessagePumpForUI::Attach(Delegate* delegate) {
Michael Thiessendbeca242017-08-28 21:10:08244 DCHECK(!quit_);
Michael Thiessend7ae7352018-07-10 00:57:13245
246 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
247 // can't use Run() like we do on other platforms or we would prevent Java
248 // tasks from running. Instead we create and initialize a run loop here, then
249 // return control back to the Looper.
250
251 SetDelegate(delegate);
Michael Thiessen781ddeb2017-11-15 17:07:23252 run_loop_ = std::make_unique<RunLoop>();
[email protected]8e937c1e2012-06-28 22:57:30253 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
254 // return true (it only returns false if the RunLoop has been Quit already).
255 if (!run_loop_->BeforeRun())
256 NOTREACHED();
[email protected]61c86c62011-08-02 16:11:16257}
258
259void MessagePumpForUI::Quit() {
Michael Thiessend7ae7352018-07-10 00:57:13260 if (quit_)
261 return;
262
Michael Thiessendbeca242017-08-28 21:10:08263 quit_ = true;
Michael Thiessen781ddeb2017-11-15 17:07:23264
Michael Thiessend7ae7352018-07-10 00:57:13265 int64_t value;
266 // Clear any pending timer.
267 read(delayed_fd_, &value, sizeof(value));
268 // Clear the eventfd.
269 read(non_delayed_fd_, &value, sizeof(value));
[email protected]61c86c62011-08-02 16:11:16270
[email protected]8e937c1e2012-06-28 22:57:30271 if (run_loop_) {
272 run_loop_->AfterRun();
Michael Thiessen781ddeb2017-11-15 17:07:23273 run_loop_ = nullptr;
[email protected]61c86c62011-08-02 16:11:16274 }
Michael Thiessend7ae7352018-07-10 00:57:13275 if (on_quit_callback_) {
276 std::move(on_quit_callback_).Run();
277 }
[email protected]61c86c62011-08-02 16:11:16278}
279
280void MessagePumpForUI::ScheduleWork() {
Michael Thiessend7ae7352018-07-10 00:57:13281 if (ShouldQuit())
Michael Thiessendbeca242017-08-28 21:10:08282 return;
[email protected]61c86c62011-08-02 16:11:16283
Michael Thiessend7ae7352018-07-10 00:57:13284 // Write (add) 1 to the eventfd. This tells the Looper to wake up and call our
285 // callback, allowing us to run tasks. This also allows us to detect, when we
286 // clear the fd, whether additional work was scheduled after we finished
287 // performing work, but before we cleared the fd, as we'll read back >=2
288 // instead of 1 in that case.
289 // See the eventfd man pages
290 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
291 // the read and write APIs for this file descriptor work, specifically without
292 // EFD_SEMAPHORE.
293 uint64_t value = 1;
294 int ret = write(non_delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54295 DPCHECK(ret >= 0);
[email protected]61c86c62011-08-02 16:11:16296}
297
298void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
Michael Thiessend7ae7352018-07-10 00:57:13299 if (ShouldQuit())
Michael Thiessendbeca242017-08-28 21:10:08300 return;
Michael Thiessend7ae7352018-07-10 00:57:13301
Michael Thiessen781ddeb2017-11-15 17:07:23302 if (!delayed_scheduled_time_.is_null() &&
303 delayed_work_time >= delayed_scheduled_time_) {
304 return;
305 }
Michael Thiessend7ae7352018-07-10 00:57:13306
Michael Thiessen781ddeb2017-11-15 17:07:23307 DCHECK(!delayed_work_time.is_null());
Michael Thiessen781ddeb2017-11-15 17:07:23308 delayed_scheduled_time_ = delayed_work_time;
Michael Thiessend7ae7352018-07-10 00:57:13309 int64_t nanos = delayed_work_time.since_origin().InNanoseconds();
310 struct itimerspec ts;
311 ts.it_interval.tv_sec = 0; // Don't repeat.
312 ts.it_interval.tv_nsec = 0;
313 ts.it_value.tv_sec = nanos / TimeTicks::kNanosecondsPerSecond;
314 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
315
316 int ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
Michael Thiessen7c36083d2018-08-10 20:24:54317 DPCHECK(ret >= 0);
Michael Thiessend7ae7352018-07-10 00:57:13318}
319
320void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
321 DCHECK(!on_quit_callback_);
322 DCHECK(run_loop_);
323 on_quit_callback_ = std::move(callback);
324 run_loop_->QuitWhenIdle();
325 // Pump the loop in case we're already idle.
326 ScheduleWork();
327}
328
329bool MessagePumpForUI::IsTestImplementation() const {
330 return false;
[email protected]61c86c62011-08-02 16:11:16331}
332
[email protected]61c86c62011-08-02 16:11:16333} // namespace base