234 lines
5.6 KiB
C++
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
|