summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThiago Marcos P. Santos <thiago@mapbox.com>2016-03-08 13:32:40 -0300
committerThiago Marcos P. Santos <thiago@mapbox.com>2016-04-06 04:52:51 -0300
commit74a1d3c1641673409a7058869249cf260870b9c0 (patch)
tree6dbb8b085f5f73d7360ad01bfaaceb1f2500d0a6
parent08d12f860e3eeac810cbb9355eb168fc8de38ce2 (diff)
downloadqtlocation-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.hpp3
-rw-r--r--platform/android/src/async_task.cpp60
-rw-r--r--platform/android/src/run_loop.cpp140
-rw-r--r--platform/android/src/run_loop_impl.hpp45
-rw-r--r--platform/android/src/timer.cpp78
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