blob: 61312d341bc37aceddfea7463d2f342db2d02802 [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>
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"
Hans Wennborgc3cffa62020-04-27 10:09:1219#include "base/check_op.h"
Avi Drissman63e1f992023-01-13 18:54:4320#include "base/functional/callback_helpers.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"
Peter Kasting2f61c8b2022-07-19 23:43:4623#include "base/numerics/safe_conversions.h"
[email protected]8e937c1e2012-06-28 22:57:3024#include "base/run_loop.h"
Michael Thiessen6fd43902018-08-30 23:14:1525#include "build/build_config.h"
Michael Thiessend7ae7352018-07-10 00:57:1326
27// Android stripped sys/timerfd.h out of their platform headers, so we have to
28// use syscall to make use of timerfd. Once the min API level is 20, we can
29// directly use timerfd.h.
30#ifndef __NR_timerfd_create
31#error "Unable to find syscall for __NR_timerfd_create"
32#endif
33
34#ifndef TFD_TIMER_ABSTIME
35#define TFD_TIMER_ABSTIME (1 << 0)
36#endif
[email protected]61c86c62011-08-02 16:11:1637
torne86560112016-08-04 15:59:0438using base::android::JavaParamRef;
[email protected]449019ca2012-03-14 22:17:0039using base::android::ScopedJavaLocalRef;
[email protected]61c86c62011-08-02 16:11:1640
Michael Thiessen781ddeb2017-11-15 17:07:2341namespace base {
42
Michael Thiessend7ae7352018-07-10 00:57:1343namespace {
Michael Thiessen781ddeb2017-11-15 17:07:2344
Michael Thiessend7ae7352018-07-10 00:57:1345// See sys/timerfd.h
Peter Kasting2f61c8b2022-07-19 23:43:4646long timerfd_create(int clockid, int flags) {
Michael Thiessend7ae7352018-07-10 00:57:1347 return syscall(__NR_timerfd_create, clockid, flags);
Michael Thiessen781ddeb2017-11-15 17:07:2348}
49
Michael Thiessend7ae7352018-07-10 00:57:1350// See sys/timerfd.h
Peter Kasting2f61c8b2022-07-19 23:43:4651long timerfd_settime(int ufc,
52 int flags,
53 const struct itimerspec* utmr,
54 struct itimerspec* otmr) {
Michael Thiessend7ae7352018-07-10 00:57:1355 return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
56}
Michael Thiessen781ddeb2017-11-15 17:07:2357
Michael Thiessen6fd43902018-08-30 23:14:1558// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
59// into our code due to the inconsistent ABI on older Android OS versions.
60#if defined(ARCH_CPU_X86)
61#define STACK_ALIGN __attribute__((force_align_arg_pointer))
62#else
63#define STACK_ALIGN
64#endif
65
66STACK_ALIGN int NonDelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1367 if (events & ALOOPER_EVENT_HANGUP)
68 return 0;
69
70 DCHECK(events & ALOOPER_EVENT_INPUT);
71 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
72 pump->OnNonDelayedLooperCallback();
73 return 1; // continue listening for events
74}
75
Michael Thiessen6fd43902018-08-30 23:14:1576STACK_ALIGN int DelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1377 if (events & ALOOPER_EVENT_HANGUP)
78 return 0;
79
80 DCHECK(events & ALOOPER_EVENT_INPUT);
81 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
82 pump->OnDelayedLooperCallback();
83 return 1; // continue listening for events
84}
85
Aaron Colwell48c4d5072020-11-13 16:45:0386// A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
Gabriel Charette300f16792022-07-06 20:03:3087// native work below.
88constexpr uint64_t kTryNativeWorkBeforeIdleBit = uint64_t(1) << 32;
Michael Thiessend7ae7352018-07-10 00:57:1389} // namespace
90
Torne (Richard Coles)c6993a032020-02-19 13:23:4791MessagePumpForUI::MessagePumpForUI()
François Doray253a3062022-10-24 16:45:2992 : env_(base::android::AttachCurrentThread()) {
Michael Thiessend7ae7352018-07-10 00:57:1393 // The Android native ALooper uses epoll to poll our file descriptors and wake
94 // us up. We use a simple level-triggered eventfd to signal that non-delayed
95 // work is available, and a timerfd to signal when delayed work is ready to
96 // be run.
97 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
98 CHECK_NE(non_delayed_fd_, -1);
99 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
100
Michael Thiessend7ae7352018-07-10 00:57:13101 // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
102 // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
103 // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
104 // fine.
Peter Kasting2f61c8b2022-07-19 23:43:46105 delayed_fd_ = checked_cast<int>(
106 timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC));
Michael Thiessend7ae7352018-07-10 00:57:13107 CHECK_NE(delayed_fd_, -1);
108
109 looper_ = ALooper_prepare(0);
110 DCHECK(looper_);
111 // Add a reference to the looper so it isn't deleted on us.
112 ALooper_acquire(looper_);
113 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
114 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
Michael Thiessend7ae7352018-07-10 00:57:13115 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
116 &DelayedLooperCallback, reinterpret_cast<void*>(this));
117}
118
119MessagePumpForUI::~MessagePumpForUI() {
120 DCHECK_EQ(ALooper_forThread(), looper_);
121 ALooper_removeFd(looper_, non_delayed_fd_);
Michael Thiessend7ae7352018-07-10 00:57:13122 ALooper_removeFd(looper_, delayed_fd_);
123 ALooper_release(looper_);
124 looper_ = nullptr;
125
126 close(non_delayed_fd_);
Michael Thiessend7ae7352018-07-10 00:57:13127 close(delayed_fd_);
128}
129
130void MessagePumpForUI::OnDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47131 // There may be non-Chromium callbacks on the same ALooper which may have left
132 // a pending exception set, and ALooper does not check for this between
133 // callbacks. Check here, and if there's already an exception, just skip this
134 // iteration without clearing the fd. If the exception ends up being non-fatal
135 // then we'll just get called again on the next polling iteration.
136 if (base::android::HasException(env_))
137 return;
138
Gabriel Charette9d44a9b2019-04-29 16:35:56139 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
140 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13141 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01142 return;
143
François Doray23159042023-03-01 14:14:09144 // Clear the fd.
145 uint64_t value;
146 long ret = read(delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54147
François Doray23159042023-03-01 14:14:09148 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
149 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
150 // EAGAIN only happens if no timer has expired. Also according to the man page
151 // poll only returns readable when a timer has expired. So this function will
152 // only be called when a timer has expired, but reading reveals no timer has
153 // expired...
154 // Quit() and ScheduleDelayedWork() are the only other functions that touch
155 // the timerfd, and they both run on the same thread as this callback, so
156 // there are no obvious timing or multi-threading related issues.
157 DPCHECK(ret >= 0 || errno == EAGAIN);
Aaron Colwell48c4d5072020-11-13 16:45:03158 DoDelayedLooperWork();
159}
Michael Thiessen7c36083d2018-08-10 20:24:54160
Aaron Colwell48c4d5072020-11-13 16:45:03161void MessagePumpForUI::DoDelayedLooperWork() {
Gabriel Charette9d44a9b2019-04-29 16:35:56162 delayed_scheduled_time_.reset();
[email protected]61c86c62011-08-02 16:11:16163
Etienne Pierre-doray2163f3012020-04-02 21:37:14164 Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
Gabriel Charette9d44a9b2019-04-29 16:35:56165
Michael Thiessend7ae7352018-07-10 00:57:13166 if (ShouldQuit())
167 return;
Gabriel Charette9d44a9b2019-04-29 16:35:56168
169 if (next_work_info.is_immediate()) {
170 ScheduleWork();
171 return;
172 }
173
174 DoIdleWork();
175 if (!next_work_info.delayed_run_time.is_max())
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33176 ScheduleDelayedWork(next_work_info);
Michael Thiessend7ae7352018-07-10 00:57:13177}
178
Michael Thiessend7ae7352018-07-10 00:57:13179void MessagePumpForUI::OnNonDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47180 // There may be non-Chromium callbacks on the same ALooper which may have left
181 // a pending exception set, and ALooper does not check for this between
182 // callbacks. Check here, and if there's already an exception, just skip this
183 // iteration without clearing the fd. If the exception ends up being non-fatal
184 // then we'll just get called again on the next polling iteration.
185 if (base::android::HasException(env_))
186 return;
187
Gabriel Charette9d44a9b2019-04-29 16:35:56188 // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
189 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13190 if (ShouldQuit())
191 return;
192
Gabriel Charette9d44a9b2019-04-29 16:35:56193 // We're about to process all the work requested by ScheduleWork().
194 // MessagePump users are expected to do their best not to invoke
Etienne Pierre-doray2163f3012020-04-02 21:37:14195 // ScheduleWork() again before DoWork() returns a non-immediate
Gabriel Charette9d44a9b2019-04-29 16:35:56196 // NextWorkInfo below. Hence, capturing the file descriptor's value now and
197 // resetting its contents to 0 should be okay. The value currently stored
198 // should be greater than 0 since work having been scheduled is the reason
199 // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
Aaron Colwell48c4d5072020-11-13 16:45:03200 uint64_t value = 0;
Peter Kasting2f61c8b2022-07-19 23:43:46201 long ret = read(non_delayed_fd_, &value, sizeof(value));
Gabriel Charette9d44a9b2019-04-29 16:35:56