Changeset View
Changeset View
Standalone View
Standalone View
src/qt-promise-handler.hpp
| /* | /* | ||||
| * This file is part of kazv. | * This file is part of kazv. | ||||
| * SPDX-FileCopyrightText: 2020-2021 Tusooa Zhu <tusooa@kazv.moe> | * SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe> | ||||
| * SPDX-License-Identifier: AGPL-3.0-or-later | * SPDX-License-Identifier: AGPL-3.0-or-later | ||||
| */ | */ | ||||
| #pragma once | #pragma once | ||||
| #include <libkazv-config.hpp> | #include <libkazv-config.hpp> | ||||
| #include <future> | #include <future> | ||||
| #include <QObject> | #include <QObject> | ||||
| #include <QMetaObject> | #include <QMetaObject> | ||||
| #include <QPointer> | #include <QPointer> | ||||
| #include <QTimer> | #include <QTimer> | ||||
| #include <promise-interface.hpp> | #include <promise-interface.hpp> | ||||
| #include "kazv-log.hpp" | |||||
| template<class T> | template<class T> | ||||
| class QtPromise; | class QtPromise; | ||||
| namespace QtPromiseDetail | namespace QtPromiseDetail | ||||
| { | { | ||||
| struct QtPromiseHelper | struct QtPromiseHelper | ||||
| { | { | ||||
| template<class T> | template<class T> | ||||
| Show All 10 Lines | namespace QtPromiseDetail | ||||
| template<class Func> | template<class Func> | ||||
| void post(QObject *obj, Func &&func) | void post(QObject *obj, Func &&func) | ||||
| { | { | ||||
| QMetaObject::invokeMethod(obj, std::forward<Func>(func), Qt::QueuedConnection); | QMetaObject::invokeMethod(obj, std::forward<Func>(func), Qt::QueuedConnection); | ||||
| } | } | ||||
| } | } | ||||
| class QtPromiseSignalTrigger : public QObject | |||||
| { | |||||
| Q_OBJECT | |||||
| public: | |||||
| QtPromiseSignalTrigger(QObject *parent = 0); | |||||
| ~QtPromiseSignalTrigger(); | |||||
| Q_SIGNALS: | |||||
| void finished(); | |||||
| }; | |||||
| template<class T> | template<class T> | ||||
| class QtPromise : public Kazv::AbstractPromise<QtPromiseDetail::QtPromiseHelper::PromiseType, T> | class QtPromise : public Kazv::AbstractPromise<QtPromiseDetail::QtPromiseHelper::PromiseType, T> | ||||
| { | { | ||||
| using BaseT = Kazv::AbstractPromise<QtPromiseDetail::QtPromiseHelper::PromiseType, T>; | using BaseT = Kazv::AbstractPromise<QtPromiseDetail::QtPromiseHelper::PromiseType, T>; | ||||
| template<class FuncT, class PromiseT, class ResolveT> | template<class FuncT, class PromiseT, class ResolveT> | ||||
| struct WaitHelper | struct WaitHelper | ||||
| { | { | ||||
| void wait() const { | void resolveOrWait() | ||||
| if (p.ready()) { | { | ||||
| auto res = func(p.get()); | auto res = func(p.get()); | ||||
| using ResT = decltype(res); | using ResT = decltype(res); | ||||
| if constexpr (Kazv::isPromise<ResT>) { | if constexpr (Kazv::isPromise<ResT>) { | ||||
| QtPromiseDetail::post( | res.then([resolve=resolve](auto val) { | ||||
| executor, | resolve(std::move(val)); | ||||
| [w=WaitHelper<QtPromiseDetail::IdentityFunc, ResT, ResolveT>{ | return std::decay_t<decltype(val)>(); // we don't care about this value | ||||
| executor, | |||||
| res, | |||||
| QtPromiseDetail::IdentityFunc{}, | |||||
| resolve, | |||||
| }]() mutable { | |||||
| w.wait(); | |||||
| }); | }); | ||||
| } else { | } else { | ||||
| resolve(res); | resolve(res); | ||||
| } | } | ||||
| } else { | } | ||||
| void wait() | |||||
| { | |||||
| if (p.ready()) { | |||||
| QtPromiseDetail::post( | QtPromiseDetail::post( | ||||
| executor, | executor, | ||||
| [*this]() mutable { | [*this]() mutable { resolveOrWait(); } | ||||
| wait(); | ); | ||||
| } else { | |||||
| QObject::connect( | |||||
| p.m_signalTrigger, &QtPromiseSignalTrigger::finished, | |||||
| executor, [*this]() mutable { | |||||
| resolveOrWait(); | |||||
| } | } | ||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| QPointer<QObject> executor; | QPointer<QObject> executor; | ||||
| PromiseT p; | PromiseT p; | ||||
| FuncT func; | FuncT func; | ||||
| ResolveT resolve; | ResolveT resolve; | ||||
| }; | }; | ||||
| struct ResolveHelper | struct ResolveHelper | ||||
| { | { | ||||
| template<class ValT> | template<class ValT> | ||||
| void operator()(ValT val) const { | void operator()(ValT val) const | ||||
| { | |||||
| using ResT = std::decay_t<decltype(val)>; | using ResT = std::decay_t<decltype(val)>; | ||||
| if constexpr (Kazv::isPromise<ResT>) { | if constexpr (Kazv::isPromise<ResT>) { | ||||
| auto w = WaitHelper<QtPromiseDetail::IdentityFunc, ResT, ResolveHelper>{ | QtPromiseDetail::post( | ||||
| executor, | executor, | ||||
| val, | [qtPromise=std::move(val), *this]() mutable { | ||||
| QtPromiseDetail::IdentityFunc{}, | if (qtPromise.ready()) { | ||||
| *this | setValue(qtPromise.get()); | ||||
| }; | |||||
| w.wait(); | |||||
| } else { | } else { | ||||
| p->set_value(std::move(val)); | qtPromise.then([*this](auto val) mutable { | ||||
| setValue(std::move(val)); | |||||
| return std::decay_t<decltype(val)>(); // we don't care about this value | |||||
| }); | |||||
| } | } | ||||
| }); | |||||
| } else { | |||||
| QtPromiseDetail::post( | |||||
| executor, | |||||
| [*this, val=std::move(val)]() mutable { | |||||
| setValue(std::move(val)); | |||||
| }); | |||||
| } | } | ||||
| } | |||||
| void setValue(T resolvedValue) const | |||||
| { | |||||
| p->set_value(std::move(resolvedValue)); | |||||
| if (signalTrigger) { | |||||
| Q_EMIT signalTrigger->finished(); | |||||
| signalTrigger->deleteLater(); | |||||
| } | |||||
| } | |||||
| QPointer<QObject> executor; | QPointer<QObject> executor; | ||||
| QPointer<QtPromiseSignalTrigger> signalTrigger; | |||||
| std::shared_ptr<std::promise<T>> p; | std::shared_ptr<std::promise<T>> p; | ||||
| }; | }; | ||||
| public: | public: | ||||
| QtPromise(QObject *executor, T value) | QtPromise(QObject *executor, T value) | ||||
| : BaseT(this) | : BaseT(this) | ||||
| , m_executor(executor) { | , m_executor(executor) | ||||
| , m_signalTrigger() | |||||
| { | |||||
| std::promise<T> p; | std::promise<T> p; | ||||
| m_val = p.get_future().share(); | m_val = p.get_future().share(); | ||||
| p.set_value(std::move(value)); | p.set_value(std::move(value)); | ||||
| } | } | ||||
| template<class Func> | template<class Func> | ||||
| QtPromise(QObject *executor, Func &&callback) | QtPromise(QObject *executor, Func &&callback) | ||||
| : BaseT(this) | : BaseT(this) | ||||
| , m_executor(executor) { | , m_executor(executor) | ||||
| , m_signalTrigger(new QtPromiseSignalTrigger()) | |||||
| { | |||||
| auto p = std::make_shared<std::promise<T>>(); | auto p = std::make_shared<std::promise<T>>(); | ||||
| m_val = p->get_future().share(); | m_val = p->get_future().share(); | ||||
| auto resolve = ResolveHelper{m_executor, p}; | |||||
| m_signalTrigger->moveToThread(m_executor->thread()); | |||||
| auto resolve = ResolveHelper{m_executor, m_signalTrigger, p}; | |||||
| QtPromiseDetail::post( | QtPromiseDetail::post( | ||||
| m_executor, | m_executor, | ||||
| [=, callback=std::forward<Func>(callback), | [=, callback=std::forward<Func>(callback), | ||||
| resolve=std::move(resolve)]() { | resolve=std::move(resolve)]() { | ||||
| callback(resolve); | callback(resolve); | ||||
| }); | }); | ||||
| } | } | ||||
| Show All 21 Lines | bool ready() const { | ||||
| return m_val.wait_for(std::chrono::seconds(0)) == std::future_status::ready; | return m_val.wait_for(std::chrono::seconds(0)) == std::future_status::ready; | ||||
| } | } | ||||
| T get() const { | T get() const { | ||||
| return m_val.get(); | return m_val.get(); | ||||
| } | } | ||||
| private: | private: | ||||
| QPointer<QObject> m_executor; | QPointer<QObject> m_executor; | ||||
| QPointer<QtPromiseSignalTrigger> m_signalTrigger; | |||||
| std::shared_future<T> m_val; | std::shared_future<T> m_val; | ||||
| }; | }; | ||||
| class QtPromiseHandler : public Kazv::PromiseInterface<QtPromiseHandler, | class QtPromiseHandler : public Kazv::PromiseInterface<QtPromiseHandler, | ||||
| QtPromiseDetail::QtPromiseHelper::PromiseType> | QtPromiseDetail::QtPromiseHelper::PromiseType> | ||||
| { | { | ||||
| using BaseT = Kazv::PromiseInterface<QtPromiseHandler, | using BaseT = Kazv::PromiseInterface<QtPromiseHandler, | ||||
| QtPromiseDetail::QtPromiseHelper::PromiseType>; | QtPromiseDetail::QtPromiseHelper::PromiseType>; | ||||
| ▲ Show 20 Lines • Show All 42 Lines • Show Last 20 Lines | |||||