mirror of
https://github.com/simonbrunel/qtpromise.git
synced 2024-11-25 12:11:28 +08:00
C++11 optimizations and (basic) benchmark
Make continuation methods const (then/fail/finally) and ensure that the resolved promise value/error is copied only when required, same for user lambdas (dispatching result is now fully handled by the PromiseData).
This commit is contained in:
parent
a8ad3619b9
commit
9bbef41a50
@ -16,33 +16,9 @@ class QPromiseBase
|
|||||||
public:
|
public:
|
||||||
using Type = T;
|
using Type = T;
|
||||||
|
|
||||||
virtual ~QPromiseBase() { }
|
QPromiseBase(const QPromiseBase<T>& other): m_d(other.m_d) {}
|
||||||
|
QPromiseBase(const QPromise<T>& other): m_d(other.m_d) {}
|
||||||
bool isFulfilled() const { return m_d->resolved && !m_d->rejected; }
|
QPromiseBase(QPromiseBase<T>&& other) { swap(other); }
|
||||||
bool isRejected() const { return m_d->resolved && m_d->rejected; }
|
|
||||||
bool isPending() const { return !m_d->resolved; }
|
|
||||||
|
|
||||||
template <typename TFulfilled, typename TRejected = std::nullptr_t>
|
|
||||||
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
|
||||||
then(TFulfilled fulfilled, TRejected rejected = nullptr);
|
|
||||||
|
|
||||||
template <typename TRejected>
|
|
||||||
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
|
||||||
fail(TRejected rejected);
|
|
||||||
|
|
||||||
inline QPromise<T> wait() const;
|
|
||||||
|
|
||||||
public: // STATIC
|
|
||||||
template <typename E>
|
|
||||||
inline static QPromise<T> reject(const E& error);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
friend class QPromiseResolve<T>;
|
|
||||||
friend class QPromiseReject<T>;
|
|
||||||
|
|
||||||
QExplicitlySharedDataPointer<QtPromisePrivate::PromiseData<T> > m_d;
|
|
||||||
|
|
||||||
inline QPromiseBase();
|
|
||||||
|
|
||||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type = 0>
|
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type = 0>
|
||||||
inline QPromiseBase(F resolver);
|
inline QPromiseBase(F resolver);
|
||||||
@ -50,9 +26,33 @@ protected:
|
|||||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
|
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
|
||||||
inline QPromiseBase(F resolver);
|
inline QPromiseBase(F resolver);
|
||||||
|
|
||||||
virtual void notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const = 0;
|
virtual ~QPromiseBase() { }
|
||||||
|
|
||||||
inline void dispatch();
|
bool isFulfilled() const { return m_d->isFulfilled(); }
|
||||||
|
bool isRejected() const { return m_d->isRejected(); }
|
||||||
|
bool isPending() const { return m_d->isPending(); }
|
||||||
|
|
||||||
|
template <typename TFulfilled, typename TRejected = std::nullptr_t>
|
||||||
|
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||||
|
then(const TFulfilled& fulfilled, const TRejected& rejected = nullptr) const;
|
||||||
|
|
||||||
|
template <typename TRejected>
|
||||||
|
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
||||||
|
fail(TRejected&& rejected) const;
|
||||||
|
|
||||||
|
inline QPromise<T> wait() const;
|
||||||
|
|
||||||
|
void swap(QPromiseBase<T>& other) { qSwap(m_d, other.m_d); }
|
||||||
|
|
||||||
|
public: // STATIC
|
||||||
|
template <typename E>
|
||||||
|
inline static QPromise<T> reject(E&& error);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class QPromiseResolve<T>;
|
||||||
|
friend class QPromiseReject<T>;
|
||||||
|
|
||||||
|
QExplicitlySharedDataPointer<QtPromisePrivate::PromiseData<T> > m_d;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -60,22 +60,17 @@ class QPromise: public QPromiseBase<T>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template <typename F>
|
template <typename F>
|
||||||
QPromise(F resolver): QPromiseBase<T>(resolver) { }
|
QPromise(F&& resolver): QPromiseBase<T>(std::forward<F>(resolver)) { }
|
||||||
|
|
||||||
template <typename THandler>
|
template <typename THandler>
|
||||||
inline QPromise<T> finally(THandler handler);
|
inline QPromise<T> finally(THandler handler) const;
|
||||||
|
|
||||||
public: // STATIC
|
public: // STATIC
|
||||||
inline static QPromise<QVector<T> > all(const QVector<QPromise<T> >& promises);
|
inline static QPromise<QVector<T> > all(const QVector<QPromise<T> >& promises);
|
||||||
inline static QPromise<T> resolve(const T& value);
|
inline static QPromise<T> resolve(T&& value);
|
||||||
|
|
||||||
protected:
|
|
||||||
inline void notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const Q_DECL_OVERRIDE;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class QPromiseBase<T>;
|
friend class QPromiseBase<T>;
|
||||||
|
|
||||||
QPromise(const QPromiseBase<T>& p) : QPromiseBase<T>(p) { }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@ -83,22 +78,17 @@ class QPromise<void>: public QPromiseBase<void>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
template <typename F>
|
template <typename F>
|
||||||
QPromise(F resolver): QPromiseBase<void>(resolver) { }
|
QPromise(F&& resolver): QPromiseBase<void>(std::forward<F>(resolver)) { }
|
||||||
|
|
||||||
template <typename THandler>
|
template <typename THandler>
|
||||||
inline QPromise<void> finally(THandler handler);
|
inline QPromise<void> finally(THandler handler) const;
|
||||||
|
|
||||||
public: // STATIC
|
public: // STATIC
|
||||||
inline static QPromise<void> all(const QVector<QPromise<void> >& promises);
|
inline static QPromise<void> all(const QVector<QPromise<void> >& promises);
|
||||||
inline static QPromise<void> resolve();
|
inline static QPromise<void> resolve();
|
||||||
|
|
||||||
protected:
|
|
||||||
inline void notify(const typename QtPromisePrivate::PromiseData<void>::HandlerList& handlers) const Q_DECL_OVERRIDE;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class QPromiseBase<void>;
|
friend class QPromiseBase<void>;
|
||||||
|
|
||||||
QPromise(const QPromiseBase<void>& p) : QPromiseBase<void>(p) { }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QtPromise
|
} // namespace QtPromise
|
||||||
|
@ -4,141 +4,87 @@
|
|||||||
|
|
||||||
namespace QtPromise {
|
namespace QtPromise {
|
||||||
|
|
||||||
template <class T = void>
|
template <class T>
|
||||||
class QPromiseResolve
|
class QPromiseResolve
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QPromiseResolve(const QPromise<T>& p)
|
QPromiseResolve(QPromise<T> p)
|
||||||
: m_promise(p)
|
: m_promise(new QPromise<T>(std::move(p)))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void operator()(const T& value) const
|
void operator()(const T& value) const {
|
||||||
{
|
resolve(value);
|
||||||
auto p = m_promise;
|
|
||||||
if (p.isPending()) {
|
|
||||||
p.m_d->rejected = false;
|
|
||||||
p.m_d->resolved = true;
|
|
||||||
p.m_d->value = value;
|
|
||||||
p.dispatch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void operator()(T&& value) const
|
||||||
|
{
|
||||||
|
resolve(std::move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPromise<T> m_promise;
|
QSharedPointer<QPromise<T> > m_promise;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
void resolve(U&& value) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_promise.isNull());
|
||||||
|
if (m_promise->isPending()) {
|
||||||
|
m_promise->m_d->resolve(std::forward<U>(value));
|
||||||
|
m_promise->m_d->dispatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class QPromiseResolve<void>
|
class QPromiseResolve<void>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QPromiseResolve(const QPromise<void>& p)
|
QPromiseResolve(QPromise<void> p)
|
||||||
: m_promise(p)
|
: m_promise(new QPromise<void>(std::move(p)))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void operator()() const
|
void operator()() const
|
||||||
{
|
{
|
||||||
auto p = m_promise;
|
Q_ASSERT(!m_promise.isNull());
|
||||||
if (p.isPending()) {
|
if (m_promise->isPending()) {
|
||||||
p.m_d->rejected = false;
|
m_promise->m_d->resolve();
|
||||||
p.m_d->resolved = true;
|
m_promise->m_d->dispatch();
|
||||||
p.dispatch();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPromise<void> m_promise;
|
QSharedPointer<QPromise<void> > m_promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T = void>
|
template <class T>
|
||||||
class QPromiseReject
|
class QPromiseReject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QPromiseReject()
|
QPromiseReject(QPromise<T> p)
|
||||||
|
: m_promise(new QPromise<T>(std::move(p)))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
QPromiseReject(const QPromise<T>& p)
|
template <typename E>
|
||||||
: m_promise(p)
|
void operator()(E&& error) const
|
||||||
{ }
|
|
||||||
|
|
||||||
void operator()(const QPromiseError& error) const
|
|
||||||
{
|
{
|
||||||
auto p = m_promise;
|
Q_ASSERT(!m_promise.isNull());
|
||||||
if (p.isPending()) {
|
if (m_promise->isPending()) {
|
||||||
p.m_d->rejected = true;
|
m_promise->m_d->reject(std::forward<E>(error));
|
||||||
p.m_d->resolved = true;
|
m_promise->m_d->dispatch();
|
||||||
p.m_d->error = error;
|
|
||||||
p.dispatch();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPromise<T> m_promise;
|
QSharedPointer<QPromise<T> > m_promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
template <typename TFulfilled, typename TRejected>
|
|
||||||
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
|
||||||
QPromiseBase<T>::then(TFulfilled fulfilled, TRejected rejected)
|
|
||||||
{
|
|
||||||
using namespace QtPromisePrivate;
|
|
||||||
using PromiseType = typename PromiseHandler<T, TFulfilled>::Promise;
|
|
||||||
|
|
||||||
PromiseType next([=](
|
|
||||||
const QPromiseResolve<typename PromiseType::Type>& resolve,
|
|
||||||
const QPromiseReject<typename PromiseType::Type>& reject) {
|
|
||||||
m_d->handlers << PromiseHandler<T, TFulfilled>::create(fulfilled, resolve, reject);
|
|
||||||
m_d->catchers << PromiseCatcher<T, TRejected>::create(rejected, resolve, reject);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (m_d->resolved) {
|
|
||||||
dispatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
template <typename TRejected>
|
|
||||||
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
|
||||||
QPromiseBase<T>::fail(TRejected rejected)
|
|
||||||
{
|
|
||||||
return then(nullptr, rejected);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline QPromise<T> QPromiseBase<T>::wait() const
|
|
||||||
{
|
|
||||||
// @TODO wait timeout + global timeout
|
|
||||||
while (!m_d->resolved) {
|
|
||||||
QCoreApplication::processEvents(QEventLoop::AllEvents);
|
|
||||||
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
template <typename E>
|
|
||||||
inline QPromise<T> QPromiseBase<T>::reject(const E& error)
|
|
||||||
{
|
|
||||||
return QPromise<T>([=](const QPromiseResolve<T>&) {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline QPromiseBase<T>::QPromiseBase()
|
|
||||||
: m_d(new QtPromisePrivate::PromiseData<T>())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type>
|
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type>
|
||||||
inline QPromiseBase<T>::QPromiseBase(F resolver)
|
inline QPromiseBase<T>::QPromiseBase(F resolver)
|
||||||
: m_d(new QtPromisePrivate::PromiseData<T>())
|
: m_d(new QtPromisePrivate::PromiseData<T>())
|
||||||
{
|
{
|
||||||
auto resolve = QPromiseResolve<T>(*this);
|
QPromiseResolve<T> resolve(*this);
|
||||||
auto reject = QPromiseReject<T>(*this);
|
QPromiseReject<T> reject(*this);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resolver(resolve);
|
resolver(resolve);
|
||||||
@ -152,8 +98,8 @@ template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count
|
|||||||
inline QPromiseBase<T>::QPromiseBase(F resolver)
|
inline QPromiseBase<T>::QPromiseBase(F resolver)
|
||||||
: m_d(new QtPromisePrivate::PromiseData<T>())
|
: m_d(new QtPromisePrivate::PromiseData<T>())
|
||||||
{
|
{
|
||||||
auto resolve = QPromiseResolve<T>(*this);
|
QPromiseResolve<T> resolve(*this);
|
||||||
auto reject = QPromiseReject<T>(*this);
|
QPromiseReject<T> reject(*this);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
resolver(resolve, reject);
|
resolver(resolve, reject);
|
||||||
@ -163,33 +109,59 @@ inline QPromiseBase<T>::QPromiseBase(F resolver)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline void QPromiseBase<T>::dispatch()
|
template <typename TFulfilled, typename TRejected>
|
||||||
|
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||||
|
QPromiseBase<T>::then(const TFulfilled& fulfilled, const TRejected& rejected) const
|
||||||
{
|
{
|
||||||
using namespace QtPromisePrivate;
|
using namespace QtPromisePrivate;
|
||||||
|
using PromiseType = typename PromiseHandler<T, TFulfilled>::Promise;
|
||||||
|
|
||||||
Q_ASSERT(m_d->resolved);
|
PromiseType next([&](
|
||||||
|
const QPromiseResolve<typename PromiseType::Type>& resolve,
|
||||||
typename PromiseData<T>::HandlerList handlers;
|
const QPromiseReject<typename PromiseType::Type>& reject) {
|
||||||
typename PromiseData<T>::CatcherList catchers;
|
m_d->addHandler(PromiseHandler<T, TFulfilled>::create(fulfilled, resolve, reject));
|
||||||
|
m_d->addCatcher(PromiseCatcher<T, TRejected>::create(rejected, resolve, reject));
|
||||||
handlers.swap(m_d->handlers);
|
|
||||||
catchers.swap(m_d->catchers);
|
|
||||||
|
|
||||||
if (m_d->rejected) {
|
|
||||||
const QPromiseError error = m_d->error;
|
|
||||||
qtpromise_defer([=]() {
|
|
||||||
for (auto catcher: catchers) {
|
|
||||||
catcher(error);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
notify(handlers);
|
if (!m_d->isPending()) {
|
||||||
|
m_d->dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
template <typename TRejected>
|
||||||
|
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
||||||
|
QPromiseBase<T>::fail(TRejected&& rejected) const
|
||||||
|
{
|
||||||
|
return then(nullptr, std::forward<TRejected>(rejected));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline QPromise<T> QPromiseBase<T>::wait() const
|
||||||
|
{
|
||||||
|
// @TODO wait timeout + global timeout
|
||||||
|
while (m_d->isPending()) {
|
||||||
|
QCoreApplication::processEvents(QEventLoop::AllEvents);
|
||||||
|
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
template <typename E>
|
||||||
|
inline QPromise<T> QPromiseBase<T>::reject(E&& error)
|
||||||
|
{
|
||||||
|
return QPromise<T>([&](const QPromiseResolve<T>&, const QPromiseReject<T>& reject) {
|
||||||
|
reject(std::forward<E>(error));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
template <typename THandler>
|
template <typename THandler>
|
||||||
inline QPromise<T> QPromise<T>::finally(THandler handler)
|
inline QPromise<T> QPromise<T>::finally(THandler handler) const
|
||||||
{
|
{
|
||||||
return this->then([=](const T& res) {
|
return this->then([=](const T& res) {
|
||||||
return QPromise<void>::resolve().then(handler).then([=](){
|
return QPromise<void>::resolve().then(handler).then([=](){
|
||||||
@ -220,7 +192,7 @@ inline QPromise<QVector<T> > QPromise<T>::all(const QVector<QPromise<T> >& promi
|
|||||||
QSharedPointer<QVector<T> > results(new QVector<T>(count));
|
QSharedPointer<QVector<T> > results(new QVector<T>(count));
|
||||||
|
|
||||||
for (int i=0; i<count; ++i) {
|
for (int i=0; i<count; ++i) {
|
||||||
QPromise<T>(promises[i]).then([=](const T& res) mutable {
|
promises[i].then([=](const T& res) mutable {
|
||||||
(*results)[i] = res;
|
(*results)[i] = res;
|
||||||
if (--(*remaining) == 0) {
|
if (--(*remaining) == 0) {
|
||||||
resolve(*results);
|
resolve(*results);
|
||||||
@ -236,26 +208,15 @@ inline QPromise<QVector<T> > QPromise<T>::all(const QVector<QPromise<T> >& promi
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline QPromise<T> QPromise<T>::resolve(const T& value)
|
inline QPromise<T> QPromise<T>::resolve(T&& value)
|
||||||
{
|
{
|
||||||
return QPromise<T>([=](const QPromiseResolve<T>& resolve) {
|
return QPromise<T>([&](const QPromiseResolve<T>& resolve) {
|
||||||
resolve(value);
|
resolve(std::forward<T>(value));
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline void QPromise<T>::notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const
|
|
||||||
{
|
|
||||||
const T value = this->m_d->value;
|
|
||||||
QtPromisePrivate::qtpromise_defer([handlers, value]() {
|
|
||||||
for (auto handler: handlers) {
|
|
||||||
handler(value);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename THandler>
|
template <typename THandler>
|
||||||
inline QPromise<void> QPromise<void>::finally(THandler handler)
|
inline QPromise<void> QPromise<void>::finally(THandler handler) const
|
||||||
{
|
{
|
||||||
return this->then([=]() {
|
return this->then([=]() {
|
||||||
return QPromise<void>::resolve().then(handler).then([](){});
|
return QPromise<void>::resolve().then(handler).then([](){});
|
||||||
@ -281,7 +242,7 @@ inline QPromise<void> QPromise<void>::all(const QVector<QPromise<void> >& promis
|
|||||||
QSharedPointer<int> remaining(new int(promises.size()));
|
QSharedPointer<int> remaining(new int(promises.size()));
|
||||||
|
|
||||||
for (const auto& promise: promises) {
|
for (const auto& promise: promises) {
|
||||||
QPromise<void>(promise).then([=]() {
|
promise.then([=]() {
|
||||||
if (--(*remaining) == 0) {
|
if (--(*remaining) == 0) {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
@ -302,13 +263,4 @@ inline QPromise<void> QPromise<void>::resolve()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void QPromise<void>::notify(const typename QtPromisePrivate::PromiseData<void>::HandlerList& handlers) const
|
|
||||||
{
|
|
||||||
QtPromisePrivate::qtpromise_defer([handlers]() {
|
|
||||||
for (const auto& handler: handlers) {
|
|
||||||
handler();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtPromise
|
} // namespace QtPromise
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
// Qt
|
// Qt
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QSharedPointer>
|
||||||
#include <QSharedData>
|
#include <QSharedData>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
@ -26,9 +27,9 @@ class QPromiseReject;
|
|||||||
namespace QtPromisePrivate {
|
namespace QtPromisePrivate {
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
inline void qtpromise_defer(F f)
|
inline void qtpromise_defer(F&& f)
|
||||||
{
|
{
|
||||||
QTimer::singleShot(0, f);
|
QTimer::singleShot(0, std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -46,11 +47,11 @@ template <typename T>
|
|||||||
struct PromiseFulfill
|
struct PromiseFulfill
|
||||||
{
|
{
|
||||||
static void call(
|
static void call(
|
||||||
const T& value,
|
T&& value,
|
||||||
const QtPromise::QPromiseResolve<T>& resolve,
|
const QtPromise::QPromiseResolve<T>& resolve,
|
||||||
const QtPromise::QPromiseReject<T>&)
|
const QtPromise::QPromiseReject<T>&)
|
||||||
{
|
{
|
||||||
resolve(value);
|
resolve(std::move(value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ template <typename T>
|
|||||||
struct PromiseFulfill<QtPromise::QPromise<T> >
|
struct PromiseFulfill<QtPromise::QPromise<T> >
|
||||||
{
|
{
|
||||||
static void call(
|
static void call(
|
||||||
QtPromise::QPromise<T> promise,
|
const QtPromise::QPromise<T>& promise,
|
||||||
const QtPromise::QPromiseResolve<T>& resolve,
|
const QtPromise::QPromiseResolve<T>& resolve,
|
||||||
const QtPromise::QPromiseReject<T>& reject)
|
const QtPromise::QPromiseReject<T>& reject)
|
||||||
{
|
{
|
||||||
@ -76,7 +77,10 @@ template <>
|
|||||||
struct PromiseFulfill<QtPromise::QPromise<void> >
|
struct PromiseFulfill<QtPromise::QPromise<void> >
|
||||||
{
|
{
|
||||||
template <typename TPromise, typename TResolve, typename TReject>
|
template <typename TPromise, typename TResolve, typename TReject>
|
||||||
static void call(TPromise promise, TResolve resolve, TReject reject)
|
static void call(
|
||||||
|
const TPromise& promise,
|
||||||
|
const TResolve& resolve,
|
||||||
|
const TReject& reject)
|
||||||
{
|
{
|
||||||
promise.then(
|
promise.then(
|
||||||
[=]() {
|
[=]() {
|
||||||
@ -95,11 +99,10 @@ struct PromiseDispatch
|
|||||||
using ResType = Unqualified<TRes>;
|
using ResType = Unqualified<TRes>;
|
||||||
|
|
||||||
template <typename THandler, typename TResolve, typename TReject>
|
template <typename THandler, typename TResolve, typename TReject>
|
||||||
static void call(const T& value, THandler handler, TResolve resolve, TReject reject)
|
static void call(const T& value, THandler handler, const TResolve& resolve, const TReject& reject)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const ResType res = handler(value);
|
PromiseFulfill<ResType>::call(handler(value), resolve, reject);
|
||||||
PromiseFulfill<ResType>::call(res, resolve, reject);
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
}
|
}
|
||||||
@ -112,7 +115,7 @@ struct PromiseDispatch<T, void>
|
|||||||
using Promise = QtPromise::QPromise<void>;
|
using Promise = QtPromise::QPromise<void>;
|
||||||
|
|
||||||
template <typename THandler, typename TResolve, typename TReject>
|
template <typename THandler, typename TResolve, typename TReject>
|
||||||
static void call(const T& value, THandler handler, TResolve resolve, TReject reject)
|
static void call(const T& value, THandler handler, const TResolve& resolve, const TReject& reject)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
handler(value);
|
handler(value);
|
||||||
@ -130,11 +133,10 @@ struct PromiseDispatch<void, TRes>
|
|||||||
using ResType = Unqualified<TRes>;
|
using ResType = Unqualified<TRes>;
|
||||||
|
|
||||||
template <typename THandler, typename TResolve, typename TReject>
|
template <typename THandler, typename TResolve, typename TReject>
|
||||||
static void call(THandler handler, TResolve resolve, TReject reject)
|
static void call(THandler handler, const TResolve& resolve, const TReject& reject)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const ResType res = handler();
|
PromiseFulfill<ResType>::call(handler(), resolve, reject);
|
||||||
PromiseFulfill<ResType>::call(res, resolve, reject);
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
}
|
}
|
||||||
@ -147,7 +149,7 @@ struct PromiseDispatch<void, void>
|
|||||||
using Promise = QtPromise::QPromise<void>;
|
using Promise = QtPromise::QPromise<void>;
|
||||||
|
|
||||||
template <typename THandler, typename TResolve, typename TReject>
|
template <typename THandler, typename TResolve, typename TReject>
|
||||||
static void call(THandler handler, TResolve resolve, TReject reject)
|
static void call(THandler handler, const TResolve& resolve, const TReject& reject)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
handler();
|
handler();
|
||||||
@ -165,10 +167,13 @@ struct PromiseHandler
|
|||||||
using Promise = typename PromiseDispatch<T, ResType>::Promise;
|
using Promise = typename PromiseDispatch<T, ResType>::Promise;
|
||||||
|
|
||||||
template <typename TResolve, typename TReject>
|
template <typename TResolve, typename TReject>
|
||||||
static std::function<void(T)> create(THandler handler, TResolve resolve, TReject reject)
|
static std::function<void(const T&)> create(
|
||||||
|
const THandler& handler,
|
||||||
|
const TResolve& resolve,
|
||||||
|
const TReject& reject)
|
||||||
{
|
{
|
||||||
return [=](const T& value) {
|
return [=](const T& value) {
|
||||||
PromiseDispatch<T, ResType>::call(value, handler, resolve, reject);
|
PromiseDispatch<T, ResType>::call(value, std::move(handler), resolve, reject);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -180,7 +185,10 @@ struct PromiseHandler<T, THandler, void>
|
|||||||
using Promise = typename PromiseDispatch<T, ResType>::Promise;
|
using Promise = typename PromiseDispatch<T, ResType>::Promise;
|
||||||
|
|
||||||
template <typename TResolve, typename TReject>
|
template <typename TResolve, typename TReject>
|
||||||
static std::function<void(T)> create(THandler handler, TResolve resolve, TReject reject)
|
static std::function<void(const T&)> create(
|
||||||
|
const THandler& handler,
|
||||||
|
const TResolve& resolve,
|
||||||
|
const TReject& reject)
|
||||||
{
|
{
|
||||||
return [=](const T&) {
|
return [=](const T&) {
|
||||||
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||||
@ -195,7 +203,10 @@ struct PromiseHandler<void, THandler, void>
|
|||||||
using Promise = typename PromiseDispatch<void, ResType>::Promise;
|
using Promise = typename PromiseDispatch<void, ResType>::Promise;
|
||||||
|
|
||||||
template <typename TResolve, typename TReject>
|
template <typename TResolve, typename TReject>
|
||||||
static std::function<void()> create(THandler handler, TResolve resolve, TReject reject)
|
static std::function<void()> create(
|
||||||
|
const THandler& handler,
|
||||||
|
const TResolve& resolve,
|
||||||
|
const TReject& reject)
|
||||||
{
|
{
|
||||||
return [=]() {
|
return [=]() {
|
||||||
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||||
@ -209,12 +220,15 @@ struct PromiseHandler<T, std::nullptr_t, void>
|
|||||||
using Promise = QtPromise::QPromise<T>;
|
using Promise = QtPromise::QPromise<T>;
|
||||||
|
|
||||||
template <typename TResolve, typename TReject>
|
template <typename TResolve, typename TReject>
|
||||||
static std::function<void(T)> create(std::nullptr_t, TResolve resolve, TReject reject)
|
static std::function<void(const T&)> create(
|
||||||
|
std::nullptr_t,
|
||||||
|
const TResolve& resolve,
|
||||||
|
const TReject& reject)
|
||||||
{
|
{
|
||||||
return [=](const T& value) {
|
return [=](const T& value) {
|
||||||
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
||||||
// promise2 must be fulfilled with the same value as promise1.
|
// promise2 must be fulfilled with the same value as promise1.
|
||||||
PromiseFulfill<T>::call(value, resolve, reject);
|
PromiseFulfill<T>::call(std::move(T(value)), resolve, reject);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -225,7 +239,10 @@ struct PromiseHandler<void, std::nullptr_t, void>
|
|||||||
using Promise = QtPromise::QPromise<void>;
|
using Promise = QtPromise::QPromise<void>;
|
||||||
|
|
||||||
template <typename TResolve, typename TReject>
|
template <typename TResolve, typename TReject>
|
||||||
static std::function<void()> create(std::nullptr_t, TResolve resolve, TReject)
|
static std::function<void()> create(
|
||||||
|
std::nullptr_t,
|
||||||
|
const TResolve& resolve,
|
||||||
|
const TReject&)
|
||||||
{
|
{
|
||||||
return [=]() {
|
return [=]() {
|
||||||
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
||||||
@ -238,11 +255,13 @@ struct PromiseHandler<void, std::nullptr_t, void>
|
|||||||
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
|
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
|
||||||
struct PromiseCatcher
|
struct PromiseCatcher
|
||||||
{
|
{
|
||||||
using Functor = std::function<void(QtPromise::QPromiseError)>;
|
|
||||||
using ResType = typename std::result_of<THandler(TArg)>::type;
|
using ResType = typename std::result_of<THandler(TArg)>::type;
|
||||||
|
|
||||||
template <typename TResolve, typename TReject>
|
template <typename TResolve, typename TReject>
|
||||||
static Functor create(THandler handler, TResolve resolve, TReject reject)
|
static std::function<void(const QtPromise::QPromiseError&)> create(
|
||||||
|
const THandler& handler,
|
||||||
|
const TResolve& resolve,
|
||||||
|
const TReject& reject)
|
||||||
{
|
{
|
||||||
return [=](const QtPromise::QPromiseError& error) {
|
return [=](const QtPromise::QPromiseError& error) {
|
||||||
try {
|
try {
|
||||||
@ -259,11 +278,13 @@ struct PromiseCatcher
|
|||||||
template <typename T, typename THandler>
|
template <typename T, typename THandler>
|
||||||
struct PromiseCatcher<T, THandler, void>
|
struct PromiseCatcher<T, THandler, void>
|
||||||
{
|
{
|
||||||
using Functor = std::function<void(QtPromise::QPromiseError)>;
|
|
||||||
using ResType = typename std::result_of<THandler()>::type;
|
using ResType = typename std::result_of<THandler()>::type;
|
||||||
|
|
||||||
template <typename TResolve, typename TReject>
|
template <typename TResolve, typename TReject>
|
||||||
static Functor create(THandler handler, TResolve resolve, TReject reject)
|
static std::function<void(const QtPromise::QPromiseError&)> create(
|
||||||
|
const THandler& handler,
|
||||||
|
const TResolve& resolve,
|
||||||
|
const TReject& reject)
|
||||||
{
|
{
|
||||||
return [=](const QtPromise::QPromiseError& error) {
|
return [=](const QtPromise::QPromiseError& error) {
|
||||||
try {
|
try {
|
||||||
@ -278,10 +299,11 @@ struct PromiseCatcher<T, THandler, void>
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct PromiseCatcher<T, std::nullptr_t, void>
|
struct PromiseCatcher<T, std::nullptr_t, void>
|
||||||
{
|
{
|
||||||
using Functor = std::function<void(QtPromise::QPromiseError)>;
|
|
||||||
|
|
||||||
template <typename TResolve, typename TReject>
|
template <typename TResolve, typename TReject>
|
||||||
static Functor create(std::nullptr_t, TResolve, TReject reject)
|
static std::function<void(const QtPromise::QPromiseError&)> create(
|
||||||
|
std::nullptr_t,
|
||||||
|
const TResolve&,
|
||||||
|
const TReject& reject)
|
||||||
{
|
{
|
||||||
return [=](const QtPromise::QPromiseError& error) {
|
return [=](const QtPromise::QPromiseError& error) {
|
||||||
// 2.2.7.4. If onRejected is not a function and promise1 is rejected,
|
// 2.2.7.4. If onRejected is not a function and promise1 is rejected,
|
||||||
@ -291,33 +313,142 @@ struct PromiseCatcher<T, std::nullptr_t, void>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <typename T> class PromiseData;
|
||||||
struct PromiseDataBase: public QSharedData
|
|
||||||
{
|
|
||||||
using ErrorType = QtPromise::QPromiseError;
|
|
||||||
using CatcherList = QVector<std::function<void(ErrorType)> >;
|
|
||||||
|
|
||||||
bool resolved;
|
template <typename T>
|
||||||
bool rejected;
|
class PromiseDataBase: public QSharedData
|
||||||
ErrorType error;
|
{
|
||||||
CatcherList catchers;
|
public:
|
||||||
|
using Error = QtPromise::QPromiseError;
|
||||||
|
using Catcher = std::function<void(const Error&)>;
|
||||||
|
|
||||||
|
virtual ~PromiseDataBase() {}
|
||||||
|
|
||||||
|
bool isFulfilled() const { return m_settled && m_error.isNull(); }
|
||||||
|
bool isRejected() const { return m_settled && !m_error.isNull(); }
|
||||||
|
bool isPending() const { return !m_settled; }
|
||||||
|
|
||||||
|
void addCatcher(Catcher catcher)
|
||||||
|
{
|
||||||
|
m_catchers.append(std::move(catcher));
|
||||||
|
}
|
||||||
|
|
||||||
|
void reject(Error error)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_error.isNull());
|
||||||
|
m_error.reset(new Error(std::move(error)));
|
||||||
|
setSettled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispatch()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!isPending());
|
||||||
|
|
||||||
|
if (isFulfilled()) {
|
||||||
|
notify();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(isRejected());
|
||||||
|
QSharedPointer<Error> error = m_error;
|
||||||
|
QVector<Catcher> catchers(std::move(m_catchers));
|
||||||
|
for (const auto& catcher: catchers) {
|
||||||
|
qtpromise_defer([=]() {
|
||||||
|
catcher(*error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setSettled()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_settled);
|
||||||
|
m_settled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void notify() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_settled = false;
|
||||||
|
QVector<Catcher> m_catchers;
|
||||||
|
QSharedPointer<Error> m_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct PromiseData: PromiseDataBase<T>
|
class PromiseData: public PromiseDataBase<T>
|
||||||
{
|
{
|
||||||
using HandlerList = QVector<std::function<void(T)> >;
|
public:
|
||||||
|
using Handler = std::function<void(const T&)>;
|
||||||
|
|
||||||
HandlerList handlers;
|
void addHandler(Handler handler)
|
||||||
T value;
|
{
|
||||||
|
m_handlers.append(std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& value() const
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_value.isNull());
|
||||||
|
return *m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resolve(T&& value)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_value.isNull());
|
||||||
|
m_value.reset(new T(std::move(value)));
|
||||||
|
this->setSettled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resolve(const T& value)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_value.isNull());
|
||||||
|
m_value.reset(new T(value));
|
||||||
|
this->setSettled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void notify() Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
Q_ASSERT(this->isFulfilled());
|
||||||
|
QSharedPointer<T> value(m_value);
|
||||||
|
QVector<Handler> handlers(std::move(m_handlers));
|
||||||
|
for (const auto& handler: handlers) {
|
||||||
|
qtpromise_defer([=]() {
|
||||||
|
handler(*value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<Handler> m_handlers;
|
||||||
|
QSharedPointer<T> m_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct PromiseData<void>: PromiseDataBase<void>
|
class PromiseData<void>: public PromiseDataBase<void>
|
||||||
{
|
{
|
||||||
using HandlerList = QVector<std::function<void()> >;
|
public:
|
||||||
|
using Handler = std::function<void()>;
|
||||||
|
|
||||||
HandlerList handlers;
|
void addHandler(Handler handler)
|
||||||
|
{
|
||||||
|
m_handlers.append(std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void resolve() { setSettled(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void notify() Q_DECL_OVERRIDE
|
||||||
|
{
|
||||||
|
Q_ASSERT(isFulfilled());
|
||||||
|
QVector<Handler> handlers(std::move(m_handlers));
|
||||||
|
for (const auto& handler: handlers) {
|
||||||
|
qtpromise_defer([=]() {
|
||||||
|
handler();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<Handler> m_handlers;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QtPromise
|
} // namespace QtPromise
|
||||||
|
@ -9,32 +9,48 @@ namespace QtPromise {
|
|||||||
class QPromiseError
|
class QPromiseError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QPromiseError()
|
|
||||||
: exception(nullptr)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
QPromiseError(const T& value)
|
QPromiseError(const T& value)
|
||||||
: exception(nullptr)
|
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
throw value;
|
throw value;
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
exception = std::current_exception();
|
m_exception = std::current_exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QPromiseError(const std::exception_ptr& exception)
|
QPromiseError(const std::exception_ptr& exception)
|
||||||
: exception(exception)
|
: m_exception(exception)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
QPromiseError(const QPromiseError& error)
|
||||||
|
: m_exception(error.m_exception)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
QPromiseError(QPromiseError&& other)
|
||||||
|
: m_exception(nullptr)
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPromiseError& operator =(QPromiseError other)
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(QPromiseError& other)
|
||||||
|
{
|
||||||
|
qSwap(m_exception, other.m_exception);
|
||||||
|
}
|
||||||
|
|
||||||
void rethrow() const
|
void rethrow() const
|
||||||
{
|
{
|
||||||
std::rethrow_exception(exception);
|
std::rethrow_exception(m_exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::exception_ptr exception;
|
std::exception_ptr m_exception;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QtPromise
|
} // namespace QtPromise
|
||||||
|
@ -48,8 +48,7 @@ struct PromiseFulfill<QFuture<T> >
|
|||||||
watcher->waitForFinished();
|
watcher->waitForFinished();
|
||||||
reject(QtPromise::QPromiseCanceledException());
|
reject(QtPromise::QPromiseCanceledException());
|
||||||
} else {
|
} else {
|
||||||
T res = watcher->result();
|
PromiseFulfill<T>::call(watcher->result(), resolve, reject);
|
||||||
PromiseFulfill<T>::call(res, resolve, reject);
|
|
||||||
}
|
}
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
reject(std::current_exception());
|
reject(std::current_exception());
|
||||||
|
@ -7,14 +7,14 @@
|
|||||||
namespace QtPromise {
|
namespace QtPromise {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename QtPromisePrivate::PromiseDeduce<T>::Type qPromise(const T& value)
|
typename QtPromisePrivate::PromiseDeduce<T>::Type qPromise(T&& value)
|
||||||
{
|
{
|
||||||
using namespace QtPromisePrivate;
|
using namespace QtPromisePrivate;
|
||||||
using Promise = typename PromiseDeduce<T>::Type;
|
using Promise = typename PromiseDeduce<T>::Type;
|
||||||
return Promise([=](
|
return Promise([&](
|
||||||
const QPromiseResolve<typename Promise::Type>& resolve,
|
const QPromiseResolve<typename Promise::Type>& resolve,
|
||||||
const QPromiseReject<typename Promise::Type>& reject) {
|
const QPromiseReject<typename Promise::Type>& reject) {
|
||||||
PromiseFulfill<T>::call(value, resolve, reject);
|
PromiseFulfill<T>::call(std::forward<T>(value), resolve, reject);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
SUBDIRS += \
|
SUBDIRS += \
|
||||||
|
benchmark \
|
||||||
future \
|
future \
|
||||||
helpers \
|
helpers \
|
||||||
qpromise \
|
qpromise \
|
||||||
|
5
tests/auto/benchmark/benchmark.pro
Normal file
5
tests/auto/benchmark/benchmark.pro
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
QT += concurrent
|
||||||
|
TARGET = tst_benchmark
|
||||||
|
SOURCES += $$PWD/tst_benchmark.cpp
|
||||||
|
|
||||||
|
include(../tests.pri)
|
223
tests/auto/benchmark/tst_benchmark.cpp
Normal file
223
tests/auto/benchmark/tst_benchmark.cpp
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
// QtPromise
|
||||||
|
#include <QtPromise>
|
||||||
|
|
||||||
|
// Qt
|
||||||
|
#include <QtConcurrent>
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
using namespace QtPromise;
|
||||||
|
|
||||||
|
class tst_benchmark: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
void valueResolve();
|
||||||
|
void valueReject();
|
||||||
|
void valueThen();
|
||||||
|
void errorReject();
|
||||||
|
private Q_SLOTS:
|
||||||
|
void errorThen();
|
||||||
|
|
||||||
|
}; // class tst_benchmark
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_benchmark)
|
||||||
|
#include "tst_benchmark.moc"
|
||||||
|
|
||||||
|
struct Logs {
|
||||||
|
int ctor = 0;
|
||||||
|
int copy = 0;
|
||||||
|
int move = 0;
|
||||||
|
int refs = 0;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
ctor = 0;
|
||||||
|
copy = 0;
|
||||||
|
move = 0;
|
||||||
|
refs = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Logger
|
||||||
|
{
|
||||||
|
Logger() { logs().ctor++; logs().refs++; }
|
||||||
|
Logger(const Logger&) { logs().copy++; logs().refs++; }
|
||||||
|
Logger(Logger&&) { logs().move++; logs().refs++; }
|
||||||
|
~Logger() { logs().refs--; }
|
||||||
|
|
||||||
|
Logger& operator=(const Logger&) { logs().copy++; return *this; }
|
||||||
|
Logger& operator=(Logger&&) { logs().move++; return *this; }
|
||||||
|
|
||||||
|
public: // STATICS
|
||||||
|
static Logs& logs() { static Logs logs; return logs; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Data: public Logger
|
||||||
|
{
|
||||||
|
Data(int v): Logger(), m_value(v) {}
|
||||||
|
int value() const { return m_value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_benchmark::valueResolve()
|
||||||
|
{
|
||||||
|
{ // should move the value when resolved by rvalue
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<Data>([&](const QPromiseResolve<Data>& resolve) {
|
||||||
|
resolve(Data(42));
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 1);
|
||||||
|
QCOMPARE(Data::logs().copy, 0);
|
||||||
|
QCOMPARE(Data::logs().move, 1); // move value to the promise data
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
}
|
||||||
|
{ // should create one copy of the value when resolved by lvalue
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<Data>([&](const QPromiseResolve<Data>& resolve) {
|
||||||
|
Data value(42);
|
||||||
|
resolve(value);
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 1);
|
||||||
|
QCOMPARE(Data::logs().copy, 1); // copy value to the promise data
|
||||||
|
QCOMPARE(Data::logs().move, 0);
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_benchmark::valueReject()
|
||||||
|
{
|
||||||
|
{ // should not create any data if rejected
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<Data>([&](const QPromiseResolve<Data>&, const QPromiseReject<Data>& reject) {
|
||||||
|
reject(QString("foo"));
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 0);
|
||||||
|
QCOMPARE(Data::logs().copy, 0);
|
||||||
|
QCOMPARE(Data::logs().move, 0);
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_benchmark::valueThen()
|
||||||
|
{
|
||||||
|
{ // should not copy value on continutation if fulfilled
|
||||||
|
int value = -1;
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<Data>::resolve(Data(42)).then([&](const Data& res) {
|
||||||
|
value = res.value();
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 1);
|
||||||
|
QCOMPARE(Data::logs().copy, 0);
|
||||||
|
QCOMPARE(Data::logs().move, 1); // move value to the promise data
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
QCOMPARE(value, 42);
|
||||||
|
}
|
||||||
|
{ // should not create value on continutation if rejected
|
||||||
|
int value = -1;
|
||||||
|
QString error;
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<Data>::reject(QString("foo")).then([&](const Data& res) {
|
||||||
|
value = res.value();
|
||||||
|
}, [&](const QString& err) {
|
||||||
|
error = err;
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 0);
|
||||||
|
QCOMPARE(Data::logs().copy, 0);
|
||||||
|
QCOMPARE(Data::logs().move, 0); // move value to the promise data
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
QCOMPARE(error, QString("foo"));
|
||||||
|
QCOMPARE(value, -1);
|
||||||
|
}
|
||||||
|
{ // should move the returned value when fulfilled
|
||||||
|
int value = -1;
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<int>::resolve(42).then([&](int res) {
|
||||||
|
return Data(res+2);
|
||||||
|
}).then([&](const Data& res) {
|
||||||
|
value = res.value();
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 1);
|
||||||
|
QCOMPARE(Data::logs().copy, 0);
|
||||||
|
QCOMPARE(Data::logs().move, 1); // move values to the next promise data
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
QCOMPARE(value, 44);
|
||||||
|
}
|
||||||
|
{ // should not create any data if handler throws
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<int>::resolve(42).then([&](int res) {
|
||||||
|
throw QString("foo");
|
||||||
|
return Data(res+2);
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 0);
|
||||||
|
QCOMPARE(Data::logs().copy, 0);
|
||||||
|
QCOMPARE(Data::logs().move, 0);
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_benchmark::errorReject()
|
||||||
|
{
|
||||||
|
{ // should create one copy of the error when rejected by rvalue
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<int>([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||||
|
reject(Data(42));
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 1);
|
||||||
|
QCOMPARE(Data::logs().copy, 1); // copy value in std::exception_ptr
|
||||||
|
QCOMPARE(Data::logs().move, 0);
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
}
|
||||||
|
{ // should create one copy of the error when rejected by lvalue (no extra copy)
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<int>([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||||
|
Data error(42);
|
||||||
|
reject(error);
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 1);
|
||||||
|
QCOMPARE(Data::logs().copy, 1); // copy value to the promise data
|
||||||
|
QCOMPARE(Data::logs().move, 0);
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_benchmark::errorThen()
|
||||||
|
{
|
||||||
|
{ // should not copy error on continutation if rejected
|
||||||
|
int value = -1;
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<void>::reject(Data(42)).fail([&](const Data& res) {
|
||||||
|
value = res.value();
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 1);
|
||||||
|
QCOMPARE(Data::logs().copy, 1); // (initial) copy value in std::exception_ptr
|
||||||
|
QCOMPARE(Data::logs().move, 0);
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
QCOMPARE(value, 42);
|
||||||
|
}
|
||||||
|
{ // should not copy error on continutation if rethrown
|
||||||
|
int value = -1;
|
||||||
|
Data::logs().reset();
|
||||||
|
QPromise<void>::reject(Data(42)).fail([](const Data&) {
|
||||||
|
throw;
|
||||||
|
}).fail([&](const Data& res) {
|
||||||
|
value = res.value();
|
||||||
|
}).wait();
|
||||||
|
|
||||||
|
QCOMPARE(Data::logs().ctor, 1);
|
||||||
|
QCOMPARE(Data::logs().copy, 1); // (initial) copy value in std::exception_ptr
|
||||||
|
QCOMPARE(Data::logs().move, 0);
|
||||||
|
QCOMPARE(Data::logs().refs, 0);
|
||||||
|
QCOMPARE(value, 42);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user