summaryrefslogtreecommitdiff
path: root/platform/default/src/mbgl/util/run_loop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'platform/default/src/mbgl/util/run_loop.cpp')
-rw-r--r--platform/default/src/mbgl/util/run_loop.cpp219
1 files changed, 219 insertions, 0 deletions
diff --git a/platform/default/src/mbgl/util/run_loop.cpp b/platform/default/src/mbgl/util/run_loop.cpp
new file mode 100644
index 0000000000..868ee72114
--- /dev/null
+++ b/platform/default/src/mbgl/util/run_loop.cpp
@@ -0,0 +1,219 @@
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/async_task.hpp>
+#include <mbgl/util/thread_local.hpp>
+#include <mbgl/actor/scheduler.hpp>
+
+#include <uv.h>
+
+#include <cassert>
+#include <functional>
+#include <unordered_map>
+
+namespace {
+
+void dummyCallback(uv_async_t*) {}
+
+} // namespace
+
+namespace mbgl {
+namespace util {
+
+struct Watch {
+ static void onEvent(uv_poll_t* poll, int, int event) {
+ auto watch = reinterpret_cast<Watch*>(poll->data);
+
+ RunLoop::Event watchEvent = RunLoop::Event::None;
+ switch (event) {
+ case UV_READABLE:
+ watchEvent = RunLoop::Event::Read;
+ break;
+ case UV_WRITABLE:
+ watchEvent = RunLoop::Event::Write;
+ break;
+ case UV_READABLE | UV_WRITABLE:
+ watchEvent = RunLoop::Event::ReadWrite;
+ break;
+ }
+
+ watch->eventCallback(watch->fd, watchEvent);
+ };
+
+ static void onClose(uv_handle_t *poll) {
+ auto watch = reinterpret_cast<Watch*>(poll->data);
+ watch->closeCallback();
+ };
+
+ uv_poll_t poll;
+ int fd;
+
+ std::function<void(int, RunLoop::Event)> eventCallback;
+ std::function<void()> closeCallback;
+};
+
+RunLoop* RunLoop::Get() {
+ assert(static_cast<RunLoop*>(Scheduler::GetCurrent()));
+ return static_cast<RunLoop*>(Scheduler::GetCurrent());
+}
+
+class RunLoop::Impl {
+public:
+ void closeHolder() {
+ uv_close(holderHandle(), [](uv_handle_t* h) {
+ delete reinterpret_cast<uv_async_t*>(h);
+ });
+ }
+
+ uv_handle_t* holderHandle() {
+ return reinterpret_cast<uv_handle_t*>(holder);
+ }
+
+ uv_loop_t *loop = nullptr;
+ uv_async_t* holder = new uv_async_t;
+
+ RunLoop::Type type;
+ std::unique_ptr<AsyncTask> async;
+
+ std::unordered_map<int, std::unique_ptr<Watch>> watchPoll;
+};
+
+RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) {
+ switch (type) {
+ case Type::New:
+ impl->loop = new uv_loop_t;
+ if (uv_loop_init(impl->loop) != 0) {
+ throw std::runtime_error("Failed to initialize loop.");
+ }
+ break;
+ case Type::Default:
+ impl->loop = uv_default_loop();
+ break;
+ }
+
+ // Just for holding a ref to the main loop and keep
+ // it alive as required by libuv.
+ if (uv_async_init(impl->loop, impl->holder, dummyCallback) != 0) {
+ throw std::runtime_error("Failed to initialize async.");
+ }
+
+ impl->type = type;
+
+ Scheduler::SetCurrent(this);
+ impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
+}
+
+RunLoop::~RunLoop() {
+ Scheduler::SetCurrent(nullptr);
+
+ // Close the dummy handle that we have
+ // just to keep the main loop alive.
+ impl->closeHolder();
+
+ if (impl->type == Type::Default) {
+ return;
+ }
+
+ // Run the loop again to ensure that async
+ // close callbacks have been called. Not needed
+ // for the default main loop because it is only
+ // closed when the application exits.
+ impl->async.reset();
+ runOnce();
+
+ if (uv_loop_close(impl->loop) == UV_EBUSY) {
+ assert(false && "Failed to close loop.");
+ }
+ delete impl->loop;
+}
+
+LOOP_HANDLE RunLoop::getLoopHandle() {
+ return Get()->impl->loop;
+}
+
+void RunLoop::wake() {
+ impl->async->send();
+}
+
+void RunLoop::run() {
+ MBGL_VERIFY_THREAD(tid);
+
+ uv_ref(impl->holderHandle());
+ uv_run(impl->loop, UV_RUN_DEFAULT);
+}
+
+void RunLoop::runOnce() {
+ MBGL_VERIFY_THREAD(tid);
+
+ uv_run(impl->loop, UV_RUN_NOWAIT);
+}
+
+void RunLoop::stop() {
+ invoke([&] { uv_unref(impl->holderHandle()); });
+}
+
+void RunLoop::addWatch(int fd, Event event, std::function<void(int, Event)>&& callback) {
+ MBGL_VERIFY_THREAD(tid);
+
+ Watch *watch = nullptr;
+ auto watchPollIter = impl->watchPoll.find(fd);
+
+ if (watchPollIter == impl->watchPoll.end()) {
+ std::unique_ptr<Watch> watchPtr = std::make_unique<Watch>();
+
+ watch = watchPtr.get();
+ impl->watchPoll[fd] = std::move(watchPtr);
+
+ if (uv_poll_init(impl->loop, &watch->poll, fd)) {
+ throw std::runtime_error("Failed to init poll on file descriptor.");
+ }
+ } else {
+ watch = watchPollIter->second.get();
+ }
+
+ watch->poll.data = watch;
+ watch->fd = fd;
+ watch->eventCallback = std::move(callback);
+
+ int pollEvent = 0;
+ switch (event) {
+ case Event::Read:
+ pollEvent = UV_READABLE;
+ break;
+ case Event::Write:
+ pollEvent = UV_WRITABLE;
+ break;
+ case Event::ReadWrite:
+ pollEvent = UV_READABLE | UV_WRITABLE;
+ break;
+ default:
+ throw std::runtime_error("Unhandled event.");
+ }
+
+ if (uv_poll_start(&watch->poll, pollEvent, &Watch::onEvent)) {
+ throw std::runtime_error("Failed to start poll on file descriptor.");
+ }
+}
+
+void RunLoop::removeWatch(int fd) {
+ MBGL_VERIFY_THREAD(tid);
+
+ auto watchPollIter = impl->watchPoll.find(fd);
+ if (watchPollIter == impl->watchPoll.end()) {
+ return;
+ }
+
+ Watch* watch = watchPollIter->second.release();
+ impl->watchPoll.erase(watchPollIter);
+
+ watch->closeCallback = [watch] {
+ delete watch;
+ };
+
+ if (uv_poll_stop(&watch->poll)) {
+ throw std::runtime_error("Failed to stop poll on file descriptor.");
+ }
+
+ uv_close(reinterpret_cast<uv_handle_t*>(&watch->poll), &Watch::onClose);
+}
+
+} // namespace util
+} // namespace mbgl