From 480e26c53f48900c5a1bd65aac7411144043cc24 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 6 Jul 2016 16:57:20 +0300 Subject: [PATCH] [android] Add support for timers on the main thread Wake up the main thread run loop with a file descriptor event from a thread that exists for this sole purpose. --- platform/android/src/run_loop.cpp | 33 ++++++++++++++++++++++++-- platform/android/src/run_loop_impl.hpp | 5 ++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp index 886dcab89..007ea2919 100644 --- a/platform/android/src/run_loop.cpp +++ b/platform/android/src/run_loop.cpp @@ -2,12 +2,14 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -53,6 +55,24 @@ int looperCallbackDefault(int fd, int, void* data) { namespace mbgl { namespace util { +// This is needed only for the RunLoop living on the main thread because of +// how we implement timers. We sleep on `ALooper_pollAll` until the next +// timeout, but on the main thread `ALooper_pollAll` is called by the activity +// automatically, thus we cannot set the timeout. Instead we wake the loop +// with an external file descriptor event coming from this thread. +class Alarm { +public: + Alarm(RunLoop::Impl* loop_) : loop(loop_) {} + + void set(const Milliseconds& timeout) { + alarm.start(timeout, mbgl::Duration::zero(), [this]() { loop->wake(); }); + } + +private: + Timer alarm; + RunLoop::Impl* loop; +}; + RunLoop::Impl::Impl(RunLoop* runLoop_, RunLoop::Type type) : runLoop(runLoop_) { using namespace mbgl::android; detach = attach_jni_thread(theJVM, &env, ""); @@ -80,6 +100,8 @@ RunLoop::Impl::Impl(RunLoop* runLoop_, RunLoop::Type type) : runLoop(runLoop_) { case Type::Default: ret = ALooper_addFd(loop, fds[PIPE_OUT], ALOOPER_POLL_CALLBACK, ALOOPER_EVENT_INPUT, looperCallbackDefault, this); + alarm = std::make_unique>(ThreadContext{"Alarm"}, this); + running = true; break; } @@ -89,6 +111,8 @@ RunLoop::Impl::Impl(RunLoop* runLoop_, RunLoop::Type type) : runLoop(runLoop_) { } RunLoop::Impl::~Impl() { + alarm.reset(); + if (ALooper_removeFd(loop, fds[PIPE_OUT]) != 1) { throw std::runtime_error("Failed to remove file descriptor from Looper."); } @@ -160,9 +184,14 @@ Milliseconds RunLoop::Impl::processRunnables() { if (runnables.empty() || nextDue == TimePoint::max()) { return Milliseconds(-1); - } else { - return std::chrono::duration_cast(nextDue - now); } + + auto timeout = std::chrono::duration_cast(nextDue - now); + if (alarm) { + alarm->invoke(&Alarm::set, timeout); + } + + return timeout; } RunLoop* RunLoop::Get() { diff --git a/platform/android/src/run_loop_impl.hpp b/platform/android/src/run_loop_impl.hpp index cb553d1f8..121a5127d 100644 --- a/platform/android/src/run_loop_impl.hpp +++ b/platform/android/src/run_loop_impl.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -15,6 +16,8 @@ struct ALooper; namespace mbgl { namespace util { +class Alarm; + class RunLoop::Impl { public: class Runnable { @@ -50,6 +53,8 @@ private: JNIEnv *env = nullptr; bool detach = false; + std::unique_ptr> alarm; + std::recursive_mutex mtx; std::list runnables; std::list::iterator nextRunnable;