Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[android] Introduce RunLoop based on Looper
Browse files Browse the repository at this point in the history
Also implement a Timer and AsyncTask based on Android's Looper.
  • Loading branch information
tmpsantos committed Mar 31, 2016
1 parent 0539f7b commit a610d14
Show file tree
Hide file tree
Showing 5 changed files with 329 additions and 1 deletion.
3 changes: 2 additions & 1 deletion include/mbgl/util/run_loop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ class RunLoop : private util::noncopyable {
return std::make_unique<WorkRequest>(task);
}

class Impl;

private:
MBGL_STORE_THREAD(tid)

Expand Down Expand Up @@ -175,7 +177,6 @@ class RunLoop : private util::noncopyable {
Queue queue;
std::mutex mutex;

class Impl;
std::unique_ptr<Impl> impl;
};

Expand Down
60 changes: 60 additions & 0 deletions platform/android/src/async_task.cpp
Original file line number Diff line number Diff line change
@@ -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
140 changes: 140 additions & 0 deletions platform/android/src/run_loop.cpp
Original file line number Diff line number Diff line change
@@ -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
45 changes: 45 additions & 0 deletions platform/android/src/run_loop_impl.hpp
Original file line number Diff line number Diff line change
@@ -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
82 changes: 82 additions & 0 deletions platform/android/src/timer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#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;

// Won't really re-add, but it will
// wake up the main loop to update
// the sleep timeout.
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

0 comments on commit a610d14

Please sign in to comment.