[android] Use ALooper_addFd() and callbacks to wake up

This is needed for plugging into an existing Looper because
we won't call ::run() on the main thread (this is called
on the Java side of the application automatically by Android).

But we still need to wake up and process the events. For that
we create a pipe and write to the pipe to wake up the main
loop and we process events on the callback of the fd handler.

Fixes #4682
This commit is contained in:
Thiago Marcos P. Santos
2016-04-13 17:07:35 +03:00
committed by John Firebaugh
parent 204c7fee03
commit 40e590b681
2 changed files with 95 additions and 22 deletions

View File

@@ -8,18 +8,101 @@
#include <algorithm>
#include <cassert>
#include <functional>
#include <stdexcept>
#include <vector>
#include <fcntl.h>
#include <unistd.h>
#define PIPE_OUT 0
#define PIPE_IN 1
namespace {
using namespace mbgl::util;
static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
int looperCallbackNew(int fd, int, void* data) {
int buffer[1];
while (read(fd, buffer, sizeof(buffer)) > 0) {}
auto loop = reinterpret_cast<ALooper*>(data);
ALooper_wake(loop);
return 1;
}
int looperCallbackDefault(int fd, int, void* data) {
int buffer[1];
while (read(fd, buffer, sizeof(buffer)) > 0) {}
auto runLoop = reinterpret_cast<RunLoop*>(data);
runLoop->runOnce();
return 1;
}
} // namespace
namespace mbgl {
namespace util {
RunLoop::Impl::Impl(RunLoop* runLoop, RunLoop::Type type) {
using namespace mbgl::android;
detach = attach_jni_thread(theJVM, &env, ThreadContext::getName());
loop = ALooper_prepare(0);
assert(loop);
ALooper_acquire(loop);
if (pipe(fds)) {
throw std::runtime_error("Failed to create pipe.");
}
if (fcntl(fds[PIPE_OUT], F_SETFL, O_NONBLOCK)) {
throw std::runtime_error("Failed to set pipe read end non-blocking.");
}
int ret = 0;
switch (type) {
case Type::New:
ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK,
ALOOPER_EVENT_INPUT, looperCallbackNew, loop);
break;
case Type::Default:
ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK,
ALOOPER_EVENT_INPUT, looperCallbackDefault, runLoop);
break;
}
if (ret != 1) {
throw std::runtime_error("Failed to add file descriptor to Looper.");
}
}
RunLoop::Impl::~Impl() {
if (ALooper_removeFd(loop, fds[PIPE_OUT]) != 1) {
throw std::runtime_error("Failed to remove file descriptor from Looper.");
}
if (close(fds[PIPE_IN]) || close(fds[PIPE_OUT])) {
throw std::runtime_error("Failed to close file descriptor.");
}
ALooper_release(loop);
using namespace mbgl::android;
detach_jni_thread(theJVM, &env, detach);
}
void RunLoop::Impl::wake() {
if (write(fds[PIPE_IN], "\n", 1) == -1) {
throw std::runtime_error("Failed to write to file descriptor.");
}
}
void RunLoop::Impl::addRunnable(Runnable* runnable) {
std::lock_guard<std::recursive_mutex> lock(mtx);
@@ -28,7 +111,7 @@ void RunLoop::Impl::addRunnable(Runnable* runnable) {
runnable->iter = std::move(iter);
}
ALooper_wake(loop);
wake();
}
void RunLoop::Impl::removeRunnable(Runnable* runnable) {
@@ -74,25 +157,12 @@ RunLoop* RunLoop::Get() {
return current.get();
}
RunLoop::RunLoop(Type) : impl(std::make_unique<Impl>()) {
using namespace mbgl::android;
impl->detach = attach_jni_thread(theJVM, &impl->env, ThreadContext::getName());
impl->loop = ALooper_prepare(0);
assert(impl->loop);
ALooper_acquire(impl->loop);
RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>(this, type)) {
current.set(this);
}
RunLoop::~RunLoop() {
current.set(nullptr);
ALooper_release(impl->loop);
using namespace mbgl::android;
detach_jni_thread(theJVM, &impl->env, impl->detach);
}
LOOP_HANDLE RunLoop::getLoopHandle() {
@@ -101,7 +171,7 @@ LOOP_HANDLE RunLoop::getLoopHandle() {
void RunLoop::push(std::shared_ptr<WorkTask> task) {
withMutex([&] { queue.push(std::move(task)); });
ALooper_wake(impl->loop);
impl->wake();
}
void RunLoop::run() {
@@ -122,17 +192,13 @@ void RunLoop::run() {
void RunLoop::runOnce() {
MBGL_VERIFY_THREAD(tid);
int outFd, outEvents;
char *outData = nullptr;
process();
impl->processRunnables().count();
ALooper_pollOnce(0, &outFd, &outEvents, reinterpret_cast<void**>(&outData));
impl->processRunnables();
}
void RunLoop::stop() {
impl->running = false;
ALooper_wake(impl->loop);
impl->wake();
}
void RunLoop::addWatch(int, Event, std::function<void(int, Event)>&&) {