blob: f3e68d11884b2d76cfeff7ee8ed3a5e6fb4f2523 [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
85} // namespace
86
Torne (Richard Coles)c6993a032020-02-19 13:23:4787MessagePumpForUI::MessagePumpForUI()
88 : env_(base::android::AttachCurrentThread()) {
Michael Thiessend7ae7352018-07-10 00:57:1389 // The Android native ALooper uses epoll to poll our file descriptors and wake
90 // us up. We use a simple level-triggered eventfd to signal that non-delayed
91 // work is available, and a timerfd to signal when delayed work is ready to
92 // be run.
93 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
94 CHECK_NE(non_delayed_fd_, -1);
95 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
96
97 // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
98 // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
99 // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
100 // fine.
101 delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC);
102 CHECK_NE(delayed_fd_, -1);
103
104 looper_ = ALooper_prepare(0);
105 DCHECK(looper_);
106 // Add a reference to the looper so it isn't deleted on us.
107 ALooper_acquire(looper_);
108 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
109 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
110 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
111 &DelayedLooperCallback, reinterpret_cast<void*>(this));
112}
113
114MessagePumpForUI::~MessagePumpForUI() {
115 DCHECK_EQ(ALooper_forThread(), looper_);
116 ALooper_removeFd(looper_, non_delayed_fd_);
117 ALooper_removeFd(looper_, delayed_fd_);
118 ALooper_release(looper_);
119 looper_ = nullptr;
120
121 close(non_delayed_fd_);
122 close(delayed_fd_);
123}
124
125void MessagePumpForUI::OnDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47126 // There may be non-Chromium callbacks on the same ALooper which may have left
127 // a pending exception set, and ALooper does not check for this between
128 // callbacks. Check here, and if there's already an exception, just skip this
129 // iteration without clearing the fd. If the exception ends up being non-fatal
130 // then we'll just get called again on the next polling iteration.
131 if (base::android::HasException(env_))
132 return;
133
Gabriel Charette9d44a9b2019-04-29 16:35:56134 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
135 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13136 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01137 return;
138
Michael Thiessend7ae7352018-07-10 00:57:13139 // Clear the fd.
140 uint64_t value;
141 int ret = read(delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54142
143 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
144 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
145 // EAGAIN only happens if no timer has expired. Also according to the man page
146 // poll only returns readable when a timer has expired. So this function will
147 // only be called when a timer has expired, but reading reveals no timer has
148 // expired...
149 // Quit() and ScheduleDelayedWork() are the only other functions that touch
150 // the timerfd, and they both run on the same thread as this callback, so
151 // there are no obvious timing or multi-threading related issues.
152 DPCHECK(ret >= 0 || errno == EAGAIN);
153
Gabriel Charette9d44a9b2019-04-29 16:35:56154 delayed_scheduled_time_.reset();
[email protected]61c86c62011-08-02 16:11:16155
Etienne Pierre-doray2163f3012020-04-02 21:37:14156 Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
Gabriel Charette9d44a9b2019-04-29 16:35:56157
Michael Thiessend7ae7352018-07-10 00:57:13158 if (ShouldQuit())
159 return;
Gabriel Charette9d44a9b2019-04-29 16:35:56160
161 if (next_work_info.is_immediate()) {
162 ScheduleWork();
163 return;
164 }
165
166 DoIdleWork();
167 if (!next_work_info.delayed_run_time.is_max())
168 ScheduleDelayedWork(next_work_info.delayed_run_time);
Michael Thiessend7ae7352018-07-10 00:57:13169}
170
171void MessagePumpForUI::OnNonDelayedLooperCallback() {
Torne (Richard Coles)c6993a032020-02-19 13:23:47172 // There may be non-Chromium callbacks on the same ALooper which may have left
173 // a pending exception set, and ALooper does not check for this between
174 // callbacks. Check here, and if there's already an exception, just skip this
175 // iteration without clearing the fd. If the exception ends up being non-fatal
176 // then we'll just get called again on the next polling iteration.
177 if (base::android::HasException(env_))
178 return;
179
Gabriel Charette9d44a9b2019-04-29 16:35:56180 // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
181 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13182 if (ShouldQuit())
183 return;
184
Gabriel Charette9d44a9b2019-04-29 16:35:56185 // A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
Gabriel Charette1c6e0a22019-04-30 11:15:14186 // native tasks below.
187 constexpr uint64_t kTryNativeTasksBeforeIdleBit = uint64_t(1) << 32;
Michael Thiessend7ae7352018-07-10 00:57:13188
Gabriel Charette9d44a9b2019-04-29 16:35:56189 // We're about to process all the work requested by ScheduleWork().
190 // MessagePump users are expected to do their best not to invoke
Etienne Pierre-doray2163f3012020-04-02 21:37:14191 // ScheduleWork() again before DoWork() returns a non-immediate
Gabriel Charette9d44a9b2019-04-29 16:35:56192 // NextWorkInfo below. Hence, capturing the file descriptor's value now and
193 // resetting its contents to 0 should be okay. The value currently stored
194 // should be greater than 0 since work having been scheduled is the reason
195 // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
196 uint64_t pre_work_value = 0;
197 int ret = read(non_delayed_fd_, &pre_work_value, sizeof(pre_work_value));
198 DPCHECK(ret >= 0);
199 DCHECK_GT(pre_work_value, 0U);
200
Etienne Pierre-doray2163f3012020-04-02 21:37:14201 // Note: We can't skip DoWork() even if
Gabriel Charette1c6e0a22019-04-30 11:15:14202 // |pre_work_value == kTryNativeTasksBeforeIdleBit| here (i.e. no additional
203 // ScheduleWork() since yielding to native) as delayed tasks might have come
204 // in and we need to re-sample |next_work_info|.
Gabriel Charette9d44a9b2019-04-29 16:35:56205
206 // Runs all application tasks scheduled to run.
207 Delegate::NextWorkInfo next_work_info;
208 do {
209 if (ShouldQuit())
210 return;
211
Etienne Pierre-doray2163f3012020-04-02 21:37:14212 next_work_info = delegate_->DoWork();
Gabriel Charette9d44a9b2019-04-29 16:35:56213 } while (next_work_info.is_immediate());
214
215 // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
216 // allow nesting so needing to resume in an outer loop is not an issue
217 // either).
218 if (ShouldQuit())
219 return;
220
Gabriel Charette1c6e0a22019-04-30 11:15:14221 // Before declaring this loop idle, yield to native tasks and arrange to be
Gabriel Charette9d44a9b2019-04-29 16:35:56222 // called again (unless we're already in that second call).
Gabriel Charette1c6e0a22019-04-30 11:15:14223 if (pre_work_value != kTryNativeTasksBeforeIdleBit) {
Gabriel Charette9d44a9b2019-04-29 16:35:56224 // Note: This write() is racing with potential ScheduleWork() calls. This is
225 // fine as write() is adding this bit, not overwriting the existing value,
226 // and as such racing ScheduleWork() calls would merely add 1 to the lower
Gabriel Charette1c6e0a22019-04-30 11:15:14227 // bits and we would find |pre_work_value != kTryNativeTasksBeforeIdleBit|
228 // in the next cycle again, retrying this.
229 ret = write(non_delayed_fd_, &kTryNativeTasksBeforeIdleBit,
230 sizeof(kTryNativeTasksBeforeIdleBit));
Gabriel Charette9d44a9b2019-04-29 16:35:56231 DPCHECK(ret >= 0);
232 return;
Michael Thiessend7ae7352018-07-10 00:57:13233 }
Gabriel Charette9d44a9b2019-04-29 16:35:56234
Gabriel Charette1c6e0a22019-04-30 11:15:14235 // We yielded to native tasks already and they didn't generate a
236 // ScheduleWork() request so we can declare idleness. It's possible for a
237 // ScheduleWork() request to come in racily while this method unwinds, this is
238 // fine and will merely result in it being re-invoked shortly after it
239 // returns.
240 // TODO(scheduler-dev): this doesn't account for tasks that don't ever call
241 // SchedulerWork() but still keep the system non-idle (e.g., the Java Handler
242 // API). It would be better to add an API to query the presence of native
243 // tasks instead of relying on yielding once + kTryNativeTasksBeforeIdleBit.
244 DCHECK_EQ(pre_work_value, kTryNativeTasksBeforeIdleBit);
Gabriel Charette9d44a9b2019-04-29 16:35:56245
246 if (ShouldQuit())
247 return;
248
249 // At this point, the java looper might not be idle - it's impossible to know
250 // pre-Android-M, so we may end up doing Idle work while java tasks are still
251 // queued up. Note that this won't cause us to fail to run java tasks using
252 // QuitWhenIdle, as the JavaHandlerThread will finish running all currently
253 // scheduled tasks before it quits. Also note that we can't just add an idle
254 // callback to the java looper, as that will fire even if application tasks
255 // are still queued up.
256 DoIdleWork();
257 if (!next_work_info.delayed_run_time.is_max())
258 ScheduleDelayedWork(next_work_info.delayed_run_time);
Michael Thiessend7ae7352018-07-10 00:57:13259}
260
261void MessagePumpForUI::DoIdleWork() {
262 if (delegate_->DoIdleWork()) {
263 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
264 // the loop here because we may in fact be idle after doing idle work
265 // without any new tasks being queued.
266 ScheduleWork();
267 }
[email protected]61c86c62011-08-02 16:11:16268}
269
[email protected]61c86c62011-08-02 16:11:16270void MessagePumpForUI::Run(Delegate* delegate) {
Aaron Colwelld34116aaa2020-11-11 02:15:37271 CHECK(false) << "Unexpected call to Run()";
[email protected]61c86c62011-08-02 16:11:16272}
273
Ran Ji3d6ec662018-07-09 21:18:30274void MessagePumpForUI::Attach(Delegate* delegate) {
Michael Thiessendbeca242017-08-28 21:10:08275 DCHECK(!quit_);
Michael Thiessend7ae7352018-07-10 00:57:13276
277 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
278 // can't use Run() like we do on other platforms or we would prevent Java
279 // tasks from running. Instead we create and initialize a run loop here, then
280 // return control back to the Looper.
281
282 SetDelegate(delegate);
Michael Thiessen781ddeb2017-11-15 17:07:23283 run_loop_ = std::make_unique<RunLoop>();
[email protected]8e937c1e2012-06-28 22:57:30284 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
285 // return true (it only returns false if the RunLoop has been Quit already).
286 if (!run_loop_->BeforeRun())
287 NOTREACHED();
[email protected]61c86c62011-08-02 16:11:16288}
289
290void MessagePumpForUI::Quit() {
Michael Thiessend7ae7352018-07-10 00:57:13291 if (quit_)
292 return;
293
Michael Thiessendbeca242017-08-28 21:10:08294 quit_ = true;
Michael Thiessen781ddeb2017-11-15 17:07:23295
Michael Thiessend7ae7352018-07-10 00:57:13296 int64_t value;
297 // Clear any pending timer.
298 read(delayed_fd_, &value, sizeof(value));
299 // Clear the eventfd.
300 read(non_delayed_fd_, &value, sizeof(value));
[email protected]61c86c62011-08-02 16:11:16301
[email protected]8e937c1e2012-06-28 22:57:30302 if (run_loop_) {
303 run_loop_->AfterRun();
Michael Thiessen781ddeb2017-11-15 17:07:23304 run_loop_ = nullptr;
[email protected]61c86c62011-08-02 16:11:16305 }
Michael Thiessend7ae7352018-07-10 00:57:13306 if (on_quit_callback_) {
307 std::move(on_quit_callback_).Run();
308 }
[email protected]61c86c62011-08-02 16:11:16309}
310
311void MessagePumpForUI::ScheduleWork() {
Michael Thiessend7ae7352018-07-10 00:57:13312 // Write (add) 1 to the eventfd. This tells the Looper to wake up and call our
313 // callback, allowing us to run tasks. This also allows us to detect, when we
314 // clear the fd, whether additional work was scheduled after we finished
315 // performing work, but before we cleared the fd, as we'll read back >=2
316 // instead of 1 in that case.
317 // See the eventfd man pages
318 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
319 // the read and write APIs for this file descriptor work, specifically without
320 // EFD_SEMAPHORE.
321 uint64_t value = 1;
322 int ret = write(non_delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54323 DPCHECK(ret >= 0);
[email protected]61c86c62011-08-02 16:11:16324}
325
326void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
Michael Thiessend7ae7352018-07-10 00:57:13327 if (ShouldQuit())
Michael Thiessendbeca242017-08-28 21:10:08328 return;
Michael Thiessend7ae7352018-07-10 00:57:13329
Gabriel Charette9d44a9b2019-04-29 16:35:56330 if (delayed_scheduled_time_ && *delayed_scheduled_time_ == delayed_work_time)
Michael Thiessen781ddeb2017-11-15 17:07:23331 return;
Michael Thiessend7ae7352018-07-10 00:57:13332
Michael Thiessen781ddeb2017-11-15 17:07:23333 DCHECK(!delayed_work_time.is_null());
Michael Thiessen781ddeb2017-11-15 17:07:23334 delayed_scheduled_time_ = delayed_work_time;
Michael Thiessend7ae7352018-07-10 00:57:13335 int64_t nanos = delayed_work_time.since_origin().InNanoseconds();
336 struct itimerspec ts;
337 ts.it_interval.tv_sec = 0; // Don't repeat.
338 ts.it_interval.tv_nsec = 0;
339 ts.it_value.tv_sec = nanos / TimeTicks::kNanosecondsPerSecond;
340 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
341
342 int ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
Michael Thiessen7c36083d2018-08-10 20:24:54343 DPCHECK(ret >= 0);
Michael Thiessend7ae7352018-07-10 00:57:13344}
345
346void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
347 DCHECK(!on_quit_callback_);
348 DCHECK(run_loop_);
349 on_quit_callback_ = std::move(callback);
350 run_loop_->QuitWhenIdle();
351 // Pump the loop in case we're already idle.
352 ScheduleWork();
353}
354
[email protected]61c86c62011-08-02 16:11:16355} // namespace base