blob: 8b649bf7629e5242cc0bdd90ef4362b2d7316a32 [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
Torne (Richard Coles)c6993a032020-02-19 13:23:4786MessagePumpForUI::MessagePumpForUI()
87 : env_(base::android::AttachCurrentThread()) {
Michael Thiessend7ae7352018-07-10 00:57:1388 // The Android native ALooper uses epoll to poll our file descriptors and wake
89 // us up. We use a simple level-triggered eventfd to signal that non-delayed
90 // work is available, and a timerfd to signal when delayed work is ready to
91 // be run.
92 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
93 CHECK_NE(non_delayed_fd_, -1);
94 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
95
96 // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
97 // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
98 // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
99 // fine.
100 delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC);
101 CHECK_NE(delayed_fd_, -1);
102
103 looper_ = ALooper_prepare(0);
104 DCHECK(looper_);
105 // Add a reference to the looper so it isn't deleted on us.
106 ALooper_acquire(looper_);
107 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
108 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
109 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
110 &DelayedLooperCallback, reinterpret_cast<void*>(this));
111}
112
113MessagePumpForUI::~MessagePumpForUI() {
114 DCHECK_EQ(ALooper_forThread(), looper_);
115 ALooper_removeFd(looper_, non_delayed_fd_);
116 ALooper_removeFd(looper_, delayed_fd_);
117 ALooper_release(looper_);
118 looper_ = nullptr;
119
120 close(non_delayed_fd_);
121 close(delayed_fd_);
122}
123
124void MessagePumpForUI::OnDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47125 // There may be non-Chromium callbacks on the same ALooper which may have left
126 // a pending exception set, and ALooper does not check for this between
127 // callbacks. Check here, and if there's already an exception, just skip this
128 // iteration without clearing the fd. If the exception ends up being non-fatal
129 // then we'll just get called again on the next polling iteration.
130 if (base::android::HasException(env_))
131 return;
132
Gabriel Charette9d44a9b2019-04-29 16:35:56133 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
134 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13135 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01136 return;
137
Michael Thiessend7ae7352018-07-10 00:57:13138 // Clear the fd.
139 uint64_t value;
140 int ret = read(delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54141
142 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
143 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
144 // EAGAIN only happens if no timer has expired. Also according to the man page
145 // poll only returns readable when a timer has expired. So this function will
146 // only be called when a timer has expired, but reading reveals no timer has
147 // expired...
148 // Quit() and ScheduleDelayedWork() are the only other functions that touch
149 // the timerfd, and they both run on the same thread as this callback, so
150 // there are no obvious timing or multi-threading related issues.
151 DPCHECK(ret >= 0 || errno == EAGAIN);
152
Gabriel Charette9d44a9b2019-04-29 16:35:56153 delayed_scheduled_time_.reset();
[email protected]61c86c62011-08-02 16:11:16154
Gabriel Charette9d44a9b2019-04-29 16:35:56155 Delegate::NextWorkInfo next_work_info = delegate_->DoSomeWork();
156
Michael Thiessend7ae7352018-07-10 00:57:13157 if (ShouldQuit())
158 return;
Gabriel Charette9d44a9b2019-04-29 16:35:56159
160 if (next_work_info.is_immediate()) {
161 ScheduleWork();
162 return;
163 }
164
165 DoIdleWork();
166 if (!next_work_info.delayed_run_time.is_max())
167 ScheduleDelayedWork(next_work_info.delayed_run_time);
Michael Thiessend7ae7352018-07-10 00:57:13168}
169
170void MessagePumpForUI::OnNonDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47171 // There may be non-Chromium callbacks on the same ALooper which may have left
172 // a pending exception set, and ALooper does not check for this between
173 // callbacks. Check here, and if there's already an exception, just skip this
174 // iteration without clearing the fd. If the exception ends up being non-fatal
175 // then we'll just get called again on the next polling iteration.
176 if (base::android::HasException(env_))
177 return;
178
Gabriel Charette9d44a9b2019-04-29 16:35:56179 // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
180 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13181 if (ShouldQuit())
182 return;
183
Gabriel Charette9d44a9b2019-04-29 16:35:56184 // A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
Gabriel Charette1c6e0a22019-04-30 11:15:14185 // native tasks below.
186 constexpr uint64_t kTryNativeTasksBeforeIdleBit = uint64_t(1) << 32;
Michael Thiessend7ae7352018-07-10 00:57:13187
Gabriel Charette9d44a9b2019-04-29 16:35:56188 // We're about to process all the work requested by ScheduleWork().
189 // MessagePump users are expected to do their best not to invoke
190 // ScheduleWork() again before DoSomeWork() returns a non-immediate
191 // NextWorkInfo below. Hence, capturing the file descriptor's value now and
192 // resetting its contents to 0 should be okay. The value currently stored
193 // should be greater than 0 since work having been scheduled is the reason
194 // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
195 uint64_t pre_work_value = 0;
196 int ret = read(non_delayed_fd_, &pre_work_value, sizeof(pre_work_value));
197 DPCHECK(ret >= 0);
198 DCHECK_GT(pre_work_value, 0U);
199
200 // Note: We can't skip DoSomeWork() even if
Gabriel Charette1c6e0a22019-04-30 11:15:14201 // |pre_work_value == kTryNativeTasksBeforeIdleBit| here (i.e. no additional
202 // ScheduleWork() since yielding to native) as delayed tasks might have come
203 // in and we need to re-sample |next_work_info|.
Gabriel Charette9d44a9b2019-04-29 16:35:56204
205 // Runs all application tasks scheduled to run.
206 Delegate::NextWorkInfo next_work_info;
207 do {
208 if (ShouldQuit())
209 return;
210
211 next_work_info = delegate_->DoSomeWork();
212 } while (next_work_info.is_immediate());
213
214 // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
215 // allow nesting so needing to resume in an outer loop is not an issue
216 // either).
217 if (ShouldQuit())
218 return;
219
Gabriel Charette1c6e0a22019-04-30 11:15:14220 // Before declaring this loop idle, yield to native tasks and arrange to be
Gabriel Charette9d44a9b2019-04-29 16:35:56221 // called again (unless we're already in that second call).
Gabriel Charette1c6e0a22019-04-30 11:15:14222 if (pre_work_value != kTryNativeTasksBeforeIdleBit) {
Gabriel Charette9d44a9b2019-04-29 16:35:56223 // Note: This write() is racing with potential ScheduleWork() calls. This is
224 // fine as write() is adding this bit, not overwriting the existing value,
225 // and as such racing ScheduleWork() calls would merely add 1 to the lower
Gabriel Charette1c6e0a22019-04-30 11:15:14226 // bits and we would find |pre_work_value != kTryNativeTasksBeforeIdleBit|
227 // in the next cycle again, retrying this.
228 ret = write(non_delayed_fd_, &kTryNativeTasksBeforeIdleBit,
229 sizeof(kTryNativeTasksBeforeIdleBit));
Gabriel Charette9d44a9b2019-04-29 16:35:56230 DPCHECK(ret >= 0);
231 return;
Michael Thiessend7ae7352018-07-10 00:57:13232 }
Gabriel Charette9d44a9b2019-04-29 16:35:56233
Gabriel Charette1c6e0a22019-04-30 11:15:14234 // We yielded to native tasks already and they didn't generate a
235 // ScheduleWork() request so we can declare idleness. It's possible for a
236 // ScheduleWork() request to come in racily while this method unwinds, this is
237 // fine and will merely result in it being re-invoked shortly after it
238 // returns.
239 // TODO(scheduler-dev): this doesn't account for tasks that don't ever call
240 // SchedulerWork() but still keep the system non-idle (e.g., the Java Handler
241 // API). It would be better to add an API to query the presence of native
242 // tasks instead of relying on yielding once + kTryNativeTasksBeforeIdleBit.
243 DCHECK_EQ(pre_work_value, kTryNativeTasksBeforeIdleBit);
Gabriel Charette9d44a9b2019-04-29 16:35:56244
245 if (ShouldQuit())
246 return;
247
248 // At this point, the java looper might not be idle - it's impossible to know
249 // pre-Android-M, so we may end up doing Idle work while java tasks are still
250 // queued up. Note that this won't cause us to fail to run java tasks using
251 // QuitWhenIdle, as the JavaHandlerThread will finish running all currently
252 // scheduled tasks before it quits. Also note that we can't just add an idle
253 // callback to the java looper, as that will fire even if application tasks
254 // are still queued up.
255 DoIdleWork();
256 if (!next_work_info.delayed_run_time.is_max())
257 ScheduleDelayedWork(next_work_info.delayed_run_time);
Michael Thiessend7ae7352018-07-10 00:57:13258}
259
260void MessagePumpForUI::DoIdleWork() {
261 if (delegate_->DoIdleWork()) {
262 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
263 // the loop here because we may in fact be idle after doing idle work
264 // without any new tasks being queued.
265 ScheduleWork();
266 }
[email protected]61c86c62011-08-02 16:11:16267}
268
[email protected]61c86c62011-08-02 16:11:16269void MessagePumpForUI::Run(Delegate* delegate) {
Michael Thiessend7ae7352018-07-10 00:57:13270 DCHECK(IsTestImplementation());
271 // This function is only called in tests. We manually pump the native looper
272 // which won't run any java tasks.
273 quit_ = false;
274
275 SetDelegate(delegate);
276
277 // Pump the loop once in case we're starting off idle as ALooper_pollOnce will
278 // never return in that case.
279 ScheduleWork();
280 while (true) {
281 // Waits for either the delayed, or non-delayed fds to be signalled, calling
282 // either OnDelayedLooperCallback, or OnNonDelayedLooperCallback,
283 // respectively. This uses Android's Looper implementation, which is based
284 // off of epoll.
285 ALooper_pollOnce(-1, nullptr, nullptr, nullptr);
286 if (quit_)
287 break;
288 }
[email protected]61c86c62011-08-02 16:11:16289}
290
Ran Ji3d6ec662018-07-09 21:18:30291void MessagePumpForUI::Attach(Delegate* delegate) {
Michael Thiessendbeca242017-08-28 21:10:08292 DCHECK(!quit_);
Michael Thiessend7ae7352018-07-10 00:57:13293
294 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
295 // can't use Run() like we do on other platforms or we would prevent Java
296 // tasks from running. Instead we create and initialize a run loop here, then
297 // return control back to the Looper.
298
299 SetDelegate(delegate);
Michael Thiessen781ddeb2017-11-15 17:07:23300 run_loop_ = std::make_unique<RunLoop>();
[email protected]8e937c1e2012-06-28 22:57:30301 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
302 // return true (it only returns false if the RunLoop has been Quit already).
303 if (!run_loop_->BeforeRun())
304 NOTREACHED();
[email protected]61c86c62011-08-02 16:11:16305}
306
307void MessagePumpForUI::Quit() {
Michael Thiessend7ae7352018-07-10 00:57:13308 if (quit_)
309 return;
310
Michael Thiessendbeca242017-08-28 21:10:08311 quit_ = true;
Michael Thiessen781ddeb2017-11-15 17:07:23312
Michael Thiessend7ae7352018-07-10 00:57:13313 int64_t value;
314 // Clear any pending timer.
315 read(delayed_fd_, &value, sizeof(value));
316 // Clear the eventfd.
317 read(non_delayed_fd_, &value, sizeof(value));
[email protected]61c86c62011-08-02 16:11:16318
[email protected]8e937c1e2012-06-28 22:57:30319 if (run_loop_) {
320 run_loop_->AfterRun();
Michael Thiessen781ddeb2017-11-15 17:07:23321 run_loop_ = nullptr;
[email protected]61c86c62011-08-02 16:11:16322 }
Michael Thiessend7ae7352018-07-10 00:57:13323 if (on_quit_callback_) {
324 std::move(on_quit_callback_).Run();
325 }
[email protected]61c86c62011-08-02 16:11:16326}
327
328void MessagePumpForUI::ScheduleWork() {
Michael Thiessend7ae7352018-07-10 00:57:13329 // Write (add) 1 to the eventfd. This tells the Looper to wake up and call our
330 // callback, allowing us to run tasks. This also allows us to detect, when we
331 // clear the fd, whether additional work was scheduled after we finished
332 // performing work, but before we cleared the fd, as we'll read back >=2
333 // instead of 1 in that case.
334 // See the eventfd man pages
335 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
336 // the read and write APIs for this file descriptor work, specifically without
337 // EFD_SEMAPHORE.
338 uint64_t value = 1;
339 int ret = write(non_delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54340 DPCHECK(ret >= 0);
[email protected]61c86c62011-08-02 16:11:16341}
342
343void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
Michael Thiessend7ae7352018-07-10 00:57:13344 if (ShouldQuit())
Michael Thiessendbeca242017-08-28 21:10:08345 return;
Michael Thiessend7ae7352018-07-10 00:57:13346
Gabriel Charette9d44a9b2019-04-29 16:35:56347 if (delayed_scheduled_time_ && *delayed_scheduled_time_ == delayed_work_time)
Michael Thiessen781ddeb2017-11-15 17:07:23348 return;
Michael Thiessend7ae7352018-07-10 00:57:13349
Michael Thiessen781ddeb2017-11-15 17:07:23350 DCHECK(!delayed_work_time.is_null());
Michael Thiessen781ddeb2017-11-15 17:07:23351 delayed_scheduled_time_ = delayed_work_time;
Michael Thiessend7ae7352018-07-10 00:57:13352 int64_t nanos = delayed_work_time.since_origin().InNanoseconds();
353 struct itimerspec ts;
354 ts.it_interval.tv_sec = 0; // Don't repeat.
355 ts.it_interval.tv_nsec = 0;
356 ts.it_value.tv_sec = nanos / TimeTicks::kNanosecondsPerSecond;
357 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
358
359 int ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
Michael Thiessen7c36083d2018-08-10 20:24:54360 DPCHECK(ret >= 0);
Michael Thiessend7ae7352018-07-10 00:57:13361}
362
363void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
364 DCHECK(!on_quit_callback_);
365 DCHECK(run_loop_);
366 on_quit_callback_ = std::move(callback);
367 run_loop_->QuitWhenIdle();
368 // Pump the loop in case we're already idle.
369 ScheduleWork();
370}
371
372bool MessagePumpForUI::IsTestImplementation() const {
373 return false;
[email protected]61c86c62011-08-02 16:11:16374}
375
[email protected]61c86c62011-08-02 16:11:16376} // namespace base