Files
MapLibreParticleLab/platform/default/run_loop.cpp
Thiago Marcos P. Santos d7fdcc73bf [core] Get rid of ::unref() for Timer and AsyncTask
Not need, legacy from libuv. The RunLoop keep the main
loop running until is explicitly no longer needed.
2016-01-09 01:26:29 +02:00

234 lines
5.6 KiB
C++

#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/async_task.hpp>
#include <mbgl/util/thread_local.hpp>
#include <uv.h>
#include <cassert>
#include <functional>
#include <unordered_map>
namespace {
using namespace mbgl::util;
static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>;
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
void dummyCallback(uv_async_t*, int) {};
#else
void dummyCallback(uv_async_t*) {};
#endif
} // 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() {
return current.get();
}
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:
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
impl->loop = uv_loop_new();
if (impl->loop == nullptr) {
#else
impl->loop = new uv_loop_t;
if (uv_loop_init(impl->loop) != 0) {
#endif
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;
current.set(this);
impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this));
}
RunLoop::~RunLoop() {
current.set(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_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
uv_loop_delete(impl->loop);
#else
if (uv_loop_close(impl->loop) == UV_EBUSY) {
throw std::runtime_error("Failed to close loop.");
}
delete impl->loop;
#endif
}
LOOP_HANDLE RunLoop::getLoopHandle() {
return current.get()->impl->loop;
}
void RunLoop::push(std::shared_ptr<WorkTask> task) {
withMutex([&] { queue.push(std::move(task)); });
impl->async->send();
}
void RunLoop::run() {
MBGL_VERIFY_THREAD(tid);
uv_run(impl->loop, UV_RUN_DEFAULT);
}
void RunLoop::runOnce() {
MBGL_VERIFY_THREAD(tid);
uv_run(impl->loop, UV_RUN_ONCE);
}
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