mirror of
https://github.com/simonbrunel/qtpromise.git
synced 2024-11-21 18:24:29 +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:
|
||||
using Type = T;
|
||||
|
||||
virtual ~QPromiseBase() { }
|
||||
|
||||
bool isFulfilled() const { return m_d->resolved && !m_d->rejected; }
|
||||
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();
|
||||
QPromiseBase(const QPromiseBase<T>& other): m_d(other.m_d) {}
|
||||
QPromiseBase(const QPromise<T>& other): m_d(other.m_d) {}
|
||||
QPromiseBase(QPromiseBase<T>&& other) { swap(other); }
|
||||
|
||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type = 0>
|
||||
inline QPromiseBase(F resolver);
|
||||
@ -50,9 +26,33 @@ protected:
|
||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
|
||||
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>
|
||||
@ -60,22 +60,17 @@ class QPromise: public QPromiseBase<T>
|
||||
{
|
||||
public:
|
||||
template <typename F>
|
||||
QPromise(F resolver): QPromiseBase<T>(resolver) { }
|
||||
QPromise(F&& resolver): QPromiseBase<T>(std::forward<F>(resolver)) { }
|
||||
|
||||
template <typename THandler>
|
||||
inline QPromise<T> finally(THandler handler);
|
||||
inline QPromise<T> finally(THandler handler) const;
|
||||
|
||||
public: // STATIC
|
||||
inline static QPromise<QVector<T> > all(const QVector<QPromise<T> >& promises);
|
||||
inline static QPromise<T> resolve(const T& value);
|
||||
|
||||
protected:
|
||||
inline void notify(const typename QtPromisePrivate::PromiseData<T>::HandlerList& handlers) const Q_DECL_OVERRIDE;
|
||||
inline static QPromise<T> resolve(T&& value);
|
||||
|
||||
private:
|
||||
friend class QPromiseBase<T>;
|
||||
|
||||
QPromise(const QPromiseBase<T>& p) : QPromiseBase<T>(p) { }
|
||||
};
|
||||
|
||||
template <>
|
||||
@ -83,22 +78,17 @@ class QPromise<void>: public QPromiseBase<void>
|
||||
{
|
||||
public:
|
||||
template <typename F>
|
||||
QPromise(F resolver): QPromiseBase<void>(resolver) { }
|
||||
QPromise(F&& resolver): QPromiseBase<void>(std::forward<F>(resolver)) { }
|
||||
|
||||
template <typename THandler>
|
||||
inline QPromise<void> finally(THandler handler);
|
||||
inline QPromise<void> finally(THandler handler) const;
|
||||
|
||||
public: // STATIC
|
||||
inline static QPromise<void> all(const QVector<QPromise<void> >& promises);
|
||||
inline static QPromise<void> resolve();
|
||||
|
||||
protected:
|
||||
inline void notify(const typename QtPromisePrivate::PromiseData<void>::HandlerList& handlers) const Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
friend class QPromiseBase<void>;
|
||||
|
||||
QPromise(const QPromiseBase<void>& p) : QPromiseBase<void>(p) { }
|
||||
};
|
||||
|
||||
} // namespace QtPromise
|
||||
|
@ -4,141 +4,87 @@
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <class T = void>
|
||||
template <class T>
|
||||
class QPromiseResolve
|
||||
{
|
||||
public:
|
||||
QPromiseResolve(const QPromise<T>& p)
|
||||
: m_promise(p)
|
||||
QPromiseResolve(QPromise<T> p)
|
||||
: m_promise(new QPromise<T>(std::move(p)))
|
||||
{ }
|
||||
|
||||
void operator()(const T& value) const
|
||||
void operator()(const T& value) const {
|
||||
resolve(value);
|
||||
}
|
||||
|
||||
void operator()(T&& value) const
|
||||
{
|
||||
auto p = m_promise;
|
||||
if (p.isPending()) {
|
||||
p.m_d->rejected = false;
|
||||
p.m_d->resolved = true;
|
||||
p.m_d->value = value;
|
||||
p.dispatch();
|
||||
}
|
||||
resolve(std::move(value));
|
||||
}
|
||||
|
||||
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 <>
|
||||
class QPromiseResolve<void>
|
||||
{
|
||||
public:
|
||||
QPromiseResolve(const QPromise<void>& p)
|
||||
: m_promise(p)
|
||||
QPromiseResolve(QPromise<void> p)
|
||||
: m_promise(new QPromise<void>(std::move(p)))
|
||||
{ }
|
||||
|
||||
void operator()() const
|
||||
{
|
||||
auto p = m_promise;
|
||||
if (p.isPending()) {
|
||||
p.m_d->rejected = false;
|
||||
p.m_d->resolved = true;
|
||||
p.dispatch();
|
||||
Q_ASSERT(!m_promise.isNull());
|
||||
if (m_promise->isPending()) {
|
||||
m_promise->m_d->resolve();
|
||||
m_promise->m_d->dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QPromise<void> m_promise;
|
||||
QSharedPointer<QPromise<void> > m_promise;
|
||||
};
|
||||
|
||||
template <class T = void>
|
||||
template <class T>
|
||||
class QPromiseReject
|
||||
{
|
||||
public:
|
||||
QPromiseReject()
|
||||
QPromiseReject(QPromise<T> p)
|
||||
: m_promise(new QPromise<T>(std::move(p)))
|
||||
{ }
|
||||
|
||||
QPromiseReject(const QPromise<T>& p)
|
||||
: m_promise(p)
|
||||
{ }
|
||||
|
||||
void operator()(const QPromiseError& error) const
|
||||
template <typename E>
|
||||
void operator()(E&& error) const
|
||||
{
|
||||
auto p = m_promise;
|
||||
if (p.isPending()) {
|
||||
p.m_d->rejected = true;
|
||||
p.m_d->resolved = true;
|
||||
p.m_d->error = error;
|
||||
p.dispatch();
|
||||
Q_ASSERT(!m_promise.isNull());
|
||||
if (m_promise->isPending()) {
|
||||
m_promise->m_d->reject(std::forward<E>(error));
|
||||
m_promise->m_d->dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
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 F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type>
|
||||
inline QPromiseBase<T>::QPromiseBase(F resolver)
|
||||
: m_d(new QtPromisePrivate::PromiseData<T>())
|
||||
{
|
||||
auto resolve = QPromiseResolve<T>(*this);
|
||||
auto reject = QPromiseReject<T>(*this);
|
||||
QPromiseResolve<T> resolve(*this);
|
||||
QPromiseReject<T> reject(*this);
|
||||
|
||||
try {
|
||||
resolver(resolve);
|
||||
@ -152,8 +98,8 @@ template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count
|
||||
inline QPromiseBase<T>::QPromiseBase(F resolver)
|
||||
: m_d(new QtPromisePrivate::PromiseData<T>())
|
||||
{
|
||||
auto resolve = QPromiseResolve<T>(*this);
|
||||
auto reject = QPromiseReject<T>(*this);
|
||||
QPromiseResolve<T> resolve(*this);
|
||||
QPromiseReject<T> reject(*this);
|
||||
|
||||
try {
|
||||
resolver(resolve, reject);
|
||||
@ -163,33 +109,59 @@ inline QPromiseBase<T>::QPromiseBase(F resolver)
|
||||
}
|
||||
|
||||
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 PromiseType = typename PromiseHandler<T, TFulfilled>::Promise;
|
||||
|
||||
Q_ASSERT(m_d->resolved);
|
||||
PromiseType next([&](
|
||||
const QPromiseResolve<typename PromiseType::Type>& resolve,
|
||||
const QPromiseReject<typename PromiseType::Type>& reject) {
|
||||
m_d->addHandler(PromiseHandler<T, TFulfilled>::create(fulfilled, resolve, reject));
|
||||
m_d->addCatcher(PromiseCatcher<T, TRejected>::create(rejected, resolve, reject));
|
||||
});
|
||||
|
||||
typename PromiseData<T>::HandlerList handlers;
|
||||
typename PromiseData<T>::CatcherList catchers;
|
||||
|
||||
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 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 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));
|
||||
|
||||
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;
|
||||
if (--(*remaining) == 0) {
|
||||
resolve(*results);
|
||||
@ -236,26 +208,15 @@ inline QPromise<QVector<T> > QPromise<T>::all(const QVector<QPromise<T> >& promi
|
||||
}
|
||||
|
||||
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) {
|
||||
resolve(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);
|
||||
}
|
||||
return QPromise<T>([&](const QPromiseResolve<T>& resolve) {
|
||||
resolve(std::forward<T>(value));
|
||||
});
|
||||
}
|
||||
|
||||
template <typename THandler>
|
||||
inline QPromise<void> QPromise<void>::finally(THandler handler)
|
||||
inline QPromise<void> QPromise<void>::finally(THandler handler) const
|
||||
{
|
||||
return this->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()));
|
||||
|
||||
for (const auto& promise: promises) {
|
||||
QPromise<void>(promise).then([=]() {
|
||||
promise.then([=]() {
|
||||
if (--(*remaining) == 0) {
|
||||
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
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
// Qt
|
||||
#include <QTimer>
|
||||
#include <QSharedPointer>
|
||||
#include <QSharedData>
|
||||
#include <QVector>
|
||||
|
||||
@ -26,9 +27,9 @@ class QPromiseReject;
|
||||
namespace QtPromisePrivate {
|
||||
|
||||
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>
|
||||
@ -46,11 +47,11 @@ template <typename T>
|
||||
struct PromiseFulfill
|
||||
{
|
||||
static void call(
|
||||
const T& value,
|
||||
T&& value,
|
||||
const QtPromise::QPromiseResolve<T>& resolve,
|
||||
const QtPromise::QPromiseReject<T>&)
|
||||
{
|
||||
resolve(value);
|
||||
resolve(std::move(value));
|
||||
}
|
||||
};
|
||||
|
||||
@ -58,7 +59,7 @@ template <typename T>
|
||||
struct PromiseFulfill<QtPromise::QPromise<T> >
|
||||
{
|
||||
static void call(
|
||||
QtPromise::QPromise<T> promise,
|
||||
const QtPromise::QPromise<T>& promise,
|
||||
const QtPromise::QPromiseResolve<T>& resolve,
|
||||
const QtPromise::QPromiseReject<T>& reject)
|
||||
{
|
||||
@ -76,7 +77,10 @@ template <>
|
||||
struct PromiseFulfill<QtPromise::QPromise<void> >
|
||||
{
|
||||
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(
|
||||
[=]() {
|
||||
@ -95,11 +99,10 @@ struct PromiseDispatch
|
||||
using ResType = Unqualified<TRes>;
|
||||
|
||||
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 {
|
||||
const ResType res = handler(value);
|
||||
PromiseFulfill<ResType>::call(res, resolve, reject);
|
||||
PromiseFulfill<ResType>::call(handler(value), resolve, reject);
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
}
|
||||
@ -112,7 +115,7 @@ struct PromiseDispatch<T, void>
|
||||
using Promise = QtPromise::QPromise<void>;
|
||||
|
||||
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 {
|
||||
handler(value);
|
||||
@ -130,11 +133,10 @@ struct PromiseDispatch<void, TRes>
|
||||
using ResType = Unqualified<TRes>;
|
||||
|
||||
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 {
|
||||
const ResType res = handler();
|
||||
PromiseFulfill<ResType>::call(res, resolve, reject);
|
||||
PromiseFulfill<ResType>::call(handler(), resolve, reject);
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
}
|
||||
@ -147,7 +149,7 @@ struct PromiseDispatch<void, void>
|
||||
using Promise = QtPromise::QPromise<void>;
|
||||
|
||||
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 {
|
||||
handler();
|
||||
@ -165,10 +167,13 @@ struct PromiseHandler
|
||||
using Promise = typename PromiseDispatch<T, ResType>::Promise;
|
||||
|
||||
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) {
|
||||
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;
|
||||
|
||||
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&) {
|
||||
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||
@ -195,7 +203,10 @@ struct PromiseHandler<void, THandler, void>
|
||||
using Promise = typename PromiseDispatch<void, ResType>::Promise;
|
||||
|
||||
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 [=]() {
|
||||
PromiseDispatch<void, ResType>::call(handler, resolve, reject);
|
||||
@ -209,12 +220,15 @@ struct PromiseHandler<T, std::nullptr_t, void>
|
||||
using Promise = QtPromise::QPromise<T>;
|
||||
|
||||
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) {
|
||||
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
||||
// 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>;
|
||||
|
||||
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 [=]() {
|
||||
// 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>
|
||||
struct PromiseCatcher
|
||||
{
|
||||
using Functor = std::function<void(QtPromise::QPromiseError)>;
|
||||
using ResType = typename std::result_of<THandler(TArg)>::type;
|
||||
|
||||
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) {
|
||||
try {
|
||||
@ -259,11 +278,13 @@ struct PromiseCatcher
|
||||
template <typename T, typename THandler>
|
||||
struct PromiseCatcher<T, THandler, void>
|
||||
{
|
||||
using Functor = std::function<void(QtPromise::QPromiseError)>;
|
||||
using ResType = typename std::result_of<THandler()>::type;
|
||||
|
||||
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) {
|
||||
try {
|
||||
@ -278,10 +299,11 @@ struct PromiseCatcher<T, THandler, void>
|
||||
template <typename T>
|
||||
struct PromiseCatcher<T, std::nullptr_t, void>
|
||||
{
|
||||
using Functor = std::function<void(QtPromise::QPromiseError)>;
|
||||
|
||||
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) {
|
||||
// 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>
|
||||
struct PromiseDataBase: public QSharedData
|
||||
{
|
||||
using ErrorType = QtPromise::QPromiseError;
|
||||
using CatcherList = QVector<std::function<void(ErrorType)> >;
|
||||
template <typename T> class PromiseData;
|
||||
|
||||
bool resolved;
|
||||
bool rejected;
|
||||
ErrorType error;
|
||||
CatcherList catchers;
|
||||
template <typename T>
|
||||
class PromiseDataBase: public QSharedData
|
||||
{
|
||||
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>
|
||||
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;
|
||||
T value;
|
||||
void addHandler(Handler handler)
|
||||
{
|
||||
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 <>
|
||||
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
|
||||
|
@ -9,32 +9,48 @@ namespace QtPromise {
|
||||
class QPromiseError
|
||||
{
|
||||
public:
|
||||
QPromiseError()
|
||||
: exception(nullptr)
|
||||
{ }
|
||||
|
||||
template <typename T>
|
||||
QPromiseError(const T& value)
|
||||
: exception(nullptr)
|
||||
{
|
||||
try {
|
||||
throw value;
|
||||
} catch(...) {
|
||||
exception = std::current_exception();
|
||||
m_exception = std::current_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
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
std::rethrow_exception(m_exception);
|
||||
}
|
||||
|
||||
private:
|
||||
std::exception_ptr exception;
|
||||
std::exception_ptr m_exception;
|
||||
};
|
||||
|
||||
} // namespace QtPromise
|
||||
|
@ -48,8 +48,7 @@ struct PromiseFulfill<QFuture<T> >
|
||||
watcher->waitForFinished();
|
||||
reject(QtPromise::QPromiseCanceledException());
|
||||
} else {
|
||||
T res = watcher->result();
|
||||
PromiseFulfill<T>::call(res, resolve, reject);
|
||||
PromiseFulfill<T>::call(watcher->result(), resolve, reject);
|
||||
}
|
||||
} catch(...) {
|
||||
reject(std::current_exception());
|
||||
|
@ -7,14 +7,14 @@
|
||||
namespace QtPromise {
|
||||
|
||||
template <typename T>
|
||||
typename QtPromisePrivate::PromiseDeduce<T>::Type qPromise(const T& value)
|
||||
typename QtPromisePrivate::PromiseDeduce<T>::Type qPromise(T&& value)
|
||||
{
|
||||
using namespace QtPromisePrivate;
|
||||
using Promise = typename PromiseDeduce<T>::Type;
|
||||
return Promise([=](
|
||||
return Promise([&](
|
||||
const QPromiseResolve<typename Promise::Type>& resolve,
|
||||
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
|
||||
SUBDIRS += \
|
||||
benchmark \
|
||||
future \
|
||||
helpers \
|
||||
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