658 lines
22 KiB
C++
658 lines
22 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/message_loop/message_pump_android.h"
|
|
|
|
#include <android/looper.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <jni.h>
|
|
#include <sys/eventfd.h>
|
|
#include <sys/timerfd.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <atomic>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "base/android/input_hint_checker.h"
|
|
#include "base/android/jni_android.h"
|
|
#include "base/android/scoped_java_ref.h"
|
|
#include "base/android/yield_to_looper_checker.h"
|
|
#include "base/check.h"
|
|
#include "base/check_op.h"
|
|
#include "base/message_loop/io_watcher.h"
|
|
#include "base/notreached.h"
|
|
#include "base/numerics/safe_conversions.h"
|
|
#include "base/run_loop.h"
|
|
#include "base/task/task_features.h"
|
|
#include "base/time/time.h"
|
|
#include "base/trace_event/trace_event.h"
|
|
#include "build/build_config.h"
|
|
|
|
using base::android::InputHintChecker;
|
|
using base::android::InputHintResult;
|
|
using base::android::YieldToLooperChecker;
|
|
|
|
namespace base {
|
|
|
|
namespace {
|
|
|
|
// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
|
|
// into our code due to the inconsistent ABI on older Android OS versions.
|
|
//
|
|
// https://crbug.com/330761384#comment3. Calls from libutils.so into
|
|
// NonDelayedLooperCallback() and DelayedLooperCallback() confuse aarch64 builds
|
|
// with orderfile instrumentation causing incorrect value in
|
|
// __builtin_return_address(0). Disable instrumentation for them. TODO(pasko):
|
|
// Add these symbols to the orderfile manually or fix the builtin.
|
|
#if defined(ARCH_CPU_X86)
|
|
#define NO_INSTRUMENT_STACK_ALIGN \
|
|
__attribute__((force_align_arg_pointer, no_instrument_function))
|
|
#else
|
|
#define NO_INSTRUMENT_STACK_ALIGN __attribute__((no_instrument_function))
|
|
#endif
|
|
|
|
NO_INSTRUMENT_STACK_ALIGN int NonDelayedLooperCallback(int fd,
|
|
int events,
|
|
void* data) {
|
|
if (events & ALOOPER_EVENT_HANGUP) {
|
|
return 0;
|
|
}
|
|
|
|
DCHECK(events & ALOOPER_EVENT_INPUT);
|
|
MessagePumpAndroid* pump = reinterpret_cast<MessagePumpAndroid*>(data);
|
|
pump->OnNonDelayedLooperCallback();
|
|
return 1; // continue listening for events
|
|
}
|
|
|
|
NO_INSTRUMENT_STACK_ALIGN int DelayedLooperCallback(int fd,
|
|
int events,
|
|
void* data) {
|
|
if (events & ALOOPER_EVENT_HANGUP) {
|
|
return 0;
|
|
}
|
|
|
|
DCHECK(events & ALOOPER_EVENT_INPUT);
|
|
MessagePumpAndroid* pump = reinterpret_cast<MessagePumpAndroid*>(data);
|
|
pump->OnDelayedLooperCallback();
|
|
return 1; // continue listening for events
|
|
}
|
|
|
|
// A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
|
|
// native work below.
|
|
constexpr uint64_t kTryNativeWorkBeforeIdleBit = uint64_t(1) << 32;
|
|
|
|
// Implements IOWatcher to allow any MessagePumpAndroid thread to watch
|
|
// arbitrary file descriptors for I/O events.
|
|
// NOTE: When attempting to watch the same `fd` for the same `mode`, this
|
|
// implementation of IOWatcher will unregister the previously registered
|
|
// FdWatch.
|
|
class IOWatcherImpl : public IOWatcher {
|
|
public:
|
|
explicit IOWatcherImpl(ALooper* looper) : looper_(looper) {}
|
|
|
|
~IOWatcherImpl() override {
|
|
for (auto& [fd, watches] : watched_fds_) {
|
|
ALooper_removeFd(looper_, fd);
|
|
if (auto read_watch = std::exchange(watches.read_watch, nullptr)) {
|
|
read_watch->Detach();
|
|
}
|
|
if (auto write_watch = std::exchange(watches.write_watch, nullptr)) {
|
|
write_watch->Detach();
|
|
}
|
|
}
|
|
}
|
|
|
|
// IOWatcher implementation:
|
|
std::unique_ptr<IOWatcher::FdWatch> WatchFileDescriptorImpl(
|
|
int fd,
|
|
FdWatchDuration duration,
|
|
FdWatchMode mode,
|
|
IOWatcher::FdWatcher& watcher,
|
|
const Location& location) override {
|
|
const bool is_read =
|
|
(mode == FdWatchMode::kRead || mode == FdWatchMode::kReadWrite);
|
|
const bool is_write =
|
|
(mode == FdWatchMode::kWrite || mode == FdWatchMode::kReadWrite);
|
|
TRACE_EVENT("base", "MessagePumpAndroid::IOWatcher::WatchFileDescriptor",
|
|
"fd", fd, "persistent",
|
|
duration == FdWatchDuration::kPersistent, "write_mode",
|
|
is_write, "read_mode", is_read);
|
|
auto& watches = watched_fds_[fd];
|
|
auto watch = std::make_unique<FdWatchImpl>(*this, fd, duration, watcher);
|
|
if (is_write) {
|
|
// Detaches the previous FdWatch if there's an attempt to watch the same
|
|
// `fd` for the same `mode`. This means the previous watch is no longer
|
|
// responsible for controlling the lifetime of the active FD watch. The
|
|
// most common case for this happening is multiple EAGAINs in
|
|
// channel_posix when handling socket/FD writable callbacks.
|
|
if (watches.write_watch) {
|
|
watches.write_watch->Detach();
|
|
}
|
|
watches.write_watch = watch.get();
|
|
}
|
|
if (is_read) {
|
|
if (watches.read_watch) {
|
|
// Analogous to the write scenario.
|
|
watches.read_watch->Detach();
|
|
}
|
|
watches.read_watch = watch.get();
|
|
}
|
|
|
|
const int events = (watches.read_watch ? ALOOPER_EVENT_INPUT : 0) |
|
|
(watches.write_watch ? ALOOPER_EVENT_OUTPUT : 0);
|
|
ALooper_addFd(looper_, fd, 0, events, &OnFdIoEvent, this);
|
|
return watch;
|
|
}
|
|
|
|
private:
|
|
// Scopes the maximum lifetime of an FD watch started by WatchFileDescriptor.
|
|
class FdWatchImpl : public FdWatch {
|
|
public:
|
|
FdWatchImpl(IOWatcherImpl& io_watcher,
|
|
int fd,
|
|
FdWatchDuration duration,
|
|
FdWatcher& fd_watcher)
|
|
: fd_(fd),
|
|
duration_(duration),
|
|
fd_watcher_(fd_watcher),
|
|
io_watcher_(&io_watcher) {}
|
|
|
|
~FdWatchImpl() override {
|
|
Stop();
|
|
if (destruction_flag_) {
|
|
*destruction_flag_ = true;
|
|
}
|
|
}
|
|
|
|
void set_destruction_flag(bool* flag) { destruction_flag_ = flag; }
|
|
int fd() const { return fd_; }
|
|
FdWatcher& fd_watcher() const { return *fd_watcher_; }
|
|
|
|
bool is_persistent() const {
|
|
return duration_ == FdWatchDuration::kPersistent;
|
|
}
|
|
|
|
void Detach() { io_watcher_ = nullptr; }
|
|
|
|
void Stop() {
|
|
if (io_watcher_) {
|
|
std::exchange(io_watcher_, nullptr)->StopWatching(*this);
|
|
}
|
|
}
|
|
|
|
private:
|
|
const int fd_;
|
|
const FdWatchDuration duration_;
|
|
raw_ref<FdWatcher> fd_watcher_;
|
|
raw_ptr<IOWatcherImpl> io_watcher_;
|
|
|
|
// If non-null during destruction, the pointee is set to true. Used to
|
|
// detect reentrant destruction during dispatch.
|
|
raw_ptr<bool> destruction_flag_ = nullptr;
|
|
};
|
|
|
|
enum class EventResult {
|
|
kStopWatching,
|
|
kKeepWatching,
|
|
};
|
|
|
|
static NO_INSTRUMENT_STACK_ALIGN int OnFdIoEvent(int fd,
|
|
int events,
|
|
void* data) {
|
|
switch (static_cast<IOWatcherImpl*>(data)->HandleEvent(fd, events)) {
|
|
case EventResult::kStopWatching:
|
|
return 0;
|
|
case EventResult::kKeepWatching:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
EventResult HandleEvent(int fd, int events) {
|
|
// NOTE: It is possible for Looper to dispatch one last event for `fd`
|
|
// *after* we have removed the FD from the Looper - for example if multiple
|
|
// FDs wake the thread at the same time, and a handler for another FD runs
|
|
// first and removes the watch for `fd`; this callback will have already
|
|
// been queued for `fd` and will still run. As such, we must gracefully
|
|
// tolerate receiving a callback for an FD that is no longer watched.
|
|
auto it = watched_fds_.find(fd);
|
|
if (it == watched_fds_.end()) {
|
|
return EventResult::kStopWatching;
|
|
}
|
|
|
|
auto& watches = it->second;
|
|
const bool is_readable =
|
|
events & (ALOOPER_EVENT_INPUT | ALOOPER_EVENT_HANGUP);
|
|
const bool is_writable =
|
|
events & (ALOOPER_EVENT_OUTPUT | ALOOPER_EVENT_HANGUP);
|
|
auto* read_watch = watches.read_watch.get();
|
|
auto* write_watch = watches.write_watch.get();
|
|
|
|
// Any event dispatch can stop any number of watches, so we're careful to
|
|
// set up destruction observation before dispatching anything.
|
|
bool read_watch_destroyed = false;
|
|
// Don't use this variable directly, see write_watch_destroyed function
|
|
// below.
|
|
bool write_watch_destroyed_helper = false;
|
|
bool fd_removed = false;
|
|
if (read_watch) {
|
|
read_watch->set_destruction_flag(&read_watch_destroyed);
|
|
}
|
|
if (write_watch && read_watch != write_watch) {
|
|
write_watch->set_destruction_flag(&write_watch_destroyed_helper);
|
|
}
|
|
auto write_watch_destroyed = [&]() {
|
|
// Following the rule in the 'if' above, if the objects are the same,
|
|
// then the observer boolean is read_watch_destroyed for the write_watch
|
|
if (read_watch == write_watch) {
|
|
return read_watch_destroyed;
|
|
}
|
|
return write_watch_destroyed_helper;
|
|
};
|
|
watches.removed_flag = &fd_removed;
|
|
|
|
bool did_observe_one_shot_read = false;
|
|
if (read_watch && is_readable) {
|
|
DCHECK_EQ(read_watch->fd(), fd);
|
|
did_observe_one_shot_read = !read_watch->is_persistent();
|
|
read_watch->fd_watcher().OnFdReadable(fd);
|
|
if (!read_watch_destroyed && did_observe_one_shot_read) {
|
|
read_watch->Stop();
|
|
}
|
|
}
|
|
|
|
// If the read and write watches are the same object, it may have been a
|
|
// one-shot watch already consumed by a read above, so we inhibit the
|
|
// write dispatch.
|
|
if (read_watch == write_watch && did_observe_one_shot_read) {
|
|
write_watch = nullptr;
|
|
}
|
|
|
|
if (write_watch && is_writable && !write_watch_destroyed()) {
|
|
DCHECK_EQ(write_watch->fd(), fd);
|
|
DCHECK(!(read_watch == write_watch && read_watch_destroyed));
|
|
const bool is_persistent = write_watch->is_persistent();
|
|
write_watch->fd_watcher().OnFdWritable(fd);
|
|
if (!write_watch_destroyed() && !is_persistent) {
|
|
write_watch->Stop();
|
|
}
|
|
}
|
|
|
|
if (read_watch && !read_watch_destroyed) {
|
|
read_watch->set_destruction_flag(nullptr);
|
|
}
|
|
if (write_watch && !write_watch_destroyed()) {
|
|
write_watch->set_destruction_flag(nullptr);
|
|
}
|
|
|
|
if (fd_removed) {
|
|
return EventResult::kStopWatching;
|
|
}
|
|
|
|
watches.removed_flag = nullptr;
|
|
return EventResult::kKeepWatching;
|
|
}
|
|
|
|
void StopWatching(FdWatchImpl& watch) {
|
|
const int fd = watch.fd();
|
|
auto it = watched_fds_.find(fd);
|
|
if (it == watched_fds_.end()) {
|
|
return;
|
|
}
|
|
|
|
WatchPair& watches = it->second;
|
|
if (watches.read_watch == &watch) {
|
|
watches.read_watch = nullptr;
|
|
}
|
|
if (watches.write_watch == &watch) {
|
|
watches.write_watch = nullptr;
|
|
}
|
|
|
|
const int remaining_events =
|
|
(watches.read_watch ? ALOOPER_EVENT_INPUT : 0) |
|
|
(watches.write_watch ? ALOOPER_EVENT_OUTPUT : 0);
|
|
if (remaining_events) {
|
|
ALooper_addFd(looper_, fd, 0, remaining_events, &OnFdIoEvent, this);
|
|
return;
|
|
}
|
|
|
|
ALooper_removeFd(looper_, fd);
|
|
if (watches.removed_flag) {
|
|
*watches.removed_flag = true;
|
|
}
|
|
watched_fds_.erase(it);
|
|
}
|
|
|
|
private:
|
|
const raw_ptr<ALooper> looper_;
|
|
|
|
// The set of active FdWatches. Note that each FD may have up to two active
|
|
// watches only - one for read and one for write. No two FdWatches can watch
|
|
// the same FD for the same signal. `read_watch` and `write_watch` may point
|
|
// to the same object.
|
|
struct WatchPair {
|
|
raw_ptr<FdWatchImpl> read_watch = nullptr;
|
|
raw_ptr<FdWatchImpl> write_watch = nullptr;
|
|
|
|
// If non-null when this WatchPair is removed, the pointee is set to true.
|
|
// Used to track reentrant map mutations during dispatch.
|
|
raw_ptr<bool> removed_flag = nullptr;
|
|
};
|
|
std::map<int, WatchPair> watched_fds_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
MessagePumpAndroid::MessagePumpAndroid()
|
|
: env_(base::android::AttachCurrentThread()) {
|
|
// The Android native ALooper uses epoll to poll our file descriptors and wake
|
|
// us up. We use a simple level-triggered eventfd to signal that non-delayed
|
|
// work is available, and a timerfd to signal when delayed work is ready to
|
|
// be run.
|
|
non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
|
CHECK_NE(non_delayed_fd_, -1);
|
|
DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
|
|
|
|
delayed_fd_ = checked_cast<int>(
|
|
timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC));
|
|
CHECK_NE(delayed_fd_, -1);
|
|
|
|
looper_ = ALooper_prepare(0);
|
|
DCHECK(looper_);
|
|
// Add a reference to the looper so it isn't deleted on us.
|
|
ALooper_acquire(looper_);
|
|
ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
|
|
&NonDelayedLooperCallback, reinterpret_cast<void*>(this));
|
|
ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
|
|
&DelayedLooperCallback, reinterpret_cast<void*>(this));
|
|
}
|
|
|
|
MessagePumpAndroid::~MessagePumpAndroid() {
|
|
DCHECK_EQ(ALooper_forThread(), looper_);
|
|
io_watcher_.reset();
|
|
ALooper_removeFd(looper_, non_delayed_fd_);
|
|
ALooper_removeFd(looper_, delayed_fd_);
|
|
ALooper_release(looper_);
|
|
looper_ = nullptr;
|
|
|
|
close(non_delayed_fd_);
|
|
close(delayed_fd_);
|
|
}
|
|
|
|
void MessagePumpAndroid::OnDelayedLooperCallback() {
|
|
OnReturnFromLooper();
|
|
// There may be non-Chromium callbacks on the same ALooper which may have left
|
|
// a pending exception set, and ALooper does not check for this between
|
|
// callbacks. Check here, and if there's already an exception, just skip this
|
|
// iteration without clearing the fd. If the exception ends up being non-fatal
|
|
// then we'll just get called again on the next polling iteration.
|
|
if (base::android::HasException(env_)) {
|
|
return;
|
|
}
|
|
|
|
// ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
|
|
// resulted in Quit() in the same round.
|
|
if (ShouldQuit()) {
|
|
return;
|
|
}
|
|
|
|
// Clear the fd.
|
|
uint64_t value;
|
|
long ret = read(delayed_fd_, &value, sizeof(value));
|
|
|
|
// TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
|
|
// According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
|
|
// EAGAIN only happens if no timer has expired. Also according to the man page
|
|
// poll only returns readable when a timer has expired. So this function will
|
|
// only be called when a timer has expired, but reading reveals no timer has
|
|
// expired...
|
|
// Quit() and ScheduleDelayedWork() are the only other functions that touch
|
|
// the timerfd, and they both run on the same thread as this callback, so
|
|
// there are no obvious timing or multi-threading related issues.
|
|
DPCHECK(ret >= 0 || errno == EAGAIN);
|
|
DoDelayedLooperWork();
|
|
}
|
|
|
|
void MessagePumpAndroid::DoDelayedLooperWork() {
|
|
delayed_scheduled_time_.reset();
|
|
|
|
Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
|
|
|
|
if (ShouldQuit()) {
|
|
return;
|
|
}
|
|
|
|
if (next_work_info.is_immediate()) {
|
|
ScheduleWork();
|
|
return;
|
|
}
|
|
|
|
delegate_->DoIdleWork();
|
|
if (!next_work_info.delayed_run_time.is_max()) {
|
|
ScheduleDelayedWork(next_work_info);
|
|
}
|
|
}
|
|
|
|
void MessagePumpAndroid::OnNonDelayedLooperCallback() {
|
|
OnReturnFromLooper();
|
|
// There may be non-Chromium callbacks on the same ALooper which may have left
|
|
// a pending exception set, and ALooper does not check for this between
|
|
// callbacks. Check here, and if there's already an exception, just skip this
|
|
// iteration without clearing the fd. If the exception ends up being non-fatal
|
|
// then we'll just get called again on the next polling iteration.
|
|
if (base::android::HasException(env_)) {
|
|
return;
|
|
}
|
|
|
|
// ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
|
|
// resulted in Quit() in the same round.
|
|
if (ShouldQuit()) {
|
|
return;
|
|
}
|
|
|
|
// We're about to process all the work requested by ScheduleWork().
|
|
// MessagePump users are expected to do their best not to invoke
|
|
// ScheduleWork() again before DoWork() returns a non-immediate
|
|
// NextWorkInfo below. Hence, capturing the file descriptor's value now and
|
|
// resetting its contents to 0 should be okay. The value currently stored
|
|
// should be greater than 0 since work having been scheduled is the reason
|
|
// we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
|
|
uint64_t value = 0;
|
|
long ret = read(non_delayed_fd_, &value, sizeof(value));
|
|
DPCHECK(ret >= 0);
|
|
DCHECK_GT(value, 0U);
|
|
bool do_idle_work = value == kTryNativeWorkBeforeIdleBit;
|
|
DoNonDelayedLooperWork(do_idle_work);
|
|
}
|
|
|
|
void MessagePumpAndroid::DoNonDelayedLooperWork(bool do_idle_work) {
|
|
// Note: We can't skip DoWork() even if |do_idle_work| is true here (i.e. no
|
|
// additional ScheduleWork() since yielding to native) as delayed tasks might
|
|
// have come in and we need to re-sample |next_work_info|.
|
|
|
|
// Runs all application tasks scheduled to run.
|
|
Delegate::NextWorkInfo next_work_info;
|
|
do {
|
|
if (ShouldQuit()) {
|
|
return;
|
|
}
|
|
|
|
next_work_info = delegate_->DoWork();
|
|
|
|
if (is_type_ui_ && next_work_info.is_immediate()) {
|
|
// To reduce startup ANRs, yield if an embedder signifies that startup is
|
|
// currently running.
|
|
if (YieldToLooperChecker::GetInstance().ShouldYield()) {
|
|
ScheduleWork();
|
|
return;
|
|
}
|
|
|
|
// As an optimization, yield to the Looper when input events are waiting
|
|
// to be handled. In some cases input events can remain undetected. Such
|
|
// "input hint false negatives" happen, for example, during
|
|
// initialization, in multi-window cases, or when a previous value is
|
|
// cached to throttle polling the input channel.
|
|
if (InputHintChecker::HasInput()) {
|
|
ScheduleWork();
|
|
return;
|
|
}
|
|
}
|
|
} while (next_work_info.is_immediate());
|
|
|
|
// Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
|
|
// allow nesting so needing to resume in an outer loop is not an issue
|
|
// either).
|
|
if (ShouldQuit()) {
|
|
return;
|
|
}
|
|
|
|
// Do the idle work.
|
|
//
|
|
// At this point, the Java Looper might not be idle. It is possible to skip
|
|
// idle work if !MessageQueue.isIdle(), but this check is not very accurate
|
|
// because the MessageQueue does not know about the additional tasks
|
|
// potentially waiting in the Looper.
|
|
//
|
|
// Note that this won't cause us to fail to run java tasks using QuitWhenIdle,
|
|
// as the JavaHandlerThread will finish running all currently scheduled tasks
|
|
// before it quits. Also note that we can't just add an idle callback to the
|
|
// java looper, as that will fire even if application tasks are still queued
|
|
// up.
|
|
delegate_->DoIdleWork();
|
|
if (!next_work_info.delayed_run_time.is_max()) {
|
|
ScheduleDelayedWork(next_work_info);
|
|
}
|
|
}
|
|
|
|
void MessagePumpAndroid::Run(Delegate* delegate) {
|
|
NOTREACHED() << "Unexpected call to Run()";
|
|
}
|
|
|
|
void MessagePumpAndroid::Attach(Delegate* delegate) {
|
|
DCHECK(!quit_);
|
|
|
|
// Since the Looper is controlled by the UI thread or JavaHandlerThread, we
|
|
// can't use Run() like we do on other platforms or we would prevent Java
|
|
// tasks from running. Instead we create and initialize a run loop here, then
|
|
// return control back to the Looper.
|
|
|
|
SetDelegate(delegate);
|
|
run_loop_ = std::make_unique<RunLoop>();
|
|
// Since the RunLoop was just created above, BeforeRun should be guaranteed to
|
|
// return true (it only returns false if the RunLoop has been Quit already).
|
|
CHECK(run_loop_->BeforeRun());
|
|
}
|
|
|
|
void MessagePumpAndroid::Quit() {
|
|
if (quit_) {
|
|
return;
|
|
}
|
|
|
|
quit_ = true;
|
|
|
|
int64_t value;
|
|
// Clear any pending timer.
|
|
read(delayed_fd_, &value, sizeof(value));
|
|
// Clear the eventfd.
|
|
read(non_delayed_fd_, &value, sizeof(value));
|
|
|
|
if (run_loop_) {
|
|
run_loop_->AfterRun();
|
|
run_loop_ = nullptr;
|
|
}
|
|
if (on_quit_callback_) {
|
|
std::move(on_quit_callback_).Run();
|
|
}
|
|
}
|
|
|
|
void MessagePumpAndroid::ScheduleWork() {
|
|
ScheduleWorkInternal(/*do_idle_work=*/false);
|
|
}
|
|
|
|
void MessagePumpAndroid::ScheduleWorkInternal(bool do_idle_work) {
|
|
// Write (add) |value| to the eventfd. This tells the Looper to wake up and
|
|
// call our callback, allowing us to run tasks. This also allows us to detect,
|
|
// when we clear the fd, whether additional work was scheduled after we
|
|
// finished performing work, but before we cleared the fd, as we'll read back
|
|
// >=2 instead of 1 in that case. See the eventfd man pages
|
|
// (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
|
|
// the read and write APIs for this file descriptor work, specifically without
|
|
// EFD_SEMAPHORE.
|
|
// Note: Calls with |do_idle_work| set to true may race with potential calls
|
|
// where the parameter is false. This is fine as write() is adding |value|,
|
|
// not overwriting the existing value, and as such racing calls would merely
|
|
// have their values added together. Since idle work is only executed when the
|
|
// value read equals kTryNativeWorkBeforeIdleBit, a race would prevent idle
|
|
// work from being run and trigger another call to this method with
|
|
// |do_idle_work| set to true.
|
|
uint64_t value = do_idle_work ? kTryNativeWorkBeforeIdleBit : 1;
|
|
long ret = write(non_delayed_fd_, &value, sizeof(value));
|
|
DPCHECK(ret >= 0);
|
|
}
|
|
|
|
void MessagePumpAndroid::OnReturnFromLooper() {
|
|
if (!is_type_ui_) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
void MessagePumpAndroid::ScheduleDelayedWork(
|
|
const Delegate::NextWorkInfo& next_work_info) {
|
|
if (ShouldQuit()) {
|
|
return;
|
|
}
|
|
|
|
if (delayed_scheduled_time_ &&
|
|
*delayed_scheduled_time_ == next_work_info.delayed_run_time) {
|
|
return;
|
|
}
|
|
|
|
DCHECK(!next_work_info.is_immediate());
|
|
delayed_scheduled_time_ = next_work_info.delayed_run_time;
|
|
int64_t nanos =
|
|
next_work_info.delayed_run_time.since_origin().InNanoseconds();
|
|
struct itimerspec ts;
|
|
ts.it_interval.tv_sec = 0; // Don't repeat.
|
|
ts.it_interval.tv_nsec = 0;
|
|
ts.it_value.tv_sec =
|
|
static_cast<time_t>(nanos / TimeTicks::kNanosecondsPerSecond);
|
|
ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
|
|
|
|
long ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
|
|
DPCHECK(ret >= 0);
|
|
}
|
|
|
|
IOWatcher* MessagePumpAndroid::GetIOWatcher() {
|
|
if (!io_watcher_) {
|
|
io_watcher_ = std::make_unique<IOWatcherImpl>(looper_);
|
|
}
|
|
return io_watcher_.get();
|
|
}
|
|
|
|
bool MessagePumpAndroid::IsAsyncIOSupported() {
|
|
return true;
|
|
}
|
|
|
|
void MessagePumpAndroid::QuitWhenIdle(base::OnceClosure callback) {
|
|
DCHECK(!on_quit_callback_);
|
|
DCHECK(run_loop_);
|
|
on_quit_callback_ = std::move(callback);
|
|
run_loop_->QuitWhenIdle();
|
|
// Pump the loop in case we're already idle.
|
|
ScheduleWork();
|
|
}
|
|
|
|
MessagePump::Delegate* MessagePumpAndroid::SetDelegate(Delegate* delegate) {
|
|
return std::exchange(delegate_, delegate);
|
|
}
|
|
|
|
bool MessagePumpAndroid::SetQuit(bool quit) {
|
|
return std::exchange(quit_, quit);
|
|
}
|
|
|
|
} // namespace base
|