This repository has been archived by the owner on Aug 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[android] Introduce RunLoop based on Looper
Also implement a Timer and AsyncTask based on Android's Looper.
- Loading branch information
Showing
5 changed files
with
329 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |