Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[Qt] Implement the AsyncTask, Timer and RunLoop using Qt
Browse files Browse the repository at this point in the history
Now Mapbox GL will handle events using Qt as backend
instead of libuv.
  • Loading branch information
tmpsantos committed Dec 16, 2015
1 parent 9c2da79 commit f5cb832
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 3 deletions.
6 changes: 6 additions & 0 deletions gyp/platform-qt.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@
'../platform/default/log_stderr.cpp',
'../platform/default/string_stdlib.cpp',
'../platform/default/thread.cpp',
'../platform/qt/async_task.cpp',
'../platform/qt/async_task_impl.hpp',
'../platform/qt/image.cpp',
'../platform/qt/qfilesource_p.cpp',
'../platform/qt/qfilesource_p.hpp',
'../platform/qt/qmapboxgl.cpp',
'../platform/qt/qmapboxgl_p.hpp',
'../platform/qt/qsqlitecache_p.cpp',
'../platform/qt/qsqlitecache_p.hpp',
'../platform/qt/run_loop.cpp',
'../platform/qt/run_loop_impl.hpp',
'../platform/qt/timer.cpp',
'../platform/qt/timer_impl.hpp',
],

'variables': {
Expand Down
44 changes: 44 additions & 0 deletions platform/qt/async_task.cpp
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() {
}

}
}
38 changes: 38 additions & 0 deletions platform/qt/async_task_impl.hpp
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;
};


}
}
4 changes: 2 additions & 2 deletions platform/qt/qfilesource_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void QFileSourcePrivate::setCacheDatabase(const QString& path)
}
}

mbgl::Request *QFileSourcePrivate::request(const mbgl::Resource &resource, uv_loop_t *loop, Callback cb)
mbgl::Request *QFileSourcePrivate::request(const mbgl::Resource &resource, Callback cb)
{
// WARNING: Must be thread-safe.

Expand All @@ -59,7 +59,7 @@ mbgl::Request *QFileSourcePrivate::request(const mbgl::Resource &resource, uv_lo
normalizedUrl = resource.url;
}

mbgl::Request* req = new mbgl::Request({ resource.kind, normalizedUrl }, loop, std::move(cb));
mbgl::Request* req = new mbgl::Request({ resource.kind, normalizedUrl }, std::move(cb));
emit urlRequested(req);

return req;
Expand Down
2 changes: 1 addition & 1 deletion platform/qt/qfilesource_p.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class QFileSourcePrivate : public QObject, public mbgl::FileSource
void setCacheDatabase(const QString& path);

// FileSource implementation.
mbgl::Request *request(const mbgl::Resource &, uv_loop_t *, Callback) override;
mbgl::Request *request(const mbgl::Resource &, Callback) override;
void cancel(mbgl::Request*) override;

signals:
Expand Down
144 changes: 144 additions & 0 deletions platform/qt/run_loop.cpp
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);
}
}

}
}
38 changes: 38 additions & 0 deletions platform/qt/run_loop_impl.hpp
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);
};

}
}
62 changes: 62 additions & 0 deletions platform/qt/timer.cpp
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() {
}

}
}
Loading

0 comments on commit f5cb832

Please sign in to comment.