diff --git a/include/mbgl/util/run_loop.hpp b/include/mbgl/util/run_loop.hpp index 4e25caf55..d3a61d418 100644 --- a/include/mbgl/util/run_loop.hpp +++ b/include/mbgl/util/run_loop.hpp @@ -107,6 +107,8 @@ public: return std::make_unique(task); } + class Impl; + private: MBGL_STORE_THREAD(tid) @@ -175,7 +177,6 @@ private: Queue queue; std::mutex mutex; - class Impl; std::unique_ptr impl; }; diff --git a/platform/android/src/async_task.cpp b/platform/android/src/async_task.cpp new file mode 100644 index 000000000..4a68c1c09 --- /dev/null +++ b/platform/android/src/async_task.cpp @@ -0,0 +1,60 @@ +#include "run_loop_impl.hpp" + +#include +#include + +#include +#include + +namespace mbgl { +namespace util { + +class AsyncTask::Impl : public RunLoop::Impl::Runnable { +public: + Impl(std::function&& 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::getLoopHandle()); + + std::atomic_flag queued = ATOMIC_FLAG_INIT; + std::function task; +}; + +AsyncTask::AsyncTask(std::function&& fn) + : impl(std::make_unique(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 000000000..b3b1fd2d9 --- /dev/null +++ b/platform/android/src/run_loop.cpp @@ -0,0 +1,140 @@ +#include "run_loop_impl.hpp" + +#include + +#include + +#include +#include +#include +#include + +namespace { + +using namespace mbgl::util; +static ThreadLocal& current = *new ThreadLocal; + +} // namespace + +namespace mbgl { +namespace util { + +void RunLoop::Impl::addRunnable(Runnable* runnable) { + std::lock_guard 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 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 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(nextDue - now); + } +} + +RunLoop* RunLoop::Get() { + return current.get(); +} + +RunLoop::RunLoop(Type) : impl(std::make_unique()) { + 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 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(&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(&outData)); +} + +void RunLoop::stop() { + impl->running = false; + ALooper_wake(impl->loop); +} + +void RunLoop::addWatch(int, Event, std::function&&) { + 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 000000000..a96ea1a1c --- /dev/null +++ b/platform/android/src/run_loop_impl.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +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::iterator iter; + }; + + void addRunnable(Runnable*); + void removeRunnable(Runnable*); + void initRunnable(Runnable*); + + Milliseconds processRunnables(); + +private: + friend RunLoop; + + ALooper* loop = nullptr; + std::atomic running; + + std::recursive_mutex mtx; + std::list runnables; +}; + +} // namespace util +} // namespace mbgl diff --git a/platform/android/src/timer.cpp b/platform/android/src/timer.cpp new file mode 100644 index 000000000..741005df2 --- /dev/null +++ b/platform/android/src/timer.cpp @@ -0,0 +1,78 @@ +#include "run_loop_impl.hpp" + +#include +#include + +#include + +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&& 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::getLoopHandle()); + + std::function task; +}; + +Timer::Timer() + : impl(std::make_unique()) { +} + +Timer::~Timer() = default; + +void Timer::start(Duration timeout, Duration repeat, std::function&& cb) { + impl->start(timeout, repeat, std::move(cb)); +} + +void Timer::stop() { + impl->stop(); +} + +} // namespace util +} // namespace mbgl