Add support for setSharable in RefCount

A reference count of 0 (zero) would never change. RefCount::deref to
zero would return false (resource should be freed), subsequent calls on
the same state would return true and not change state. While safe from
RefCount's side, calling deref on a reference count of zero potentially
indicated a dangling reference.

With this change, a reference count of 0 is now abused to imply a
non-sharable instance (cf. QVector::setSharable). This instance is to be
deleted upon deref(), as the data is not shared and has a single owner.

In practice, this means an (intentional) change in behaviour in that
deref'ing zero still won't change state, but will return false, turning
previous access to dangling references into double free errors.

Users of RefCount wanting to support non-sharable instances are required
to check the return of RefCount::ref() and use RefCount::isShared() to
determine whether to detach (instead of directly checking count == 1).

New functions are introduced to determine whether RefCount indicates a
"Static" (permanent, typically read-only) or "Sharable" instance and
whether the instance is currently "Shared" and requires detaching prior
to accepting modifications..

This change formalizes -1 as the value used to flag persistent,
read-only instances, no longer reserving the full negative domain. The
concrete value is part of the ABI, but not of the API. (isStatic and
Q_REFCOUNT_INITIALIZE_STATIC are part of the API, instead)

Change-Id: I9a63c844155319bef0411e02b47f9d92476afefe
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
Reviewed-by: Robin Burchell <robin+qt@viroteck.net>
This commit is contained in:
João Abecasis 2011-11-25 11:09:09 +01:00 committed by Qt by Nokia
parent 3fad9a846f
commit 9a890a519e
3 changed files with 97 additions and 8 deletions

View File

@ -56,17 +56,51 @@ namespace QtPrivate
class RefCount
{
public:
inline void ref() {
if (atomic.load() > 0)
inline bool ref() {
int count = atomic.load();
if (count == 0) // !isSharable
return false;
if (count != -1) // !isStatic
atomic.ref();
return true;
}
inline bool deref() {
if (atomic.load() <= 0)
int count = atomic.load();
if (count == 0) // !isSharable
return false;
if (count == -1) // isStatic
return true;
return atomic.deref();
}
bool setSharable(bool sharable)
{
Q_ASSERT(!isShared());
if (sharable)
return atomic.testAndSetRelaxed(0, 1);
else
return atomic.testAndSetRelaxed(1, 0);
}
bool isStatic() const
{
// Persistent object, never deleted
return atomic.load() == -1;
}
bool isSharable() const
{
// Sharable === Shared ownership.
return atomic.load() != 0;
}
bool isShared() const
{
int count = atomic.load();
return (count != 1) && (count != 0);
}
inline bool operator==(int value) const
{ return atomic.load() == value; }
inline bool operator!=(int value) const

View File

@ -86,6 +86,8 @@ public:
bool isNull() const { return d.isNull(); }
bool isEmpty() const { return this->empty(); }
bool isStatic() const { return d->ref.isStatic(); }
bool isShared() const { return d->ref.isShared(); }
bool isSharedWith(const SimpleVector &other) const { return d == other.d; }
size_t size() const { return d->size; }

View File

@ -72,13 +72,16 @@ void tst_QArrayData::referenceCounting()
QCOMPARE(int(array.ref), 1);
array.ref.ref();
QVERIFY(!array.ref.isStatic());
QVERIFY(array.ref.isSharable());
QVERIFY(array.ref.ref());
QCOMPARE(int(array.ref), 2);
QVERIFY(array.ref.deref());
QCOMPARE(int(array.ref), 1);
array.ref.ref();
QVERIFY(array.ref.ref());
QCOMPARE(int(array.ref), 2);
QVERIFY(array.ref.deref());
@ -90,13 +93,35 @@ void tst_QArrayData::referenceCounting()
// Now would be a good time to free/release allocated data
}
{
// Reference counting initialized to 0 (non-sharable)
QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 };
QCOMPARE(int(array.ref), 0);
QVERIFY(!array.ref.isStatic());
QVERIFY(!array.ref.isSharable());
QVERIFY(!array.ref.ref());
// Reference counting fails, data should be copied
QCOMPARE(int(array.ref), 0);
QVERIFY(!array.ref.deref());
QCOMPARE(int(array.ref), 0);
// Free/release data
}
{
// Reference counting initialized to -1 (static read-only data)
QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 };
QCOMPARE(int(array.ref), -1);
array.ref.ref();
QVERIFY(array.ref.isStatic());
QVERIFY(array.ref.isSharable());
QVERIFY(array.ref.ref());
QCOMPARE(int(array.ref), -1);
QVERIFY(array.ref.deref());
@ -109,11 +134,19 @@ void tst_QArrayData::sharedNullEmpty()
QArrayData *null = const_cast<QArrayData *>(&QArrayData::shared_null);
QArrayData *empty = const_cast<QArrayData *>(&QArrayData::shared_empty);
QVERIFY(null->ref.isStatic());
QVERIFY(null->ref.isSharable());
QVERIFY(null->ref.isShared());
QVERIFY(empty->ref.isStatic());
QVERIFY(empty->ref.isSharable());
QVERIFY(empty->ref.isShared());
QCOMPARE(int(null->ref), -1);
QCOMPARE(int(empty->ref), -1);
null->ref.ref();
empty->ref.ref();
QVERIFY(null->ref.ref());
QVERIFY(empty->ref.ref());
QCOMPARE(int(null->ref), -1);
QCOMPARE(int(empty->ref), -1);
@ -218,6 +251,26 @@ void tst_QArrayData::simpleVector()
QVERIFY(v7.capacity() >= size_t(10));
QVERIFY(v8.capacity() >= size_t(10));
QVERIFY(v1.isStatic());
QVERIFY(v2.isStatic());
QVERIFY(v3.isStatic());
QVERIFY(v4.isStatic());
QVERIFY(v5.isStatic());
QVERIFY(v6.isStatic());
QVERIFY(!v7.isStatic());
QVERIFY(!v8.isStatic());
QVERIFY(v1.isShared());
QVERIFY(v2.isShared());
QVERIFY(v3.isShared());
QVERIFY(v4.isShared());
QVERIFY(v5.isShared());
QVERIFY(v6.isShared());
QVERIFY(!v7.isShared());
QVERIFY((SimpleVector<int>(v7), v7.isShared()));
QVERIFY(!v7.isShared());
QVERIFY(!v8.isShared());
QVERIFY(v1.isSharedWith(v2));
QVERIFY(v1.isSharedWith(v3));
QVERIFY(!v1.isSharedWith(v4));