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);
protected:
friend struct QtPromisePrivate::PromiseFulfill<QPromise<T> >;
friend class QPromiseResolve<T>;
friend class QPromiseReject<T>;

View File

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

View File

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

View File

@ -14,6 +14,7 @@ private Q_SLOTS:
void valueResolve();
void valueReject();
void valueThen();
void valueDelayed();
void errorReject();
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()
{
{ // should create one copy of the error when rejected by rvalue