Avoid value copy when fulfilled from promise

This commit is contained in:
Simon Brunel 2017-08-22 21:52:28 +02:00
parent c4aab4ef36
commit 49a1d6a57b
4 changed files with 92 additions and 29 deletions

View File

@ -49,6 +49,7 @@ public: // STATIC
inline static QPromise<T> reject(E&& error); inline static QPromise<T> reject(E&& error);
protected: protected:
friend struct QtPromisePrivate::PromiseFulfill<QPromise<T> >;
friend class QPromiseResolve<T>; friend class QPromiseResolve<T>;
friend class QPromiseReject<T>; friend class QPromiseReject<T>;

View File

@ -12,28 +12,18 @@ public:
: m_promise(new QPromise<T>(std::move(p))) : m_promise(new QPromise<T>(std::move(p)))
{ } { }
void operator()(const T& value) const template <typename V>
void operator()(V&& value) const
{ {
resolve(value); Q_ASSERT(!m_promise.isNull());
} if (m_promise->isPending()) {
m_promise->m_d->resolve(std::forward<V>(value));
void operator()(T&& value) const m_promise->m_d->dispatch();
{ }
resolve(std::move(value));
} }
private: private:
QSharedPointer<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 <>

View File

@ -79,13 +79,17 @@ struct PromiseFulfill<QtPromise::QPromise<T> >
const QtPromise::QPromiseResolve<T>& resolve, const QtPromise::QPromiseResolve<T>& resolve,
const QtPromise::QPromiseReject<T>& reject) const QtPromise::QPromiseReject<T>& reject)
{ {
promise.then( if (promise.isFulfilled()) {
[=](const T& value) { resolve(promise.m_d->value());
resolve(value); } else if (promise.isRejected()) {
}, reject(promise.m_d->error());
[=]() { // catch all } else {
reject(std::current_exception()); promise.then([=]() {
resolve(promise.m_d->value());
}, [=]() { // catch all
reject(promise.m_d->error());
}); });
}
} }
}; };
@ -98,13 +102,17 @@ struct PromiseFulfill<QtPromise::QPromise<void> >
const TResolve& resolve, const TResolve& resolve,
const TReject& reject) const TReject& reject)
{ {
promise.then( if (promise.isFulfilled()) {
[=]() { resolve();
} else if (promise.isRejected()) {
reject(promise.m_d->error());
} else {
promise.then([=]() {
resolve(); resolve();
}, }, [=]() { // catch all
[=]() { // catch all reject(promise.m_d->error());
reject(std::current_exception());
}); });
}
} }
}; };
@ -377,6 +385,20 @@ public:
setSettled(); setSettled();
} }
void reject(const QSharedPointer<Error>& error)
{
Q_ASSERT(isPending());
Q_ASSERT(m_error.isNull());
m_error = error;
this->setSettled();
}
const QSharedPointer<Error>& error() const
{
Q_ASSERT(isRejected());
return m_error;
}
void dispatch() void dispatch()
{ {
if (isPending()) { if (isPending()) {
@ -438,6 +460,7 @@ class PromiseData : public PromiseDataBase<T, void(const T&)>
public: public:
void resolve(T&& value) void resolve(T&& value)
{ {
Q_ASSERT(this->isPending());
Q_ASSERT(m_value.isNull()); Q_ASSERT(m_value.isNull());
m_value.reset(new T(std::move(value))); m_value.reset(new T(std::move(value)));
this->setSettled(); this->setSettled();
@ -445,11 +468,26 @@ public:
void resolve(const T& value) void resolve(const T& value)
{ {
Q_ASSERT(this->isPending());
Q_ASSERT(m_value.isNull()); Q_ASSERT(m_value.isNull());
m_value.reset(new T(value)); m_value.reset(new T(value));
this->setSettled(); this->setSettled();
} }
void resolve(const QSharedPointer<T>& value)
{
Q_ASSERT(this->isPending());
Q_ASSERT(m_value.isNull());
m_value = value;
this->setSettled();
}
const QSharedPointer<T>& value() const
{
Q_ASSERT(this->isFulfilled());
return m_value;
}
void notify(const QVector<Handler>& handlers) Q_DECL_OVERRIDE void notify(const QVector<Handler>& handlers) Q_DECL_OVERRIDE
{ {
QSharedPointer<T> value(m_value); QSharedPointer<T> value(m_value);
@ -473,7 +511,10 @@ class PromiseData<void> : public PromiseDataBase<void, void()>
using Handler = typename PromiseDataBase<void, void()>::Handler; using Handler = typename PromiseDataBase<void, void()>::Handler;
public: public:
void resolve() { setSettled(); } void resolve()
{
setSettled();
}
protected: protected:
void notify(const QVector<Handler>& handlers) Q_DECL_OVERRIDE void notify(const QVector<Handler>& handlers) Q_DECL_OVERRIDE

View File

@ -14,6 +14,7 @@ private Q_SLOTS:
void valueResolve(); void valueResolve();
void valueReject(); void valueReject();
void valueThen(); void valueThen();
void valueDelayed();
void errorReject(); void errorReject();
void errorThen(); void errorThen();
@ -162,6 +163,36 @@ void tst_benchmark::valueThen()
} }
} }
void tst_benchmark::valueDelayed()
{
{ // should not copy the value on continutation if fulfilled
int value = -1;
Data::logs().reset();
QPromise<int>::resolve(42).then([&](int res) {
return QPromise<Data>::resolve(Data(res + 1));
}).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 input promise data
QCOMPARE(Data::logs().refs, 0);
QCOMPARE(value, 43);
}
{ // should not create value on continutation if rejected
Data::logs().reset();
QPromise<int>::resolve(42).then([&]() {
return QPromise<Data>::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::errorReject() void tst_benchmark::errorReject()
{ {
{ // should create one copy of the error when rejected by rvalue { // should create one copy of the error when rejected by rvalue