Fix for leak in QFuture

To avoid leaking when converting a QFuture<T> to a QFuture<void> we need
to have a separate ref. counter for QFuture<T>. When the last QFuture<T>
goes out of scope, we need to clean out the result data.

Task-number: QTBUG-27224

Change-Id: I965a64a11fffbb191ab979cdd030a9aafd4436c2
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@digia.com>
This commit is contained in:
Christian Strømme 2012-10-25 12:32:52 +02:00 committed by The Qt Project
parent d45cebbf4d
commit 731ba8ed08
5 changed files with 111 additions and 13 deletions

View File

@ -419,6 +419,16 @@ bool QFutureInterfaceBase::referenceCountIsOne() const
return d->refCount.load() == 1;
}
bool QFutureInterfaceBase::refT() const
{
return d->refCount.refT();
}
bool QFutureInterfaceBase::derefT() const
{
return d->refCount.derefT();
}
QFutureInterfaceBasePrivate::QFutureInterfaceBasePrivate(QFutureInterfaceBase::State initialState)
: refCount(1), m_progressValue(0), m_progressMinimum(0), m_progressMaximum(0),
state(initialState), pendingResults(0),

View File

@ -130,6 +130,8 @@ public:
protected:
bool referenceCountIsOne() const;
bool refT() const;
bool derefT() const;
public:
#ifndef QFUTURE_TEST
@ -148,13 +150,17 @@ class QFutureInterface : public QFutureInterfaceBase
public:
QFutureInterface(State initialState = NoState)
: QFutureInterfaceBase(initialState)
{ }
{
refT();
}
QFutureInterface(const QFutureInterface &other)
: QFutureInterfaceBase(other)
{ }
{
refT();
}
~QFutureInterface()
{
if (referenceCountIsOne())
if (!derefT())
resultStore().clear();
}
@ -163,7 +169,8 @@ public:
QFutureInterface &operator=(const QFutureInterface &other)
{
if (referenceCountIsOne())
other.refT();
if (!derefT())
resultStore().clear();
QFutureInterfaceBase::operator=(other);
return *this;

View File

@ -129,7 +129,31 @@ class QFutureInterfaceBasePrivate
public:
QFutureInterfaceBasePrivate(QFutureInterfaceBase::State initialState);
QAtomicInt refCount;
// When the last QFuture<T> reference is removed, we need to make
// sure that data stored in the ResultStore is cleaned out.
// Since QFutureInterfaceBasePrivate can be shared between QFuture<T>
// and QFuture<void> objects, we use a separate ref. counter
// to keep track of QFuture<T> objects.
class RefCount
{
public:
inline RefCount(int r = 0, int rt = 0)
: m_refCount(r), m_refCountT(rt) {}
// Default ref counter for QFIBP
inline bool ref() { return m_refCount.ref(); }
inline bool deref() { return m_refCount.deref(); }
inline int load() const { return m_refCount.load(); }
// Ref counter for type T
inline bool refT() { return m_refCountT.ref(); }
inline bool derefT() { return m_refCountT.deref(); }
inline int loadT() const { return m_refCountT.load(); }
private:
QAtomicInt m_refCount;
QAtomicInt m_refCountT;
};
RefCount refCount;
mutable QMutex m_mutex;
QWaitCondition waitCondition;
QList<QFutureCallOutInterface *> outputConnections;

View File

@ -43,6 +43,7 @@
#include <qdebug.h>
#include <QThread>
#include <QMutex>
#include <QtTest/QtTest>
@ -75,6 +76,7 @@ private slots:
void stlContainers();
void qFutureAssignmentLeak();
void stressTest();
void persistentResultTest();
public slots:
void throttling();
};
@ -2395,5 +2397,46 @@ void tst_QtConcurrentMap::stressTest()
}
}
struct LockedCounter
{
LockedCounter(QMutex *mutex, QAtomicInt *ai)
: mtx(mutex),
ref(ai) {}
typedef int result_type;
int operator()(int x)
{
QMutexLocker locker(mtx);
ref->ref();
return ++x;
}
QMutex *mtx;
QAtomicInt *ref;
};
// The Thread engine holds the last reference
// to the QFuture, so this should not leak
// or fail.
void tst_QtConcurrentMap::persistentResultTest()
{
QFuture<void> voidFuture;
QMutex mtx;
QAtomicInt ref;
LockedCounter lc(&mtx, &ref);
QList<int> list;
{
list << 1 << 2 << 3;
mtx.lock();
QFuture<int> future = QtConcurrent::mapped(list
,lc);
voidFuture = future;
}
QCOMPARE(ref.loadAcquire(), 0);
mtx.unlock(); // Unblock
voidFuture.waitForFinished();
QCOMPARE(ref.loadAcquire(), 3);
}
QTEST_MAIN(tst_QtConcurrentMap)
#include "tst_qtconcurrentmap.moc"

View File

@ -1251,18 +1251,32 @@ void tst_QFuture::throttling()
void tst_QFuture::voidConversions()
{
QFutureInterface<int> iface;
iface.reportStarted();
{
QFutureInterface<int> iface;
iface.reportStarted();
QFuture<int> intFuture(&iface);
QFuture<int> intFuture(&iface);
int value = 10;
iface.reportFinished(&value);
int value = 10;
iface.reportFinished(&value);
QFuture<void> voidFuture(intFuture);
voidFuture = intFuture;
QFuture<void> voidFuture(intFuture);
voidFuture = intFuture;
QVERIFY(voidFuture == intFuture);
}
QVERIFY(voidFuture == intFuture);
{
QFuture<void> voidFuture;
{
QFutureInterface<QList<int> > iface;
iface.reportStarted();
QFuture<QList<int> > listFuture(&iface);
iface.reportResult(QList<int>() << 1 << 2 << 3);
voidFuture = listFuture;
}
QCOMPARE(voidFuture.resultCount(), 0);
}
}