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.
[Qt] Implement the AsyncTask, Timer and RunLoop using Qt
Now Mapbox GL will handle events using Qt as backend instead of libuv.
- Loading branch information
Showing
9 changed files
with
366 additions
and
3 deletions.
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,44 @@ | ||
#include <mbgl/util/async_task.hpp> | ||
|
||
#include "async_task_impl.hpp" | ||
|
||
#include <mbgl/util/run_loop.hpp> | ||
|
||
namespace mbgl { | ||
namespace util { | ||
|
||
AsyncTask::Impl::Impl(std::function<void()>&& fn) | ||
: runLoop(RunLoop::Get()), | ||
task(std::move(fn)) { | ||
connect(this, SIGNAL(send(void)), this, SLOT(runTask(void)), Qt::QueuedConnection); | ||
} | ||
|
||
void AsyncTask::Impl::maySend() { | ||
if (!queued.test_and_set()) { | ||
emit send(); | ||
|
||
runLoop->notifyPendingEvents(); | ||
} | ||
} | ||
|
||
void AsyncTask::Impl::runTask() { | ||
queued.clear(); | ||
task(); | ||
} | ||
|
||
AsyncTask::AsyncTask(std::function<void()>&& fn) | ||
: impl(std::make_unique<Impl>(std::move(fn))) { | ||
} | ||
|
||
AsyncTask::~AsyncTask() { | ||
} | ||
|
||
void AsyncTask::send() { | ||
impl->maySend(); | ||
} | ||
|
||
void AsyncTask::unref() { | ||
} | ||
|
||
} | ||
} |
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,38 @@ | ||
#pragma once | ||
|
||
#include <mbgl/util/async_task.hpp> | ||
|
||
#include <QObject> | ||
|
||
#include <functional> | ||
#include <atomic> | ||
|
||
namespace mbgl { | ||
namespace util { | ||
|
||
class RunLoop; | ||
|
||
class AsyncTask::Impl : public QObject { | ||
Q_OBJECT | ||
|
||
public: | ||
Impl(std::function<void()>&& fn); | ||
|
||
void maySend(); | ||
|
||
public slots: | ||
void runTask(); | ||
|
||
signals: | ||
void send(); | ||
|
||
private: | ||
RunLoop* runLoop; | ||
|
||
std::function<void()> task; | ||
std::atomic_flag queued = ATOMIC_FLAG_INIT; | ||
}; | ||
|
||
|
||
} | ||
} |
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
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,144 @@ | ||
#include "run_loop_impl.hpp" | ||
|
||
#include <mbgl/util/thread_local.hpp> | ||
|
||
#include <QCoreApplication> | ||
|
||
#include <cassert> | ||
#include <functional> | ||
#include <utility> | ||
|
||
namespace { | ||
|
||
using namespace mbgl::util; | ||
static ThreadLocal<RunLoop>& current = *new ThreadLocal<RunLoop>; | ||
|
||
} | ||
|
||
namespace mbgl { | ||
namespace util { | ||
|
||
void RunLoop::Impl::onReadEvent(int fd) { | ||
readPoll[fd].second(fd, Event::Read); | ||
} | ||
|
||
void RunLoop::Impl::onWriteEvent(int fd) { | ||
writePoll[fd].second(fd, Event::Write); | ||
} | ||
|
||
RunLoop* RunLoop::Get() { | ||
return current.get(); | ||
} | ||
|
||
RunLoop::RunLoop(Type type) : impl(std::make_unique<Impl>()) { | ||
// XXX: We should probably throw an runtime exception | ||
// here instead of creating a QCoreApplication which is | ||
// way too intrusive. This is a hack mostly for the unit | ||
// tests to work, as you always need a QCoreApplication | ||
// prior to run a Qt app. | ||
if (!QCoreApplication::instance()) { | ||
static const char* argv[] = { "mbgl" }; | ||
static int argc = 1; | ||
|
||
// We need to keep this around because it would otherwise crash | ||
// on Qt4 due to a bug on QNetworkConfigurationManager when recreating | ||
// a QCoreApplication: https://bugreports.qt.io/browse/QTBUG-36897 | ||
static auto* app = new QCoreApplication(argc, const_cast<char**>(argv)); | ||
Q_UNUSED(app); | ||
} | ||
|
||
switch (type) { | ||
case Type::New: | ||
impl->loop = std::make_unique<QEventLoop>(); | ||
break; | ||
case Type::Default: | ||
// Use QCoreApplication::instance(). | ||
break; | ||
} | ||
|
||
impl->type = type; | ||
|
||
current.set(this); | ||
impl->async = std::make_unique<AsyncTask>(std::bind(&RunLoop::process, this)); | ||
} | ||
|
||
RunLoop::~RunLoop() { | ||
MBGL_VERIFY_THREAD(tid); | ||
|
||
current.set(nullptr); | ||
} | ||
|
||
LOOP_HANDLE RunLoop::getLoopHandle() { | ||
throw std::runtime_error("Should not be used in Qt."); | ||
|
||
return nullptr; | ||
} | ||
|
||
void RunLoop::push(std::shared_ptr<WorkTask> task) { | ||
withMutex([&] { queue.push(task); }); | ||
impl->async->send(); | ||
} | ||
|
||
void RunLoop::run() { | ||
MBGL_VERIFY_THREAD(tid); | ||
|
||
if (impl->type == Type::Default) { | ||
QCoreApplication::instance()->exec(); | ||
} else { | ||
impl->loop->exec(); | ||
} | ||
} | ||
|
||
void RunLoop::stop() { | ||
if (impl->type == Type::Default) { | ||
invoke([&] { QCoreApplication::instance()->exit(); }); | ||
} else { | ||
invoke([&] { impl->loop->exit(); }); | ||
} | ||
} | ||
|
||
|
||
void RunLoop::runOnce() { | ||
MBGL_VERIFY_THREAD(tid); | ||
|
||
if (impl->type == Type::Default) { | ||
QCoreApplication::instance()->processEvents(); | ||
} else { | ||
impl->loop->processEvents(); | ||
} | ||
} | ||
|
||
void RunLoop::addWatch(int fd, Event event, std::function<void(int, Event)>&& cb) { | ||
MBGL_VERIFY_THREAD(tid); | ||
|
||
assert(!RunLoop::Get()->hasForeignRunLoopIntegration()); | ||
|
||
if (event == Event::Read || event == Event::ReadWrite) { | ||
auto notifier = std::make_unique<QSocketNotifier>(fd, QSocketNotifier::Read); | ||
QObject::connect(notifier.get(), SIGNAL(activated(int)), impl.get(), SLOT(onReadEvent(int))); | ||
impl->readPoll[fd] = WatchPair(std::move(notifier), std::move(cb)); | ||
} | ||
|
||
if (event == Event::Write || event == Event::ReadWrite) { | ||
auto notifier = std::make_unique<QSocketNotifier>(fd, QSocketNotifier::Write); | ||
QObject::connect(notifier.get(), SIGNAL(activated(int)), impl.get(), SLOT(onWriteEvent(int))); | ||
impl->writePoll[fd] = WatchPair(std::move(notifier), std::move(cb)); | ||
} | ||
} | ||
|
||
void RunLoop::removeWatch(int fd) { | ||
MBGL_VERIFY_THREAD(tid); | ||
|
||
auto writePollIter = impl->writePoll.find(fd); | ||
if (writePollIter != impl->writePoll.end()) { | ||
impl->writePoll.erase(writePollIter); | ||
} | ||
|
||
auto readPollIter = impl->readPoll.find(fd); | ||
if (readPollIter != impl->readPoll.end()) { | ||
impl->readPoll.erase(readPollIter); | ||
} | ||
} | ||
|
||
} | ||
} |
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,38 @@ | ||
#pragma once | ||
|
||
#include <mbgl/util/async_task.hpp> | ||
#include <mbgl/util/run_loop.hpp> | ||
|
||
#include <QEventLoop> | ||
#include <QObject> | ||
#include <QSocketNotifier> | ||
|
||
#include <unordered_map> | ||
|
||
namespace mbgl { | ||
namespace util { | ||
|
||
using WatchCallback = std::function<void(int, RunLoop::Event)>; | ||
using WatchPair = std::pair<std::unique_ptr<QSocketNotifier>, WatchCallback>; | ||
|
||
class RunLoop::Impl : public QObject { | ||
Q_OBJECT | ||
|
||
public: | ||
Impl() = default; | ||
|
||
RunLoop::Type type; | ||
|
||
std::unique_ptr<QEventLoop> loop; | ||
std::unique_ptr<AsyncTask> async; | ||
|
||
std::unordered_map<int, WatchPair> readPoll; | ||
std::unordered_map<int, WatchPair> writePoll; | ||
|
||
public slots: | ||
void onReadEvent(int fd); | ||
void onWriteEvent(int fd); | ||
}; | ||
|
||
} | ||
} |
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,62 @@ | ||
#include <mbgl/util/timer.hpp> | ||
|
||
#include <mbgl/util/run_loop.hpp> | ||
|
||
#include <cassert> | ||
#include <memory> | ||
|
||
#include "timer_impl.hpp" | ||
|
||
namespace mbgl { | ||
namespace util { | ||
|
||
Timer::Impl::Impl() { | ||
#if QT_VERSION >= 0x050000 | ||
timer.setTimerType(Qt::PreciseTimer); | ||
#endif | ||
connect(&timer, SIGNAL(timeout()), this, SLOT(timerFired())); | ||
} | ||
|
||
void Timer::Impl::start(uint64_t timeout, uint64_t repeat_, std::function<void ()>&& cb) { | ||
repeat = repeat_; | ||
callback = std::move(cb); | ||
|
||
timer.setSingleShot(true); | ||
timer.start(timeout); | ||
} | ||
|
||
void Timer::Impl::stop() { | ||
timer.stop(); | ||
} | ||
|
||
void Timer::Impl::timerFired() { | ||
if (repeat) { | ||
timer.setSingleShot(false); | ||
timer.start(repeat); | ||
} | ||
|
||
callback(); | ||
} | ||
|
||
Timer::Timer() | ||
: impl(std::make_unique<Impl>()) { | ||
assert(!RunLoop::Get()->hasForeignRunLoopIntegration()); | ||
} | ||
|
||
Timer::~Timer() = default; | ||
|
||
void Timer::start(Duration timeout, Duration repeat, std::function<void()>&& cb) { | ||
impl->start(std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count(), | ||
std::chrono::duration_cast<std::chrono::milliseconds>(repeat).count(), | ||
std::move(cb)); | ||
} | ||
|
||
void Timer::stop() { | ||
impl->stop(); | ||
} | ||
|
||
void Timer::unref() { | ||
} | ||
|
||
} | ||
} |
Oops, something went wrong.