diff options
author | Thiago Marcos P. Santos <thiago@mapbox.com> | 2016-03-08 13:32:40 -0300 |
---|---|---|
committer | Thiago Marcos P. Santos <thiago@mapbox.com> | 2016-04-06 04:52:51 -0300 |
commit | 74a1d3c1641673409a7058869249cf260870b9c0 (patch) | |
tree | 6dbb8b085f5f73d7360ad01bfaaceb1f2500d0a6 | |
parent | 08d12f860e3eeac810cbb9355eb168fc8de38ce2 (diff) | |
download | qtlocation-mapboxgl-74a1d3c1641673409a7058869249cf260870b9c0.tar.gz |
[android] Introduce RunLoop based on Looper
Also implement a Timer and AsyncTask based on Android's Looper.
-rw-r--r-- | include/mbgl/util/run_loop.hpp | 3 | ||||
-rw-r--r-- | platform/android/src/async_task.cpp | 60 | ||||
-rw-r--r-- | platform/android/src/run_loop.cpp | 140 | ||||
-rw-r--r-- | platform/android/src/run_loop_impl.hpp | 45 | ||||
-rw-r--r-- | platform/android/src/timer.cpp | 78 |
5 files changed, 325 insertions, 1 deletions
diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index 4e25caf554..d3a61d4186 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -107,6 +107,8 @@ public: return std::make_unique<WorkRequest>(task); } + class Impl; + private: MBGL_STORE_THREAD(tid) @@ -175,7 +177,6 @@ private: Queue queue; std::mutex mutex; - class Impl; std::unique_ptr<Impl> impl; }; diff --git a/platform/android/src/async_task.cpp b/platform/android/src/async_task.cpp new file mode 100644 index 0000000000..4a68c1c093 --- /dev/null +++ b/platform/android/src/async_task.cpp @@ -0,0 +1,60 @@ +#include "run_loop_impl.hpp" + +#include <mbgl/util/async_task.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <atomic> +#include <functional> + +namespace mbgl { +namespace util { + +class AsyncTask::Impl : public RunLoop::Impl::Runnable { +public: + Impl(std::function<void()>&& fn) + : task(std::move(fn)) { + loop->initRunnable(this); + } + + ~Impl() { + loop->removeRunnable(this); + } + + void maySend() { + if (!queued.test_and_set()) { + loop->addRunnable(this); + } + } + + TimePoint dueTime() const override { + return due; + } + + void runTask() override { + loop->removeRunnable(this); + queued.clear(); + task(); + } + +private: + // Always expired, run immediately. + const TimePoint due = Clock::now(); + + RunLoop::Impl* loop = reinterpret_cast<RunLoop::Impl*>(RunLoop::getLoopHandle()); + + std::atomic_flag queued = ATOMIC_FLAG_INIT; + std::function<void()> task; +}; + +AsyncTask::AsyncTask(std::function<void()>&& fn) + : impl(std::make_unique<Impl>(std::move(fn))) { +} + +AsyncTask::~AsyncTask() = default; + +void AsyncTask::send() { + impl->maySend(); +} + +} // namespace util +} // namespace mbgl diff --git a/platform/android/src/run_loop.cpp b/platform/android/src/run_loop.cpp new file mode 100644 index 0000000000..b3b1fd2d95 --- /dev/null +++ b/platform/android/src/run_loop.cpp @@ -0,0 +1,140 @@ +#include "run_loop_impl.hpp" + +#include <mbgl/util/thread_local.hpp> + +#include <android/looper.h> + +#include <algorithm> +#include <cassert> +#include <functional> +#include <vector> + +namespace { + +using namespace mbgl::util; +static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>; + +} // namespace + +namespace mbgl { +namespace util { + +void RunLoop::Impl::addRunnable(Runnable* runnable) { + std::lock_guard<std::recursive_mutex> lock(mtx); + + if (runnable->iter == runnables.end()) { + auto iter = runnables.insert(runnables.end(), runnable); + runnable->iter = std::move(iter); + } + + ALooper_wake(loop); +} + +void RunLoop::Impl::removeRunnable(Runnable* runnable) { + std::lock_guard<std::recursive_mutex> lock(mtx); + + if (runnable->iter != runnables.end()) { + runnables.erase(runnable->iter); + runnable->iter = runnables.end(); + } +} + +void RunLoop::Impl::initRunnable(Runnable* runnable) { + runnable->iter = runnables.end(); +} + +Milliseconds RunLoop::Impl::processRunnables() { + std::lock_guard<std::recursive_mutex> lock(mtx); + + auto now = Clock::now(); + auto nextDue = TimePoint::max(); + + // O(N) but in the render thread where we get tons + // of messages, the size of the list is usually 1~2. + for (auto iter = runnables.begin(); iter != runnables.end();) { + Runnable* runnable = *(iter++); + + auto const dueTime = runnable->dueTime(); + if (dueTime <= now) { + runnable->runTask(); + } else { + nextDue = std::min(nextDue, dueTime); + } + } + + if (runnables.empty() || nextDue == TimePoint::max()) { + return Milliseconds(-1); + } else { + return std::chrono::duration_cast<Milliseconds>(nextDue - now); + } +} + +RunLoop* RunLoop::Get() { + return current.get(); +} + +RunLoop::RunLoop(Type) : impl(std::make_unique<Impl>()) { + impl->loop = ALooper_prepare(0); + assert(impl->loop); + + ALooper_acquire(impl->loop); + + current.set(this); +} + +RunLoop::~RunLoop() { + current.set(nullptr); + + ALooper_release(impl->loop); +} + +LOOP_HANDLE RunLoop::getLoopHandle() { + return current.get()->impl.get(); +} + +void RunLoop::push(std::shared_ptr<WorkTask> task) { + withMutex([&] { queue.push(std::move(task)); }); + ALooper_wake(impl->loop); +} + +void RunLoop::run() { + MBGL_VERIFY_THREAD(tid); + + impl->running = true; + + int outFd, outEvents; + char *outData = nullptr; + + while (impl->running) { + process(); + auto timeout = impl->processRunnables().count(); + ALooper_pollAll(timeout, &outFd, &outEvents, reinterpret_cast<void**>(&outData)); + } +} + +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)); +} + +void RunLoop::stop() { + impl->running = false; + ALooper_wake(impl->loop); +} + +void RunLoop::addWatch(int, Event, std::function<void(int, Event)>&&) { + throw std::runtime_error("Not implemented."); +} + +void RunLoop::removeWatch(int) { + throw std::runtime_error("Not implemented."); +} + +} // namespace util +} // namespace mbgl diff --git a/platform/android/src/run_loop_impl.hpp b/platform/android/src/run_loop_impl.hpp new file mode 100644 index 0000000000..a96ea1a1c9 --- /dev/null +++ b/platform/android/src/run_loop_impl.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include <mbgl/util/chrono.hpp> +#include <mbgl/util/run_loop.hpp> + +#include <atomic> +#include <list> +#include <memory> +#include <mutex> + +struct ALooper; + +namespace mbgl { +namespace util { + +class RunLoop::Impl { +public: + class Runnable { + public: + virtual ~Runnable() = default; + + virtual void runTask() = 0; + virtual TimePoint dueTime() const = 0; + + std::list<Runnable*>::iterator iter; + }; + + void addRunnable(Runnable*); + void removeRunnable(Runnable*); + void initRunnable(Runnable*); + + Milliseconds processRunnables(); + +private: + friend RunLoop; + + ALooper* loop = nullptr; + std::atomic<bool> running; + + std::recursive_mutex mtx; + std::list<Runnable*> runnables; +}; + +} // namespace util +} // namespace mbgl diff --git a/platform/android/src/timer.cpp b/platform/android/src/timer.cpp new file mode 100644 index 0000000000..741005df23 --- /dev/null +++ b/platform/android/src/timer.cpp @@ -0,0 +1,78 @@ +#include "run_loop_impl.hpp" + +#include <mbgl/util/run_loop.hpp> +#include <mbgl/util/timer.hpp> + +#include <functional> + +namespace mbgl { +namespace util { + +class Timer::Impl : public RunLoop::Impl::Runnable { +public: + Impl() { + loop->initRunnable(this); + } + + ~Impl() { + stop(); + } + + void start(Duration timeout, Duration repeat_, std::function<void ()>&& task_) { + stop(); + + repeat = repeat_; + task = std::move(task_); + due = Clock::now() + timeout; + + loop->addRunnable(this); + } + + void stop() { + task = nullptr; + loop->removeRunnable(this); + } + + void reschedule() { + if (repeat != Duration::zero()) { + due = Clock::now() + repeat; + loop->addRunnable(this); + } else { + stop(); + } + } + + TimePoint dueTime() const override { + return due; + } + + void runTask() override { + task(); + reschedule(); + } + +private: + TimePoint due; + Duration repeat; + + RunLoop::Impl* loop = reinterpret_cast<RunLoop::Impl*>(RunLoop::getLoopHandle()); + + std::function<void()> task; +}; + +Timer::Timer() + : impl(std::make_unique<Impl>()) { +} + +Timer::~Timer() = default; + +void Timer::start(Duration timeout, Duration repeat, std::function<void()>&& cb) { + impl->start(timeout, repeat, std::move(cb)); +} + +void Timer::stop() { + impl->stop(); +} + +} // namespace util +} // namespace mbgl |