diff options
| author | xiphon <xiphon@protonmail.com> | 2019-06-20 20:28:59 +0000 |
|---|---|---|
| committer | xiphon <xiphon@protonmail.com> | 2019-06-24 20:09:50 +0000 |
| commit | be7810c5a877898d792cc7d4406dc05c3280b38f (patch) | |
| tree | 50810c88e2cfd8936c91a806b68f188e41f7e9dc /src/qt | |
| parent | c7956f76ead2c82a320c830a63ffa97d55a10bb6 (diff) | |
| download | monzero-gui-be7810c5a877898d792cc7d4406dc05c3280b38f.tar.gz monzero-gui-be7810c5a877898d792cc7d4406dc05c3280b38f.tar.xz monzero-gui-be7810c5a877898d792cc7d4406dc05c3280b38f.zip | |
qt: implement FutureScheduler, always await async code to complete
Diffstat (limited to 'src/qt')
| -rw-r--r-- | src/qt/FutureScheduler.cpp | 89 | ||||
| -rw-r--r-- | src/qt/FutureScheduler.h | 79 |
2 files changed, 168 insertions, 0 deletions
diff --git a/src/qt/FutureScheduler.cpp b/src/qt/FutureScheduler.cpp new file mode 100644 index 00000000..bdd47829 --- /dev/null +++ b/src/qt/FutureScheduler.cpp @@ -0,0 +1,89 @@ +#include "FutureScheduler.h" + +FutureScheduler::FutureScheduler(QObject *parent) + : QObject(parent), Alive(0), Stopping(false) +{ +} + +FutureScheduler::~FutureScheduler() +{ + shutdownWaitForFinished(); +} + +void FutureScheduler::shutdownWaitForFinished() noexcept +{ + QMutexLocker locker(&Mutex); + + Stopping = true; + while (Alive > 0) + { + Condition.wait(&Mutex); + } +} + +QPair<bool, QFuture<void>> FutureScheduler::run(std::function<void()> function) noexcept +{ + return execute<void>([this, function](QFutureWatcher<void> *) { + return QtConcurrent::run([this, function] { + try + { + function(); + } + catch (const std::exception &exception) + { + qWarning() << "Exception thrown from async function: " << exception.what(); + } + done(); + }); + }); +} + +QPair<bool, QFuture<QJSValueList>> FutureScheduler::run(std::function<QJSValueList() noexcept> function, const QJSValue &callback) noexcept +{ + if (!callback.isCallable()) + { + throw std::runtime_error("js callback must be callable"); + } + + return execute<QJSValueList>([this, function, callback](QFutureWatcher<QJSValueList> *watcher) { + connect(watcher, &QFutureWatcher<QJSValueList>::finished, [watcher, callback] { + QJSValue(callback).call(watcher->future().result()); + }); + return QtConcurrent::run([this, function] { + QJSValueList result; + try + { + result = function(); + } + catch (const std::exception &exception) + { + qWarning() << "Exception thrown from async function: " << exception.what(); + } + done(); + return result; + }); + }); +} + +bool FutureScheduler::add() noexcept +{ + QMutexLocker locker(&Mutex); + + if (Stopping) + { + return false; + } + + ++Alive; + return true; +} + +void FutureScheduler::done() noexcept +{ + { + QMutexLocker locker(&Mutex); + --Alive; + } + + Condition.wakeAll(); +} diff --git a/src/qt/FutureScheduler.h b/src/qt/FutureScheduler.h new file mode 100644 index 00000000..28c2dfdf --- /dev/null +++ b/src/qt/FutureScheduler.h @@ -0,0 +1,79 @@ +#ifndef FUTURE_SCHEDULER_H +#define FUTURE_SCHEDULER_H + +#include <functional> + +#include <QtConcurrent/QtConcurrent> +#include <QFuture> +#include <QJSValue> +#include <QMutex> +#include <QMutexLocker> +#include <QPair> +#include <QWaitCondition> + +class FutureScheduler : public QObject +{ + Q_OBJECT + +public: + FutureScheduler(QObject *parent); + ~FutureScheduler(); + + void shutdownWaitForFinished() noexcept; + + QPair<bool, QFuture<void>> run(std::function<void()> function) noexcept; + QPair<bool, QFuture<QJSValueList>> run(std::function<QJSValueList() noexcept> function, const QJSValue &callback) noexcept; + +private: + bool add() noexcept; + void done() noexcept; + + template<typename T> + QFutureWatcher<T> *newWatcher() + { + QFutureWatcher<T> *watcher = new QFutureWatcher<T>(); + QThread *schedulerThread = this->thread(); + if (watcher->thread() != schedulerThread) + { + watcher->moveToThread(schedulerThread); + } + watcher->setParent(this); + + return watcher; + } + + template<typename T> + QPair<bool, QFuture<T>> execute(std::function<QFuture<T>(QFutureWatcher<T> *)> makeFuture) noexcept + { + if (add()) + { + try + { + auto *watcher = newWatcher<T>(); + watcher->setFuture(makeFuture(watcher)); + connect(watcher, &QFutureWatcher<T>::finished, [this, watcher] { + watcher->deleteLater(); + }); + return qMakePair(true, watcher->future()); + } + catch (const std::exception &exception) + { + qCritical() << "Failed to schedule async function: " << exception.what(); + done(); + } + } + + return qMakePair(false, QFuture<T>()); + } + + QFutureWatcher<void> schedule(std::function<void()> function); + QFutureWatcher<QJSValueList> schedule(std::function<QJSValueList() noexcept> function, const QJSValue &callback); + +private: + size_t Alive; + QWaitCondition Condition; + QMutex Mutex; + bool Stopping; +}; + +#endif // FUTURE_SCHEDULER_H |
