blob: 54da7208949f12df2c9df564e73d846e4dc0953f [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[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>
Egor Pasko6c9baa332023-04-04 18:08:2212#include <sys/timerfd.h>
Michael Thiessend7ae7352018-07-10 00:57:1313#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"
Hans Wennborgc3cffa62020-04-27 10:09:1219#include "base/check_op.h"
Hans Wennborgc3cffa62020-04-27 10:09:1220#include "base/notreached.h"
Peter Kasting2f61c8b2022-07-19 23:43:4621#include "base/numerics/safe_conversions.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
Michael Thiessen781ddeb2017-11-15 17:07:2325namespace base {
26
Michael Thiessend7ae7352018-07-10 00:57:1327namespace {
Michael Thiessen781ddeb2017-11-15 17:07:2328
Michael Thiessen6fd43902018-08-30 23:14:1529// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
30// into our code due to the inconsistent ABI on older Android OS versions.
31#if defined(ARCH_CPU_X86)
32#define STACK_ALIGN __attribute__((force_align_arg_pointer))
33#else
34#define STACK_ALIGN
35#endif
36
37STACK_ALIGN int NonDelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1338 if (events & ALOOPER_EVENT_HANGUP)
39 return 0;
40
41 DCHECK(events & ALOOPER_EVENT_INPUT);
42 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
43 pump->OnNonDelayedLooperCallback();
44 return 1; // continue listening for events
45}
46
Michael Thiessen6fd43902018-08-30 23:14:1547STACK_ALIGN int DelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1348 if (events & ALOOPER_EVENT_HANGUP)
49 return 0;
50
51 DCHECK(events & ALOOPER_EVENT_INPUT);
52 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
53 pump->OnDelayedLooperCallback();
54 return 1; // continue listening for events
55}
56
Aaron Colwell48c4d5072020-11-13 16:45:0357// A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
Gabriel Charette300f16792022-07-06 20:03:3058// native work below.
59constexpr uint64_t kTryNativeWorkBeforeIdleBit = uint64_t(1) << 32;
Michael Thiessend7ae7352018-07-10 00:57:1360} // namespace
61
Torne (Richard Coles)c6993a032020-02-19 13:23:4762MessagePumpForUI::MessagePumpForUI()
François Doray253a3062022-10-24 16:45:2963 : env_(base::android::AttachCurrentThread()) {
Michael Thiessend7ae7352018-07-10 00:57:1364 // The Android native ALooper uses epoll to poll our file descriptors and wake
65 // us up. We use a simple level-triggered eventfd to signal that non-delayed
66 // work is available, and a timerfd to signal when delayed work is ready to
67 // be run.
68 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
69 CHECK_NE(non_delayed_fd_, -1);
70 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
71
72 // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
73 // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
74 // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
75 // fine.
Peter Kasting2f61c8b2022-07-19 23:43:4676 delayed_fd_ = checked_cast<int>(
77 timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC));
Michael Thiessend7ae7352018-07-10 00:57:1378 CHECK_NE(delayed_fd_, -1);
79
80 looper_ = ALooper_prepare(0);
81 DCHECK(looper_);
82 // Add a reference to the looper so it isn't deleted on us.
83 ALooper_acquire(looper_);
84 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
85 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
86 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
87 &DelayedLooperCallback, reinterpret_cast<void*>(this));
88}
89
90MessagePumpForUI::~MessagePumpForUI() {
91 DCHECK_EQ(ALooper_forThread(), looper_);
92 ALooper_removeFd(looper_, non_delayed_fd_);
93 ALooper_removeFd(looper_, delayed_fd_);
94 ALooper_release(looper_);
95 looper_ = nullptr;
96
97 close(non_delayed_fd_);
98 close(delayed_fd_);
99}
100
101void MessagePumpForUI::OnDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47102 // There may be non-Chromium callbacks on the same ALooper which may have left
103 // a pending exception set, and ALooper does not check for this between
104 // callbacks. Check here, and if there's already an exception, just skip this
105 // iteration without clearing the fd. If the exception ends up being non-fatal
106 // then we'll just get called again on the next polling iteration.
107 if (base::android::HasException(env_))
108 return;
109
Gabriel Charette9d44a9b2019-04-29 16:35:56110 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
111 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13112 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01113 return;
114
François Doray23159042023-03-01 14:14:09115 // Clear the fd.
116 uint64_t value;
117 long ret = read(delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54118
François Doray23159042023-03-01 14:14:09119 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
120 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
121 // EAGAIN only happens if no timer has expired. Also according to the man page
122 // poll only returns readable when a timer has expired. So this function will
123 // only be called when a timer has expired, but reading reveals no timer has
124 // expired...
125 // Quit() and ScheduleDelayedWork() are the only other functions that touch
126 // the timerfd, and they both run on the same thread as this callback, so
127 // there are no obvious timing or multi-threading related issues.
128 DPCHECK(ret >= 0 || errno == EAGAIN);
Aaron Colwell48c4d5072020-11-13 16:45:03129 DoDelayedLooperWork();
130}
Michael Thiessen7c36083d2018-08-10 20:24:54131
Aaron Colwell48c4d5072020-11-13 16:45:03132void MessagePumpForUI::DoDelayedLooperWork() {
Gabriel Charette9d44a9b2019-04-29 16:35:56133 delayed_scheduled_time_.reset();
[email protected]61c86c62011-08-02 16:11:16134
Etienne Pierre-doray2163f3012020-04-02 21:37:14135 Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
Gabriel Charette9d44a9b2019-04-29 16:35:56136
Michael Thiessend7ae7352018-07-10 00:57:13137 if (ShouldQuit())
138 return;
Gabriel Charette9d44a9b2019-04-29 16:35:56139
140 if (next_work_info.is_immediate()) {
141 ScheduleWork();
142 return;
143 }
144
145 DoIdleWork();
146 if (!next_work_info.delayed_run_time.is_max())
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33147 ScheduleDelayedWork(next_work_info);
Michael Thiessend7ae7352018-07-10 00:57:13148}
149
150void MessagePumpForUI::OnNonDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47151 // There may be non-Chromium callbacks on the same ALooper which may have left
152 // a pending exception set, and ALooper does not check for this between
153 // callbacks. Check here, and if there's already an exception, just skip this
154 // iteration without clearing the fd. If the exception ends up being non-fatal
155 // then we'll just get called again on the next polling iteration.
156 if (base::android::HasException(env_))
157 return;
158
Gabriel Charette9d44a9b2019-04-29 16:35:56159 // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
160 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13161 if (ShouldQuit())
162 return;
163
Gabriel Charette9d44a9b2019-04-29 16:35:56164 // We're about to process all the work requested by ScheduleWork().
165 // MessagePump users are expected to do their best not to invoke
Etienne Pierre-doray2163f3012020-04-02 21:37:14166 // ScheduleWork() again before DoWork() returns a non-immediate
Gabriel Charette9d44a9b2019-04-29 16:35:56167 // NextWorkInfo below. Hence, capturing the file descriptor's value now and
168 // resetting its contents to 0 should be okay. The value currently stored
169 // should be greater than 0 since work having been scheduled is the reason
170 // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
Aaron Colwell48c4d5072020-11-13 16:45:03171 uint64_t value = 0;
Peter Kasting2f61c8b2022-07-19 23:43:46172 long ret = read(non_delayed_fd_, &value, sizeof(value));
Gabriel Charette9d44a9b2019-04-29 16:35:56173 DPCHECK(ret >= 0);
Aaron Colwell48c4d5072020-11-13 16:45:03174 DCHECK_GT(value, 0U);
Gabriel Charette300f16792022-07-06 20:03:30175 bool do_idle_work = value == kTryNativeWorkBeforeIdleBit;
Aaron Colwell48c4d5072020-11-13 16:45:03176 DoNonDelayedLooperWork(do_idle_work);
177}
Gabriel Charette9d44a9b2019-04-29 16:35:56178
Aaron Colwell48c4d5072020-11-13 16:45:03179void MessagePumpForUI::DoNonDelayedLooperWork(bool do_idle_work) {
180 // Note: We can't skip DoWork() even if |do_idle_work| is true here (i.e. no
181 // additional ScheduleWork() since yielding to native) as delayed tasks might
182 // have come in and we need to re-sample |next_work_info|.
Gabriel Charette9d44a9b2019-04-29 16:35:56183
184 // Runs all application tasks scheduled to run.
185 Delegate::NextWorkInfo next_work_info;
186 do {
187 if (ShouldQuit())
188 return;
189
Etienne Pierre-doray2163f3012020-04-02 21:37:14190 next_work_info = delegate_->DoWork();
Stephen Nusko133b4e42021-06-01 22:06:23191 // If we are prioritizing native, and the next work would normally run
Gabriel Charette300f16792022-07-06 20:03:30192 // immediately, skip the next work and let the native work items have a
193 // chance to run. This is useful when user input is waiting for native to
194 // have a chance to run.
Stephen Nusko133b4e42021-06-01 22:06:23195 if (next_work_info.is_immediate() && next_work_info.yield_to_native) {
François Doray23159042023-03-01 14:14:09196 ScheduleWork();
Stephen Nusko133b4e42021-06-01 22:06:23197 return;
198 }
Gabriel Charette9d44a9b2019-04-29 16:35:56199 } while (next_work_info.is_immediate());
200
201 // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
202 // allow nesting so needing to resume in an outer loop is not an issue
203 // either).
204 if (ShouldQuit())
205 return;
206
Gabriel Charette300f16792022-07-06 20:03:30207 // Before declaring this loop idle, yield to native work items and arrange to
François Doray23159042023-03-01 14:14:09208 // be called again (unless we're already in that second call).
Aaron Colwell48c4d5072020-11-13 16:45:03209 if (!do_idle_work) {
210 ScheduleWorkInternal(/*do_idle_work=*/true);
Gabriel Charette9d44a9b2019-04-29 16:35:56211 return;
Michael Thiessend7ae7352018-07-10 00:57:13212 }
Gabriel Charette9d44a9b2019-04-29 16:35:56213
Gabriel Charette300f16792022-07-06 20:03:30214 // We yielded to native work items already and they didn't generate a
Gabriel Charette1c6e0a22019-04-30 11:15:14215 // ScheduleWork() request so we can declare idleness. It's possible for a
216 // ScheduleWork() request to come in racily while this method unwinds, this is
217 // fine and will merely result in it being re-invoked shortly after it
218 // returns.
219 // TODO(scheduler-dev): this doesn't account for tasks that don't ever call
220 // SchedulerWork() but still keep the system non-idle (e.g., the Java Handler
221 // API). It would be better to add an API to query the presence of native
Gabriel Charette300f16792022-07-06 20:03:30222 // tasks instead of relying on yielding once + kTryNativeWorkBeforeIdleBit.
Aaron Colwell48c4d5072020-11-13 16:45:03223 DCHECK(do_idle_work);
Gabriel Charette9d44a9b2019-04-29 16:35:56224
225 if (ShouldQuit())
226 return;
227
228 // At this point, the java looper might not be idle - it's impossible to know
229 // pre-Android-M, so we may end up doing Idle work while java tasks are still
230 // queued up. Note that this won't cause us to fail to run java tasks using
231 // QuitWhenIdle, as the JavaHandlerThread will finish running all currently
232 // scheduled tasks before it quits. Also note that we can't just add an idle
233 // callback to the java looper, as that will fire even if application tasks
234 // are still queued up.
235 DoIdleWork();
Stephen Nusko408b9a92022-09-15 10:03:57236 if (!next_work_info.delayed_run_time.is_max()) {
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33237 ScheduleDelayedWork(next_work_info);
Stephen Nusko408b9a92022-09-15 10:03:57238 }
Michael Thiessend7ae7352018-07-10 00:57:13239}
240
241void MessagePumpForUI::DoIdleWork() {
242 if (delegate_->DoIdleWork()) {
243 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
244 // the loop here because we may in fact be idle after doing idle work
245 // without any new tasks being queued.
246 ScheduleWork();
247 }
[email protected]61c86c62011-08-02 16:11:16248}
249
[email protected]61c86c62011-08-02 16:11:16250void MessagePumpForUI::Run(Delegate* delegate) {
Aaron Colwelld34116aaa2020-11-11 02:15:37251 CHECK(false) << "Unexpected call to Run()";
[email protected]61c86c62011-08-02 16:11:16252}
253
Ran Ji3d6ec662018-07-09 21:18:30254void MessagePumpForUI::Attach(Delegate* delegate) {
Michael Thiessendbeca242017-08-28 21:10:08255 DCHECK(!quit_);
Michael Thiessend7ae7352018-07-10 00:57:13256
257 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
258 // can't use Run() like we do on other platforms or we would prevent Java
259 // tasks from running. Instead we create and initialize a run loop here, then
260 // return control back to the Looper.
261
262 SetDelegate(delegate);
Michael Thiessen781ddeb2017-11-15 17:07:23263 run_loop_ = std::make_unique<RunLoop>();
[email protected]8e937c1e2012-06-28 22:57:30264 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
265 // return true (it only returns false if the RunLoop has been Quit already).
266 if (!run_loop_->BeforeRun())
267 NOTREACHED();
[email protected]61c86c62011-08-02 16:11:16268}
269
270void MessagePumpForUI::Quit() {
Michael Thiessend7ae7352018-07-10 00:57:13271 if (quit_)
272 return;
273
Michael Thiessendbeca242017-08-28 21:10:08274 quit_ = true;
Michael Thiessen781ddeb2017-11-15 17:07:23275
Michael Thiessend7ae7352018-07-10 00:57:13276 int64_t value;
277 // Clear any pending timer.
278 read(delayed_fd_, &value, sizeof(value));
279 // Clear the eventfd.
280 read(non_delayed_fd_, &value, sizeof(value));
[email protected]61c86c62011-08-02 16:11:16281
[email protected]8e937c1e2012-06-28 22:57:30282 if (run_loop_) {
283 run_loop_->AfterRun();
Michael Thiessen781ddeb2017-11-15 17:07:23284 run_loop_ = nullptr;
[email protected]61c86c62011-08-02 16:11:16285 }
Michael Thiessend7ae7352018-07-10 00:57:13286 if (on_quit_callback_) {
287 std::move(on_quit_callback_).Run();
288 }
[email protected]61c86c62011-08-02 16:11:16289}
290
291void MessagePumpForUI::ScheduleWork() {
Aaron Colwell48c4d5072020-11-13 16:45:03292 ScheduleWorkInternal(/*do_idle_work=*/false);
293}
294
295void MessagePumpForUI::ScheduleWorkInternal(bool do_idle_work) {
296 // Write (add) |value| to the eventfd. This tells the Looper to wake up and
297 // call our callback, allowing us to run tasks. This also allows us to detect,
298 // when we clear the fd, whether additional work was scheduled after we
299 // finished performing work, but before we cleared the fd, as we'll read back
300 // >=2 instead of 1 in that case. See the eventfd man pages
Michael Thiessend7ae7352018-07-10 00:57:13301 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
302 // the read and write APIs for this file descriptor work, specifically without
303 // EFD_SEMAPHORE.
Aaron Colwell48c4d5072020-11-13 16:45:03304 // Note: Calls with |do_idle_work| set to true may race with potential calls
François Doray23159042023-03-01 14:14:09305 // where the parameter is false. This is fine as write() is adding |value|,
306 // not overwriting the existing value, and as such racing calls would merely
307 // have their values added together. Since idle work is only executed when the
308 // value read equals kTryNativeWorkBeforeIdleBit, a race would prevent idle
309 // work from being run and trigger another call to this method with
310 // |do_idle_work| set to true.
Gabriel Charette300f16792022-07-06 20:03:30311 uint64_t value = do_idle_work ? kTryNativeWorkBeforeIdleBit : 1;
Peter Kasting2f61c8b2022-07-19 23:43:46312 long ret = write(non_delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54313 DPCHECK(ret >= 0);
[email protected]61c86c62011-08-02 16:11:16314}
315
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33316void MessagePumpForUI::ScheduleDelayedWork(
317 const Delegate::NextWorkInfo& next_work_info) {
Michael Thiessend7ae7352018-07-10 00:57:13318 if (ShouldQuit())
Michael Thiessendbeca242017-08-28 21:10:08319 return;
Michael Thiessend7ae7352018-07-10 00:57:13320
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33321 if (delayed_scheduled_time_ &&
322 *delayed_scheduled_time_ == next_work_info.delayed_run_time) {
Michael Thiessen781ddeb2017-11-15 17:07:23323 return;
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33324 }
Michael Thiessend7ae7352018-07-10 00:57:13325
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33326 DCHECK(!next_work_info.is_immediate());
327 delayed_scheduled_time_ = next_work_info.delayed_run_time;
François Doray23159042023-03-01 14:14:09328 int64_t nanos =
329 next_work_info.delayed_run_time.since_origin().InNanoseconds();
330 struct itimerspec ts;
331 ts.it_interval.tv_sec = 0; // Don't repeat.
332 ts.it_interval.tv_nsec = 0;
333 ts.it_value.tv_sec =
334 static_cast<time_t>(nanos / TimeTicks::kNanosecondsPerSecond);
335 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
336
Peter Kasting2f61c8b2022-07-19 23:43:46337 long ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
Michael Thiessen7c36083d2018-08-10 20:24:54338 DPCHECK(ret >= 0);
Michael Thiessend7ae7352018-07-10 00:57:13339}
340
341void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
342 DCHECK(!on_quit_callback_);
343 DCHECK(run_loop_);
344 on_quit_callback_ = std::move(callback);
345 run_loop_->QuitWhenIdle();
346 // Pump the loop in case we're already idle.
347 ScheduleWork();
348}
349
Aaron Colwell48c4d5072020-11-13 16:45:03350MessagePump::Delegate* MessagePumpForUI::SetDelegate(Delegate* delegate) {
351 return std::exchange(delegate_, delegate);
352}
353
354bool MessagePumpForUI::SetQuit(bool quit) {
355 return std::exchange(quit_, quit);
356}
357
[email protected]61c86c62011-08-02 16:11:16358} // namespace base