• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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/syscall.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/callback_helpers.h"
20 #include "base/lazy_instance.h"
21 #include "base/logging.h"
22 #include "base/run_loop.h"
23 
24 // Android stripped sys/timerfd.h out of their platform headers, so we have to
25 // use syscall to make use of timerfd. Once the min API level is 20, we can
26 // directly use timerfd.h.
27 #ifndef __NR_timerfd_create
28 #error "Unable to find syscall for __NR_timerfd_create"
29 #endif
30 
31 #ifndef TFD_TIMER_ABSTIME
32 #define TFD_TIMER_ABSTIME (1 << 0)
33 #endif
34 
35 using base::android::JavaParamRef;
36 using base::android::ScopedJavaLocalRef;
37 
38 namespace base {
39 
40 namespace {
41 
42 // See sys/timerfd.h
timerfd_create(int clockid,int flags)43 int timerfd_create(int clockid, int flags) {
44   return syscall(__NR_timerfd_create, clockid, flags);
45 }
46 
47 // See sys/timerfd.h
timerfd_settime(int ufc,int flags,const struct itimerspec * utmr,struct itimerspec * otmr)48 int timerfd_settime(int ufc,
49                     int flags,
50                     const struct itimerspec* utmr,
51                     struct itimerspec* otmr) {
52   return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
53 }
54 
NonDelayedLooperCallback(int fd,int events,void * data)55 int NonDelayedLooperCallback(int fd, int events, void* data) {
56   if (events & ALOOPER_EVENT_HANGUP)
57     return 0;
58 
59   DCHECK(events & ALOOPER_EVENT_INPUT);
60   MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
61   pump->OnNonDelayedLooperCallback();
62   return 1;  // continue listening for events
63 }
64 
DelayedLooperCallback(int fd,int events,void * data)65 int DelayedLooperCallback(int fd, int events, void* data) {
66   if (events & ALOOPER_EVENT_HANGUP)
67     return 0;
68 
69   DCHECK(events & ALOOPER_EVENT_INPUT);
70   MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
71   pump->OnDelayedLooperCallback();
72   return 1;  // continue listening for events
73 }
74 
75 }  // namespace
76 
MessagePumpForUI()77 MessagePumpForUI::MessagePumpForUI() {
78   // The Android native ALooper uses epoll to poll our file descriptors and wake
79   // us up. We use a simple level-triggered eventfd to signal that non-delayed
80   // work is available, and a timerfd to signal when delayed work is ready to
81   // be run.
82   non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
83   CHECK_NE(non_delayed_fd_, -1);
84   DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
85 
86   // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
87   // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
88   // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
89   // fine.
90   delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC);
91   CHECK_NE(delayed_fd_, -1);
92 
93   looper_ = ALooper_prepare(0);
94   DCHECK(looper_);
95   // Add a reference to the looper so it isn't deleted on us.
96   ALooper_acquire(looper_);
97   ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
98                 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
99   ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
100                 &DelayedLooperCallback, reinterpret_cast<void*>(this));
101 }
102 
~MessagePumpForUI()103 MessagePumpForUI::~MessagePumpForUI() {
104   DCHECK_EQ(ALooper_forThread(), looper_);
105   ALooper_removeFd(looper_, non_delayed_fd_);
106   ALooper_removeFd(looper_, delayed_fd_);
107   ALooper_release(looper_);
108   looper_ = nullptr;
109 
110   close(non_delayed_fd_);
111   close(delayed_fd_);
112 }
113 
OnDelayedLooperCallback()114 void MessagePumpForUI::OnDelayedLooperCallback() {
115   if (ShouldQuit())
116     return;
117 
118   // Clear the fd.
119   uint64_t value;
120   int ret = read(delayed_fd_, &value, sizeof(value));
121   DCHECK_GE(ret, 0);
122   delayed_scheduled_time_ = base::TimeTicks();
123 
124   base::TimeTicks next_delayed_work_time;
125   delegate_->DoDelayedWork(&next_delayed_work_time);
126   if (!next_delayed_work_time.is_null()) {
127     ScheduleDelayedWork(next_delayed_work_time);
128   }
129   if (ShouldQuit())
130     return;
131   // We may be idle now, so pump the loop to find out.
132   ScheduleWork();
133 }
134 
OnNonDelayedLooperCallback()135 void MessagePumpForUI::OnNonDelayedLooperCallback() {
136   base::TimeTicks next_delayed_work_time;
137   bool did_any_work = false;
138 
139   // Runs all native tasks scheduled to run, scheduling delayed work if
140   // necessary.
141   while (true) {
142     bool did_work_this_loop = false;
143     if (ShouldQuit())
144       return;
145     did_work_this_loop = delegate_->DoWork();
146     if (ShouldQuit())
147       return;
148 
149     did_work_this_loop |= delegate_->DoDelayedWork(&next_delayed_work_time);
150 
151     did_any_work |= did_work_this_loop;
152 
153     // If we didn't do any work, we're out of native tasks to run, and we should
154     // return control to the looper to run Java tasks.
155     if (!did_work_this_loop)
156       break;
157   }
158   // If we did any work, return control to the looper to run java tasks before
159   // we call DoIdleWork(). We haven't cleared the fd yet, so we'll get woken up
160   // again soon to check for idle-ness.
161   if (did_any_work)
162     return;
163   if (ShouldQuit())
164     return;
165 
166   // Read the file descriptor, resetting its contents to 0 and reading back the
167   // stored value.
168   // See http://man7.org/linux/man-pages/man2/eventfd.2.html
169   uint64_t value = 0;
170   int ret = read(non_delayed_fd_, &value, sizeof(value));
171   DCHECK_GE(ret, 0);
172 
173   // If we read a value > 1, it means we lost the race to clear the fd before a
174   // new task was posted. This is okay, we can just re-schedule work.
175   if (value > 1) {
176     ScheduleWork();
177   } else {
178     // At this point, the java looper might not be idle - it's impossible to
179     // know pre-Android-M, so we may end up doing Idle work while java tasks are
180     // still queued up. Note that this won't cause us to fail to run java tasks
181     // using QuitWhenIdle, as the JavaHandlerThread will finish running all
182     // currently scheduled tasks before it quits. Also note that we can't just
183     // add an idle callback to the java looper, as that will fire even if native
184     // tasks are still queued up.
185     DoIdleWork();
186     if (!next_delayed_work_time.is_null()) {
187       ScheduleDelayedWork(next_delayed_work_time);
188     }
189   }
190 }
191 
DoIdleWork()192 void MessagePumpForUI::DoIdleWork() {
193   if (delegate_->DoIdleWork()) {
194     // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
195     // the loop here because we may in fact be idle after doing idle work
196     // without any new tasks being queued.
197     ScheduleWork();
198   }
199 }
200 
Run(Delegate * delegate)201 void MessagePumpForUI::Run(Delegate* delegate) {
202   DCHECK(IsTestImplementation());
203   // This function is only called in tests. We manually pump the native looper
204   // which won't run any java tasks.
205   quit_ = false;
206 
207   SetDelegate(delegate);
208 
209   // Pump the loop once in case we're starting off idle as ALooper_pollOnce will
210   // never return in that case.
211   ScheduleWork();
212   while (true) {
213     // Waits for either the delayed, or non-delayed fds to be signalled, calling
214     // either OnDelayedLooperCallback, or OnNonDelayedLooperCallback,
215     // respectively. This uses Android's Looper implementation, which is based
216     // off of epoll.
217     ALooper_pollOnce(-1, nullptr, nullptr, nullptr);
218     if (quit_)
219       break;
220   }
221 }
222 
Attach(Delegate * delegate)223 void MessagePumpForUI::Attach(Delegate* delegate) {
224   DCHECK(!quit_);
225 
226   // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
227   // can't use Run() like we do on other platforms or we would prevent Java
228   // tasks from running. Instead we create and initialize a run loop here, then
229   // return control back to the Looper.
230 
231   SetDelegate(delegate);
232   run_loop_ = std::make_unique<RunLoop>();
233   // Since the RunLoop was just created above, BeforeRun should be guaranteed to
234   // return true (it only returns false if the RunLoop has been Quit already).
235   if (!run_loop_->BeforeRun())
236     NOTREACHED();
237 }
238 
Quit()239 void MessagePumpForUI::Quit() {
240   if (quit_)
241     return;
242 
243   quit_ = true;
244 
245   int64_t value;
246   // Clear any pending timer.
247   read(delayed_fd_, &value, sizeof(value));
248   // Clear the eventfd.
249   read(non_delayed_fd_, &value, sizeof(value));
250 
251   if (run_loop_) {
252     run_loop_->AfterRun();
253     run_loop_ = nullptr;
254   }
255   if (on_quit_callback_) {
256     std::move(on_quit_callback_).Run();
257   }
258 }
259 
ScheduleWork()260 void MessagePumpForUI::ScheduleWork() {
261   if (ShouldQuit())
262     return;
263 
264   // Write (add) 1 to the eventfd. This tells the Looper to wake up and call our
265   // callback, allowing us to run tasks. This also allows us to detect, when we
266   // clear the fd, whether additional work was scheduled after we finished
267   // performing work, but before we cleared the fd, as we'll read back >=2
268   // instead of 1 in that case.
269   // See the eventfd man pages
270   // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
271   // the read and write APIs for this file descriptor work, specifically without
272   // EFD_SEMAPHORE.
273   uint64_t value = 1;
274   int ret = write(non_delayed_fd_, &value, sizeof(value));
275   DCHECK_GE(ret, 0);
276 }
277 
ScheduleDelayedWork(const TimeTicks & delayed_work_time)278 void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
279   if (ShouldQuit())
280     return;
281 
282   if (!delayed_scheduled_time_.is_null() &&
283       delayed_work_time >= delayed_scheduled_time_) {
284     return;
285   }
286 
287   DCHECK(!delayed_work_time.is_null());
288   delayed_scheduled_time_ = delayed_work_time;
289   int64_t nanos = delayed_work_time.since_origin().InNanoseconds();
290   struct itimerspec ts;
291   ts.it_interval.tv_sec = 0;  // Don't repeat.
292   ts.it_interval.tv_nsec = 0;
293   ts.it_value.tv_sec = nanos / TimeTicks::kNanosecondsPerSecond;
294   ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
295 
296   int ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
297   DCHECK_GE(ret, 0);
298 }
299 
QuitWhenIdle(base::OnceClosure callback)300 void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
301   DCHECK(!on_quit_callback_);
302   DCHECK(run_loop_);
303   on_quit_callback_ = std::move(callback);
304   run_loop_->QuitWhenIdle();
305   // Pump the loop in case we're already idle.
306   ScheduleWork();
307 }
308 
IsTestImplementation() const309 bool MessagePumpForUI::IsTestImplementation() const {
310   return false;
311 }
312 
313 }  // namespace base
314