blob: b1d0813c0bc874efbc5853a3e92d681b8601ca31 [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"
[email protected]449019ca2012-03-14 22:17:0020#include "base/lazy_instance.h"
[email protected]61c86c62011-08-02 16:11:1621#include "base/logging.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
25// Android stripped sys/timerfd.h out of their platform headers, so we have to
26// use syscall to make use of timerfd. Once the min API level is 20, we can
27// directly use timerfd.h.
28#ifndef __NR_timerfd_create
29#error "Unable to find syscall for __NR_timerfd_create"
30#endif
31
32#ifndef TFD_TIMER_ABSTIME
33#define TFD_TIMER_ABSTIME (1 << 0)
34#endif
[email protected]61c86c62011-08-02 16:11:1635
torne86560112016-08-04 15:59:0436using base::android::JavaParamRef;
[email protected]449019ca2012-03-14 22:17:0037using base::android::ScopedJavaLocalRef;
[email protected]61c86c62011-08-02 16:11:1638
Michael Thiessen781ddeb2017-11-15 17:07:2339namespace base {
40
Michael Thiessend7ae7352018-07-10 00:57:1341namespace {
Michael Thiessen781ddeb2017-11-15 17:07:2342
Michael Thiessend7ae7352018-07-10 00:57:1343// See sys/timerfd.h
44int timerfd_create(int clockid, int flags) {
45 return syscall(__NR_timerfd_create, clockid, flags);
Michael Thiessen781ddeb2017-11-15 17:07:2346}
47
Michael Thiessend7ae7352018-07-10 00:57:1348// See sys/timerfd.h
49int timerfd_settime(int ufc,
50 int flags,
51 const struct itimerspec* utmr,
52 struct itimerspec* otmr) {
53 return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
54}
Michael Thiessen781ddeb2017-11-15 17:07:2355
Michael Thiessen6fd43902018-08-30 23:14:1556// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
57// into our code due to the inconsistent ABI on older Android OS versions.
58#if defined(ARCH_CPU_X86)
59#define STACK_ALIGN __attribute__((force_align_arg_pointer))
60#else
61#define STACK_ALIGN
62#endif
63
64STACK_ALIGN int NonDelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1365 if (events & ALOOPER_EVENT_HANGUP)
66 return 0;
67
68 DCHECK(events & ALOOPER_EVENT_INPUT);
69 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
70 pump->OnNonDelayedLooperCallback();
71 return 1; // continue listening for events
72}
73
Michael Thiessen6fd43902018-08-30 23:14:1574STACK_ALIGN int DelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1375 if (events & ALOOPER_EVENT_HANGUP)
76 return 0;
77
78 DCHECK(events & ALOOPER_EVENT_INPUT);
79 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
80 pump->OnDelayedLooperCallback();
81 return 1; // continue listening for events
82}
83
84} // namespace
85
86MessagePumpForUI::MessagePumpForUI() {
87 // The Android native ALooper uses epoll to poll our file descriptors and wake
88 // us up. We use a simple level-triggered eventfd to signal that non-delayed
89 // work is available, and a timerfd to signal when delayed work is ready to
90 // be run.
91 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
92 CHECK_NE(non_delayed_fd_, -1);
93 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
94
95 // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
96 // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
97 // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
98 // fine.
99 delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC);
100 CHECK_NE(delayed_fd_, -1);
101
102 looper_ = ALooper_prepare(0);
103 DCHECK(looper_);
104 // Add a reference to the looper so it isn't deleted on us.
105 ALooper_acquire(looper_);
106 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
107 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
108 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
109 &DelayedLooperCallback, reinterpret_cast<void*>(this));
110}
111
112MessagePumpForUI::~MessagePumpForUI() {
113 DCHECK_EQ(ALooper_forThread(), looper_);
114 ALooper_removeFd(looper_, non_delayed_fd_);
115 ALooper_removeFd(looper_, delayed_fd_);
116 ALooper_release(looper_);
117 looper_ = nullptr;
118
119 close(non_delayed_fd_);
120 close(delayed_fd_);
121}
122
123void MessagePumpForUI::OnDelayedLooperCallback() {
Gabriel Charette9d44a9b2019-04-29 16:35:56124 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
125 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13126 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01127 return;
128
Michael Thiessend7ae7352018-07-10 00:57:13129 // Clear the fd.
130 uint64_t value;
131 int ret = read(delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54132
133 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
134 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
135 // EAGAIN only happens if no timer has expired. Also according to the man page
136 // poll only returns readable when a timer has expired. So this function will
137 // only be called when a timer has expired, but reading reveals no timer has
138 // expired...
139 // Quit() and ScheduleDelayedWork() are the only other functions that touch
140 // the timerfd, and they both run on the same thread as this callback, so
141 // there are no obvious timing or multi-threading related issues.
142 DPCHECK(ret >= 0 || errno == EAGAIN);
143
Gabriel Charette9d44a9b2019-04-29 16:35:56144 delayed_scheduled_time_.reset();
[email protected]61c86c62011-08-02 16:11:16145
Gabriel Charette9d44a9b2019-04-29 16:35:56146 Delegate::NextWorkInfo next_work_info = delegate_->DoSomeWork();
147
Michael Thiessend7ae7352018-07-10 00:57:13