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

View File

@ -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);
}
void operator()(T&& value) const
{ {
auto p = m_promise; resolve(std::move(value));
if (p.isPending()) {
p.m_d->rejected = false;
p.m_d->resolved = true;
p.m_d->value = value;
p.dispatch();
}
} }
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,
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; if (!m_d->isPending()) {
typename PromiseData<T>::CatcherList catchers; m_d->dispatch();
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);
} }
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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
TEMPLATE = subdirs TEMPLATE = subdirs
SUBDIRS += \ SUBDIRS += \
benchmark \
future \ future \
helpers \ helpers \
qpromise \ 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);
}
}