blob: d70245832be8f041a4af1cb1ceda792eacf167a0 [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"
Hans Wennborgc3cffa62020-04-27 10:09:1220#include "base/check_op.h"
[email protected]449019ca2012-03-14 22:17:0021#include "base/lazy_instance.h"
Hans Wennborgc3cffa62020-04-27 10:09:1222#include "base/notreached.h"
[email protected]8e937c1e2012-06-28 22:57:3023#include "base/run_loop.h"
Michael Thiessen6fd43902018-08-30 23:14:1524#include "build/build_config.h"
Michael Thiessend7ae7352018-07-10 00:57:1325
26// Android stripped sys/timerfd.h out of their platform headers, so we have to
27// use syscall to make use of timerfd. Once the min API level is 20, we can
28// directly use timerfd.h.
29#ifndef __NR_timerfd_create
30#error "Unable to find syscall for __NR_timerfd_create"
31#endif
32
33#ifndef TFD_TIMER_ABSTIME
34#define TFD_TIMER_ABSTIME (1 << 0)
35#endif
[email protected]61c86c62011-08-02 16:11:1636
torne86560112016-08-04 15:59:0437using base::android::JavaParamRef;
[email protected]449019ca2012-03-14 22:17:0038using base::android::ScopedJavaLocalRef;
[email protected]61c86c62011-08-02 16:11:1639
Michael Thiessen781ddeb2017-11-15 17:07:2340namespace base {
41
Michael Thiessend7ae7352018-07-10 00:57:1342namespace {
Michael Thiessen781ddeb2017-11-15 17:07:2343
Michael Thiessend7ae7352018-07-10 00:57:1344// See sys/timerfd.h
45int timerfd_create(int clockid, int flags) {
46 return syscall(__NR_timerfd_create, clockid, flags);
Michael Thiessen781ddeb2017-11-15 17:07:2347}
48
Michael Thiessend7ae7352018-07-10 00:57:1349// See sys/timerfd.h
50int timerfd_settime(int ufc,
51 int flags,
52 const struct itimerspec* utmr,
53 struct itimerspec* otmr) {
54 return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
55}
Michael Thiessen781ddeb2017-11-15 17:07:2356
Michael Thiessen6fd43902018-08-30 23:14:1557// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
58// into our code due to the inconsistent ABI on older Android OS versions.
59#if defined(ARCH_CPU_X86)
60#define STACK_ALIGN __attribute__((force_align_arg_pointer))
61#else
62#define STACK_ALIGN
63#endif
64
65STACK_ALIGN int NonDelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1366 if (events & ALOOPER_EVENT_HANGUP)
67 return 0;
68
69 DCHECK(events & ALOOPER_EVENT_INPUT);
70 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
71 pump->OnNonDelayedLooperCallback();
72 return 1; // continue listening for events
73}
74
Michael Thiessen6fd43902018-08-30 23:14:1575STACK_ALIGN int DelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1376 if (events & ALOOPER_EVENT_HANGUP)
77 return 0;
78
79 DCHECK(events & ALOOPER_EVENT_INPUT);
80 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
81 pump->OnDelayedLooperCallback();
82 return 1; // continue listening for events
83}
84
Aaron Colwell48c4d5072020-11-13 16:45:0385// A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
86// native tasks below.
87constexpr uint64_t kTryNativeTasksBeforeIdleBit = uint64_t(1) << 32;
Michael Thiessend7ae7352018-07-10 00:57:1388} // namespace
89
Torne (Richard Coles)c6993a032020-02-19 13:23:4790MessagePumpForUI::MessagePumpForUI()
91 : env_(base::android::AttachCurrentThread()) {
Michael Thiessend7ae7352018-07-10 00:57:1392 // The Android native ALooper uses epoll to poll our file descriptors and wake
93 // us up. We use a simple level-triggered eventfd to signal that non-delayed
94 // work is available, and a timerfd to signal when delayed work is ready to
95 // be run.
96 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
97 CHECK_NE(non_delayed_fd_, -1);
98 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
99
100 // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
101 // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
102 // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
103 // fine.
104 delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC);
105 CHECK_NE(delayed_fd_, -1);
106
107 looper_ = ALooper_prepare(0);
108 DCHECK(looper_);
109 // Add a reference to the looper so it isn't deleted on us.
110 ALooper_acquire(looper_);
111 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
112 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
113 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
114 &DelayedLooperCallback, reinterpret_cast<void*>(this));
115}
116
117MessagePumpForUI::~MessagePumpForUI() {
118 DCHECK_EQ(ALooper_forThread(), looper_);
119 ALooper_removeFd(looper_, non_delayed_fd_);
120 ALooper_removeFd(looper_, delayed_fd_);
121 ALooper_release(looper_);
122 looper_ = nullptr;
123
124 close(non_delayed_fd_);
125 close(delayed_fd_);
126}
127
128void MessagePumpForUI::OnDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47129 // There may be non-Chromium callbacks on the same ALooper which may have left
130 // a pending exception set, and ALooper does not check for this between
131 // callbacks. Check here, and if there's already an exception, just skip this
132 // iteration without clearing the fd. If the exception ends up being non-fatal
133 // then we'll just get called again on the next polling iteration.
134 if (base::android::HasException(env_))
135 return;
136
Gabriel Charette9d44a9b2019-04-29 16:35:56137 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
138 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13139 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01140 return;
141
Michael Thiessend7ae7352018-07-10 00:57:13142 // Clear the fd.
143 uint64_t value;
144 int ret = read(delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54145
146 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
147 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
148 // EAGAIN only happens if no timer has expired. Also according to the man page
149 // poll only returns readable when a timer has expired. So this function will
150 // only be called when a timer has expired, but reading reveals no timer has
151 // expired...
152 // Quit() and ScheduleDelayedWork() are the only other functions that touch
153 // the timerfd, and they both run on the same thread as this callback, so
154 // there are no obvious timing or multi-threading related issues.
155 DPCHECK(ret >= 0 || errno == EAGAIN);
Aaron Colwell48c4d5072020-11-13 16:45:03156 DoDelayedLooperWork();
157}
Michael Thiessen7c36083d2018-08-10 20:24:54158
Aaron Colwell48c4d5072020-11-13 16:45:03159void MessagePumpForUI::DoDelayedLooperWork() {
Gabriel Charette9d44a9b2019-04-29 16:35:56160 delayed_scheduled_time_.reset();
[email protected]61c86c62011-08-02 16:11:16161
Etienne Pierre-doray2163f3012020-04-02 21:37:14162 Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
Gabriel Charette9d44a9b2019-04-29 16:35:56163
Michael Thiessend7ae7352018-07-10 00:57:13164 if (ShouldQuit())
165 return;
Gabriel Charette9d44a9b2019-04-29 16:35:56166
167 if (next_work_info.is_immediate()) {
168 ScheduleWork();
169 return;
170 }
171
172 DoIdleWork();
173 if (!next_work_info.delayed_run_time.is_max())
174 ScheduleDelayedWork(next_work_info.delayed_run_time);
Michael Thiessend7ae7352018-07-10 00:57:13175}
176
177void MessagePumpForUI::OnNonDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47178 // There may be non-Chromium callbacks on the same ALooper which may have left
179 // a pending exception set, and ALooper does not check for this between
180 // callbacks. Check here, and if there's already an exception, just skip this
181 // iteration without clearing the fd. If the exception ends up being non-fatal
182 // then we'll just get called again on the next polling iteration.
183 if (base::android::HasException(env_))
184 return;
185
Gabriel Charette9d44a9b2019-04-29 16:35:56186 // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
187 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13188 if (ShouldQuit())
189 return;
190
Gabriel Charette9d44a9b2019-04-29 16:35:56191 // We're about to process all the work requested by ScheduleWork().
192 // MessagePump users are expected to do their best not to invoke
Etienne Pierre-doray2163f3012020-04-02 21:37:14193 // ScheduleWork() again before DoWork() returns a non-immediate
Gabriel Charette9d44a9b2019-04-29 16:35:56194 // NextWorkInfo below. Hence, capturing the file descriptor's value now and
195 // resetting its contents to 0 should be okay. The value currently stored
196 // should be greater than 0 since work having been scheduled is the reason
197 // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
Aaron Colwell48c4d5072020-11-13 16:45:03198 uint64_t value = 0;
199 int ret = read(non_delayed_fd_, &value, sizeof(value));
Gabriel Charette9d44a9b2019-04-29 16:35:56200 DPCHECK(ret >= 0);
Aaron Colwell48c4d5072020-11-13 16:45:03201 DCHECK_GT(value, 0U);
202 bool do_idle_work = value == kTryNativeTasksBeforeIdleBit;
203 DoNonDelayedLooperWork(do_idle_work);
204}
Gabriel Charette9d44a9b2019-04-29 16:35:56205
Aaron Colwell48c4d5072020-11-13 16:45:03206void MessagePumpForUI::DoNonDelayedLooperWork(bool do_idle_work) {
207 // Note: We can't skip DoWork() even if |do_idle_work| is true here (i.e. no
208 // additional ScheduleWork() since yielding to native) as delayed tasks might
209 // have come in and we need to re-sample |next_work_info|.
Gabriel Charette9d44a9b2019-04-29 16:35:56210
211 // Runs all application tasks scheduled to run.
212 Delegate::NextWorkInfo next_work_info;
213 do {
214 if (ShouldQuit())
215 return;
216
Etienne Pierre-doray2163f3012020-04-02 21:37:14217 next_work_info = delegate_->DoWork();
Stephen Nusko133b4e42021-06-01 22:06:23218 // If we are prioritizing native, and the next work would normally run
219 // immediately, skip the next work and let the native tasks have a chance to
220 // run. This is useful when user input is waiting for native to have a
221 // chance to run.
222 if (next_work_info.is_immediate() && next_work_info.yield_to_native) {
223 ScheduleWork();
224 return;
225 }
Gabriel Charette9d44a9b2019-04-29 16:35:56226 } while (next_work_info.is_immediate());
227
228 // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
229 // allow nesting so needing to resume in an outer loop is not an issue
230 // either).
231 if (ShouldQuit())
232 return;
233
Gabriel Charette1c6e0a22019-04-30 11:15:14234 // Before declaring this loop idle, yield to native tasks and arrange to be
Gabriel Charette9d44a9b2019-04-29 16:35:56235 // called again (unless we're already in that second call).
Aaron Colwell48c4d5072020-11-13 16:45:03236 if (!do_idle_work) {
237 ScheduleWorkInternal(/*do_idle_work=*/true);
Gabriel Charette9d44a9b2019-04-29 16:35:56238 return;
Michael Thiessend7ae7352018-07-10 00:57:13239 }
Gabriel Charette9d44a9b2019-04-29 16:35:56240
Gabriel Charette1c6e0a22019-04-30 11:15:14241 // We yielded to native tasks already and they didn't generate a
242 // ScheduleWork() request so we can declare idleness. It's possible for a
243 // ScheduleWork() request to come in racily while this method unwinds, this is
244 // fine and will merely result in it being re-invoked shortly after it
245 // returns.
246 // TODO(scheduler-dev): this doesn't account for tasks that don't ever call
247 // SchedulerWork() but still keep the system non-idle (e.g., the Java Handler
248 // API). It would be better to add an API to query the presence of native
249 // tasks instead of relying on yielding once + kTryNativeTasksBeforeIdleBit.
Aaron Colwell48c4d5072020-11-13 16:45:03250 DCHECK(do_idle_work);
Gabriel Charette9d44a9b2019-04-29 16:35:56251
252 if (ShouldQuit())
253 return;
254
255 // At this point, the java looper might not be idle - it's impossible to know
256 // pre-Android-M, so we may end up doing Idle work while java tasks are still
257 // queued up. Note that this won't cause us to fail to run java tasks using
258 // QuitWhenIdle, as the JavaHandlerThread will finish running all currently
259 // scheduled tasks before it quits. Also note that we can't just add an idle
260 // callback to the java looper, as that will fire even if application tasks
261 // are still queued up.
262 DoIdleWork();
263 if (!next_work_info.delayed_run_time.is_max())
264 ScheduleDelayedWork(next_work_info.delayed_run_time);
Michael Thiessend7ae7352018-07-10 00:57:13265}
266
267void MessagePumpForUI::DoIdleWork() {
268 if (delegate_->DoIdleWork()) {
269 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
270 // the loop here because we may in fact be idle after doing idle work
271 // without any new tasks being queued.
272 ScheduleWork();
273 }
[email protected]61c86c62011-08-02 16:11:16274}
275
[email protected]61c86c62011-08-02 16:11:16276void MessagePumpForUI::Run(Delegate* delegate) {
Aaron Colwelld34116aaa2020-11-11 02:15:37277 CHECK(false) << "Unexpected call to Run()";
[email protected]61c86c62011-08-02 16:11:16278}
279
Ran Ji3d6ec662018-07-09 21:18:30280void MessagePumpForUI::Attach(Delegate* delegate) {
Michael Thiessendbeca242017-08-28 21:10:08281 DCHECK(!quit_);
Michael Thiessend7ae7352018-07-10 00:57:13282
283 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
284 // can't use Run() like we do on other platforms or we would prevent Java
285 // tasks from running. Instead we create and initialize a run loop here, then
286 // return control back to the Looper.
287
288 SetDelegate(delegate);
Michael Thiessen781ddeb2017-11-15 17:07:23289 run_loop_ = std::make_unique<RunLoop>();
[email protected]8e937c1e2012-06-28 22:57:30290 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
291 // return true (it only returns false if the RunLoop has been Quit already).
292 if (!run_loop_->BeforeRun())
293 NOTREACHED();
[email protected]61c86c62011-08-02 16:11:16294}
295
296void MessagePumpForUI::Quit() {
Michael Thiessend7ae7352018-07-10 00:57:13297 if (quit_)
298 return;
299
Michael Thiessendbeca242017-08-28 21:10:08300 quit_ = true;
Michael Thiessen781ddeb2017-11-15 17:07:23301
Michael Thiessend7ae7352018-07-10 00:57:13302 int64_t value;
303 // Clear any pending timer.
304 read(delayed_fd_, &value, sizeof(value));
305 // Clear the eventfd.
306 read(non_delayed_fd_, &value, sizeof(value));
[email protected]61c86c62011-08-02 16:11:16307
[email protected]8e937c1e2012-06-28 22:57:30308 if (run_loop_) {
309 run_loop_->AfterRun();
Michael Thiessen781ddeb2017-11-15 17:07:23310 run_loop_ = nullptr;
[email protected]61c86c62011-08-02 16:11:16311 }
Michael Thiessend7ae7352018-07-10 00:57:13312 if (on_quit_callback_) {
313 std::move(on_quit_callback_).Run();
314 }
[email protected]61c86c62011-08-02 16:11:16315}
316
317void MessagePumpForUI::ScheduleWork() {
Aaron Colwell48c4d5072020-11-13 16:45:03318 ScheduleWorkInternal(/*do_idle_work=*/false);
319}
320
321void MessagePumpForUI::ScheduleWorkInternal(bool do_idle_work) {
322 // Write (add) |value| to the eventfd. This tells the Looper to wake up and
323 // call our callback, allowing us to run tasks. This also allows us to detect,
324 // when we clear the fd, whether additional work was scheduled after we
325 // finished performing work, but before we cleared the fd, as we'll read back
326 // >=2 instead of 1 in that case. See the eventfd man pages
Michael Thiessend7ae7352018-07-10 00:57:13327 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
328 // the read and write APIs for this file descriptor work, specifically without
329 // EFD_SEMAPHORE.
Aaron Colwell48c4d5072020-11-13 16:45:03330 // Note: Calls with |do_idle_work| set to true may race with potential calls
331 // where the parameter is false. This is fine as write() is adding |value|,
332 // not overwriting the existing value, and as such racing calls would merely
333 // have their values added together. Since idle work is only executed when the
334 // value read equals kTryNativeTasksBeforeIdleBit, a race would prevent idle
335 // work from being run and trigger another call to this method with
336 // |do_idle_work| set to true.
337 uint64_t value = do_idle_work ? kTryNativeTasksBeforeIdleBit : 1;
Michael Thiessend7ae7352018-07-10 00:57:13338 int ret = write(non_delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54339 DPCHECK(ret >= 0);
[email protected]61c86c62011-08-02 16:11:16340}
341
342void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
Michael Thiessend7ae7352018-07-10 00:57:13343 if (ShouldQuit())
Michael Thiessendbeca242017-08-28 21:10:08344 return;
Michael Thiessend7ae7352018-07-10 00:57:13345
Gabriel Charette9d44a9b2019-04-29 16:35:56346 if (delayed_scheduled_time_ && *delayed_scheduled_time_ == delayed_work_time)
Michael Thiessen781ddeb2017-11-15 17:07:23347 return;
Michael Thiessend7ae7352018-07-10 00:57:13348
Michael Thiessen781ddeb2017-11-15 17:07:23349 DCHECK(!delayed_work_time.is_null());
Michael Thiessen781ddeb2017-11-15 17:07:23350 delayed_scheduled_time_ = delayed_work_time;
Michael Thiessend7ae7352018-07-10 00:57:13351 int64_t nanos = delayed_work_time.since_origin().InNanoseconds();
352 struct itimerspec ts;
353 ts.it_interval.tv_sec = 0; // Don't repeat.
354 ts.it_interval.tv_nsec = 0;
355 ts.it_value.tv_sec = nanos / TimeTicks::kNanosecondsPerSecond;
356 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
357
358 int ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
Michael Thiessen7c36083d2018-08-10 20:24:54359 DPCHECK(ret >= 0);
Michael Thiessend7ae7352018-07-10 00:57:13360}
361
362void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
363 DCHECK(!on_quit_callback_);
364 DCHECK(run_loop_);
365 on_quit_callback_ = std::move(callback);
366 run_loop_->QuitWhenIdle();
367 // Pump the loop in case we're already idle.
368 ScheduleWork();
369}
370
Aaron Colwell48c4d5072020-11-13 16:45:03371MessagePump::Delegate* MessagePumpForUI::SetDelegate(Delegate* delegate) {
372 return std::exchange(delegate_, delegate);
373}
374
375bool MessagePumpForUI::SetQuit(bool quit) {
376 return std::exchange(quit_, quit);
377}
378
[email protected]61c86c62011-08-02 16:11:16379} // namespace base