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:
Simon Brunel 2017-06-03 10:04:28 +02:00
parent a8ad3619b9
commit 9bbef41a50
9 changed files with 558 additions and 241 deletions

View File

@ -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

View File

@ -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
{
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()(const T& value) const {
resolve(value);
}
void operator()(T&& value) const
{
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);
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);
}
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));
});
} 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

View File

@ -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

View File

@ -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

View File

@ -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());

View File

@ -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);
});
}

View File

@ -1,5 +1,6 @@
TEMPLATE = subdirs
SUBDIRS += \
benchmark \
future \
helpers \
qpromise \

View File

@ -0,0 +1,5 @@
QT += concurrent
TARGET = tst_benchmark
SOURCES += $$PWD/tst_benchmark.cpp
include(../tests.pri)

View 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);
}
}