245 lines
6.0 KiB
C++
245 lines
6.0 KiB
C++
#include <mbgl/actor/scheduler.hpp>
|
|
#include <mbgl/util/async_task.hpp>
|
|
#include <mbgl/util/monotonic_timer.hpp>
|
|
#include <mbgl/util/run_loop.hpp>
|
|
#include <mbgl/util/thread_local.hpp>
|
|
|
|
#include <uv.h>
|
|
|
|
#include <cassert>
|
|
#include <functional>
|
|
#include <unordered_map>
|
|
#include <stdexcept>
|
|
|
|
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() {
|
|
if (impl->async) 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::updateTime() {
|
|
MBGL_VERIFY_THREAD(tid);
|
|
|
|
uv_update_time(impl->loop);
|
|
}
|
|
|
|
void RunLoop::waitForEmpty([[maybe_unused]] const mbgl::util::SimpleIdentity tag) {
|
|
while (true) {
|
|
std::size_t remaining;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
remaining = defaultQueue.size() + highPriorityQueue.size();
|
|
}
|
|
|
|
if (remaining == 0) {
|
|
return;
|
|
}
|
|
|
|
runOnce();
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
#ifdef WIN32
|
|
if (uv_poll_init_socket(impl->loop, &watch->poll, fd)) {
|
|
#else
|
|
if (uv_poll_init(impl->loop, &watch->poll, fd)) {
|
|
#endif
|
|
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
|