1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/message_loop/message_pump_android.h"
6
7 #include <android/looper.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <jni.h>
11 #include <sys/eventfd.h>
12 #include <sys/timerfd.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <utility>
16
17 #include "base/android/jni_android.h"
18 #include "base/android/scoped_java_ref.h"
19 #include "base/check_op.h"
20 #include "base/notreached.h"
21 #include "base/numerics/safe_conversions.h"
22 #include "base/run_loop.h"
23 #include "build/build_config.h"
24
25 namespace base {
26
27 namespace {
28
29 // 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
NonDelayedLooperCallback(int fd,int events,void * data)37 STACK_ALIGN int NonDelayedLooperCallback(int fd, int events, void* data) {
38 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
DelayedLooperCallback(int fd,int events,void * data)47 STACK_ALIGN int DelayedLooperCallback(int fd, int events, void* data) {
48 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
57 // A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
58 // native work below.
59 constexpr uint64_t kTryNativeWorkBeforeIdleBit = uint64_t(1) << 32;
60 } // namespace
61
MessagePumpForUI()62 MessagePumpForUI::MessagePumpForUI()
63 : env_(base::android::AttachCurrentThread()) {
64 // 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 delayed_fd_ = checked_cast<int>(
73 timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC));
74 CHECK_NE(delayed_fd_, -1);
75
76 looper_ = ALooper_prepare(0);
77 DCHECK(looper_);
78 // Add a reference to the looper so it isn't deleted on us.
79 ALooper_acquire(looper_);
80 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
81 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
82 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
83 &DelayedLooperCallback, reinterpret_cast<void*>(this));
84 }
85
~MessagePumpForUI()86 MessagePumpForUI::~MessagePumpForUI() {
87 DCHECK_EQ(ALooper_forThread(), looper_);
88 ALooper_removeFd(looper_, non_delayed_fd_);
89 ALooper_removeFd(looper_, delayed_fd_);
90 ALooper_release(looper_);
91 looper_ = nullptr;
92
93 close(non_delayed_fd_);
94 close(delayed_fd_);
95 }
96
OnDelayedLooperCallback()97 void MessagePumpForUI::OnDelayedLooperCallback() {
98 // There may be non-Chromium callbacks on the same ALooper which may have left
99 // a pending exception set, and ALooper does not check for this between
100 // callbacks. Check here, and if there's already an exception, just skip this
101 // iteration without clearing the fd. If the exception ends up being non-fatal
102 // then we'll just get called again on the next polling iteration.
103 if (base::android::HasException(env_))
104 return;
105
106 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
107 // resulted in Quit() in the same round.
108 if (ShouldQuit())
109 return;
110
111 // Clear the fd.
112 uint64_t value;
113 long ret = read(delayed_fd_, &value, sizeof(value));
114
115 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
116 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
117 // EAGAIN only happens if no timer has expired. Also according to the man page
118 // poll only returns readable when a timer has expired. So this function will
119 // only be called when a timer has expired, but reading reveals no timer has
120 // expired...
121 // Quit() and ScheduleDelayedWork() are the only other functions that touch
122 // the timerfd, and they both run on the same thread as this callback, so
123 // there are no obvious timing or multi-threading related issues.
124 DPCHECK(ret >= 0 || errno == EAGAIN);
125 DoDelayedLooperWork();
126 }
127
DoDelayedLooperWork()128 void MessagePumpForUI::DoDelayedLooperWork() {
129 delayed_scheduled_time_.reset();
130
131 Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
132
133 if (ShouldQuit())
134 return;
135
136 if (next_work_info.is_immediate()) {
137 ScheduleWork();
138 return;
139 }
140
141 DoIdleWork();
142 if (!next_work_info.delayed_run_time.is_max())
143 ScheduleDelayedWork(next_work_info);
144 }
145
OnNonDelayedLooperCallback()146 void MessagePumpForUI::OnNonDelayedLooperCallback() {
147 // There may be non-Chromium callbacks on the same ALooper which may have left
148 // a pending exception set, and ALooper does not check for this between
149 // callbacks. Check here, and if there's already an exception, just skip this
150 // iteration without clearing the fd. If the exception ends up being non-fatal
151 // then we'll just get called again on the next polling iteration.
152 if (base::android::HasException(env_))
153 return;
154
155 // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
156 // resulted in Quit() in the same round.
157 if (ShouldQuit())
158 return;
159
160 // We're about to process all the work requested by ScheduleWork().
161 // MessagePump users are expected to do their best not to invoke
162 // ScheduleWork() again before DoWork() returns a non-immediate
163 // NextWorkInfo below. Hence, capturing the file descriptor's value now and
164 // resetting its contents to 0 should be okay. The value currently stored
165 // should be greater than 0 since work having been scheduled is the reason
166 // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
167 uint64_t value = 0;
168 long ret = read(non_delayed_fd_, &value, sizeof(value));
169 DPCHECK(ret >= 0);
170 DCHECK_GT(value, 0U);
171 bool do_idle_work = value == kTryNativeWorkBeforeIdleBit;
172 DoNonDelayedLooperWork(do_idle_work);
173 }
174
DoNonDelayedLooperWork(bool do_idle_work)175 void MessagePumpForUI::DoNonDelayedLooperWork(bool do_idle_work) {
176 // Note: We can't skip DoWork() even if |do_idle_work| is true here (i.e. no
177 // additional ScheduleWork() since yielding to native) as delayed tasks might
178 // have come in and we need to re-sample |next_work_info|.
179
180 // Runs all application tasks scheduled to run.
181 Delegate::NextWorkInfo next_work_info;
182 do {
183 if (ShouldQuit())
184 return;
185
186 next_work_info = delegate_->DoWork();
187 // If we are prioritizing native, and the next work would normally run
188 // immediately, skip the next work and let the native work items have a
189 // chance to run. This is useful when user input is waiting for native to
190 // have a chance to run.
191 if (next_work_info.is_immediate() && next_work_info.yield_to_native) {
192 ScheduleWork();
193 return;
194 }
195 } while (next_work_info.is_immediate());
196
197 // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
198 // allow nesting so needing to resume in an outer loop is not an issue
199 // either).
200 if (ShouldQuit())
201 return;
202
203 // Before declaring this loop idle, yield to native work items and arrange to
204 // be called again (unless we're already in that second call).
205 if (!do_idle_work) {
206 ScheduleWorkInternal(/*do_idle_work=*/true);
207 return;
208 }
209
210 // We yielded to native work items already and they didn't generate a
211 // ScheduleWork() request so we can declare idleness. It's possible for a
212 // ScheduleWork() request to come in racily while this method unwinds, this is
213 // fine and will merely result in it being re-invoked shortly after it
214 // returns.
215 // TODO(scheduler-dev): this doesn't account for tasks that don't ever call
216 // SchedulerWork() but still keep the system non-idle (e.g., the Java Handler
217 // API). It would be better to add an API to query the presence of native
218 // tasks instead of relying on yielding once + kTryNativeWorkBeforeIdleBit.
219 DCHECK(do_idle_work);
220
221 if (ShouldQuit())
222 return;
223
224 // At this point, the java looper might not be idle - it's impossible to know
225 // pre-Android-M, so we may end up doing Idle work while java tasks are still
226 // queued up. Note that this won't cause us to fail to run java tasks using
227 // QuitWhenIdle, as the JavaHandlerThread will finish running all currently
228 // scheduled tasks before it quits. Also note that we can't just add an idle
229 // callback to the java looper, as that will fire even if application tasks
230 // are still queued up.
231 DoIdleWork();
232 if (!next_work_info.delayed_run_time.is_max()) {
233 ScheduleDelayedWork(next_work_info);
234 }
235 }
236
DoIdleWork()237 void MessagePumpForUI::DoIdleWork() {
238 if (delegate_->DoIdleWork()) {
239 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
240 // the loop here because we may in fact be idle after doing idle work
241 // without any new tasks being queued.
242 ScheduleWork();
243 }
244 }
245
Run(Delegate * delegate)246 void MessagePumpForUI::Run(Delegate* delegate) {
247 CHECK(false) << "Unexpected call to Run()";
248 }
249
Attach(Delegate * delegate)250 void MessagePumpForUI::Attach(Delegate* delegate) {
251 DCHECK(!quit_);
252
253 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
254 // can't use Run() like we do on other platforms or we would prevent Java
255 // tasks from running. Instead we create and initialize a run loop here, then
256 // return control back to the Looper.
257
258 SetDelegate(delegate);
259 run_loop_ = std::make_unique<RunLoop>();
260 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
261 // return true (it only returns false if the RunLoop has been Quit already).
262 if (!run_loop_->BeforeRun())
263 NOTREACHED();
264 }
265
Quit()266 void MessagePumpForUI::Quit() {
267 if (quit_)
268 return;
269
270 quit_ = true;
271
272 int64_t value;
273 // Clear any pending timer.
274 read(delayed_fd_, &value, sizeof(value));
275 // Clear the eventfd.
276 read(non_delayed_fd_, &value, sizeof(value));
277
278 if (run_loop_) {
279 run_loop_->AfterRun();
280 run_loop_ = nullptr;
281 }
282 if (on_quit_callback_) {
283 std::move(on_quit_callback_).Run();
284 }
285 }
286
ScheduleWork()287 void MessagePumpForUI::ScheduleWork() {
288 ScheduleWorkInternal(/*do_idle_work=*/false);
289 }
290
ScheduleWorkInternal(bool do_idle_work)291 void MessagePumpForUI::ScheduleWorkInternal(bool do_idle_work) {
292 // Write (add) |value| to the eventfd. This tells the Looper to wake up and
293 // call our callback, allowing us to run tasks. This also allows us to detect,
294 // when we clear the fd, whether additional work was scheduled after we
295 // finished performing work, but before we cleared the fd, as we'll read back
296 // >=2 instead of 1 in that case. See the eventfd man pages
297 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
298 // the read and write APIs for this file descriptor work, specifically without
299 // EFD_SEMAPHORE.
300 // Note: Calls with |do_idle_work| set to true may race with potential calls
301 // where the parameter is false. This is fine as write() is adding |value|,
302 // not overwriting the existing value, and as such racing calls would merely
303 // have their values added together. Since idle work is only executed when the
304 // value read equals kTryNativeWorkBeforeIdleBit, a race would prevent idle
305 // work from being run and trigger another call to this method with
306 // |do_idle_work| set to true.
307 uint64_t value = do_idle_work ? kTryNativeWorkBeforeIdleBit : 1;
308 long ret = write(non_delayed_fd_, &value, sizeof(value));
309 DPCHECK(ret >= 0);
310 }
311
ScheduleDelayedWork(const Delegate::NextWorkInfo & next_work_info)312 void MessagePumpForUI::ScheduleDelayedWork(
313 const Delegate::NextWorkInfo& next_work_info) {
314 if (ShouldQuit())
315 return;
316
317 if (delayed_scheduled_time_ &&
318 *delayed_scheduled_time_ == next_work_info.delayed_run_time) {
319 return;
320 }
321
322 DCHECK(!next_work_info.is_immediate());
323 delayed_scheduled_time_ = next_work_info.delayed_run_time;
324 int64_t nanos =
325 next_work_info.delayed_run_time.since_origin().InNanoseconds();
326 struct itimerspec ts;
327 ts.it_interval.tv_sec = 0; // Don't repeat.
328 ts.it_interval.tv_nsec = 0;
329 ts.it_value.tv_sec =
330 static_cast<time_t>(nanos / TimeTicks::kNanosecondsPerSecond);
331 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
332
333 long ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
334 DPCHECK(ret >= 0);
335 }
336
QuitWhenIdle(base::OnceClosure callback)337 void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
338 DCHECK(!on_quit_callback_);
339 DCHECK(run_loop_);
340 on_quit_callback_ = std::move(callback);
341 run_loop_->QuitWhenIdle();
342 // Pump the loop in case we're already idle.
343 ScheduleWork();
344 }
345
SetDelegate(Delegate * delegate)346 MessagePump::Delegate* MessagePumpForUI::SetDelegate(Delegate* delegate) {
347 return std::exchange(delegate_, delegate);
348 }
349
SetQuit(bool quit)350 bool MessagePumpForUI::SetQuit(bool quit) {
351 return std::exchange(quit_, quit);
352 }
353
354 } // namespace base
355