Distinguish between "hard" and "soft" references to shared objects. Sending

bitstreams use soft references and remove their mappings when only soft
references remain.
This commit is contained in:
Andrzej Kapolka 2014-03-02 17:40:58 -08:00
parent 7992d2796e
commit f910c4471b
4 changed files with 134 additions and 87 deletions

View file

@ -167,11 +167,18 @@ void Bitstream::persistWriteMappings(const WriteMappings& mappings) {
_scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets); _scriptStringStreamer.persistTransientOffsets(mappings.scriptStringOffsets);
_sharedObjectStreamer.persistTransientOffsets(mappings.sharedObjectOffsets); _sharedObjectStreamer.persistTransientOffsets(mappings.sharedObjectOffsets);
// find out when shared objects' reference counts drop to one in order to clear their mappings // find out when shared objects are deleted in order to clear their mappings
for (QHash<SharedObjectPointer, int>::const_iterator it = mappings.sharedObjectOffsets.constBegin(); for (QHash<SoftSharedObjectPointer, int>::const_iterator it = mappings.sharedObjectOffsets.constBegin();
it != mappings.sharedObjectOffsets.constEnd(); it++) { it != mappings.sharedObjectOffsets.constEnd(); it++) {
if (it.key()) { if (it.key()) {
connect(it.key().data(), SIGNAL(referenceCountDroppedToOne()), SLOT(clearSharedObject())); if (it.key()->getHardReferenceCount() > 0) {
connect(it.key().data(), SIGNAL(allHardReferencesCleared(QObject*)),
SLOT(clearSharedObject(QObject*)));
} else {
// invoke on a queued connection so as to run after any other mappings are persisted
QMetaObject::invokeMethod(this, "clearSharedObject", Qt::QueuedConnection,
Q_ARG(QObject*, it.key().data()));
}
} }
} }
} }
@ -465,7 +472,7 @@ Bitstream& Bitstream::operator>(QScriptString& string) {
return *this; return *this;
} }
Bitstream& Bitstream::operator<(const SharedObjectPointer& object) { Bitstream& Bitstream::operator<(const SoftSharedObjectPointer& object) {
if (!object) { if (!object) {
return *this << (int)0; return *this << (int)0;
} }
@ -497,10 +504,12 @@ Bitstream& Bitstream::operator>(SharedObjectPointer& object) {
return *this; return *this;
} }
void Bitstream::clearSharedObject() { void Bitstream::clearSharedObject(QObject* object) {
SharedObjectPointer object(static_cast<SharedObject*>(sender()));
object->disconnect(this); object->disconnect(this);
emit sharedObjectCleared(_sharedObjectStreamer.takePersistentID(object)); int id = _sharedObjectStreamer.takePersistentID(static_cast<SharedObject*>(object));
if (id != 0) {
emit sharedObjectCleared(id);
}
} }
void Bitstream::readProperties(QObject* object) { void Bitstream::readProperties(QObject* object) {

View file

@ -55,26 +55,26 @@ private:
/// Provides a means to stream repeated values efficiently. The value is first streamed along with a unique ID. When /// Provides a means to stream repeated values efficiently. The value is first streamed along with a unique ID. When
/// subsequently streamed, only the ID is sent. /// subsequently streamed, only the ID is sent.
template<class T> class RepeatedValueStreamer { template<class K, class V = K> class RepeatedValueStreamer {
public: public:
RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream), RepeatedValueStreamer(Bitstream& stream) : _stream(stream), _idStreamer(stream),
_lastPersistentID(0), _lastTransientOffset(0) { } _lastPersistentID(0), _lastTransientOffset(0) { }
QHash<T, int> getAndResetTransientOffsets(); QHash<K, int> getAndResetTransientOffsets();
void persistTransientOffsets(const QHash<T, int>& transientOffsets); void persistTransientOffsets(const QHash<K, int>& transientOffsets);
QHash<int, T> getAndResetTransientValues(); QHash<int, V> getAndResetTransientValues();
void persistTransientValues(const QHash<int, T>& transientValues); void persistTransientValues(const QHash<int, V>& transientValues);
int takePersistentID(T value) { return _persistentIDs.take(value); } int takePersistentID(K value) { return _persistentIDs.take(value); }
void removePersistentValue(int id) { _persistentValues.remove(id); } void removePersistentValue(int id) { _persistentValues.remove(id); }
RepeatedValueStreamer& operator<<(T value); RepeatedValueStreamer& operator<<(K value);
RepeatedValueStreamer& operator>>(T& value); RepeatedValueStreamer& operator>>(V& value);
private: private:
@ -82,23 +82,24 @@ private:
IDStreamer _idStreamer; IDStreamer _idStreamer;
int _lastPersistentID; int _lastPersistentID;
int _lastTransientOffset; int _lastTransientOffset;
QHash<T, int> _persistentIDs; QHash<K, int> _persistentIDs;
QHash<T, int> _transientOffsets; QHash<K, int> _transientOffsets;
QHash<int, T> _persistentValues; QHash<int, V> _persistentValues;
QHash<int, T> _transientValues; QHash<int, V> _transientValues;
}; };
template<class T> inline QHash<T, int> RepeatedValueStreamer<T>::getAndResetTransientOffsets() { template<class K, class V> inline QHash<K, int> RepeatedValueStreamer<K, V>::getAndResetTransientOffsets() {
QHash<T, int> transientOffsets; QHash<K, int> transientOffsets;
_transientOffsets.swap(transientOffsets); _transientOffsets.swap(transientOffsets);
_lastTransientOffset = 0; _lastTransientOffset = 0;
_idStreamer.setBitsFromValue(_lastPersistentID); _idStreamer.setBitsFromValue(_lastPersistentID);
return transientOffsets; return transientOffsets;
} }
template<class T> inline void RepeatedValueStreamer<T>::persistTransientOffsets(const QHash<T, int>& transientOffsets) { template<class K, class V> inline void RepeatedValueStreamer<K, V>::persistTransientOffsets(
const QHash<K, int>& transientOffsets) {
int oldLastPersistentID = _lastPersistentID; int oldLastPersistentID = _lastPersistentID;
for (typename QHash<T, int>::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) { for (typename QHash<K, int>::const_iterator it = transientOffsets.constBegin(); it != transientOffsets.constEnd(); it++) {
int& id = _persistentIDs[it.key()]; int& id = _persistentIDs[it.key()];
if (id == 0) { if (id == 0) {
id = oldLastPersistentID + it.value(); id = oldLastPersistentID + it.value();
@ -108,16 +109,17 @@ template<class T> inline void RepeatedValueStreamer<T>::persistTransientOffsets(
_idStreamer.setBitsFromValue(_lastPersistentID); _idStreamer.setBitsFromValue(_lastPersistentID);
} }
template<class T> inline QHash<int, T> RepeatedValueStreamer<T>::getAndResetTransientValues() { template<class K, class V> inline QHash<int, V> RepeatedValueStreamer<K, V>::getAndResetTransientValues() {
QHash<int, T> transientValues; QHash<int, V> transientValues;
_transientValues.swap(transientValues); _transientValues.swap(transientValues);
_idStreamer.setBitsFromValue(_lastPersistentID); _idStreamer.setBitsFromValue(_lastPersistentID);
return transientValues; return transientValues;
} }
template<class T> inline void RepeatedValueStreamer<T>::persistTransientValues(const QHash<int, T>& transientValues) { template<class K, class V> inline void RepeatedValueStreamer<K, V>::persistTransientValues(
const QHash<int, V>& transientValues) {
int oldLastPersistentID = _lastPersistentID; int oldLastPersistentID = _lastPersistentID;
for (typename QHash<int, T>::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) { for (typename QHash<int, V>::const_iterator it = transientValues.constBegin(); it != transientValues.constEnd(); it++) {
int& id = _persistentIDs[it.value()]; int& id = _persistentIDs[it.value()];
if (id == 0) { if (id == 0) {
id = oldLastPersistentID + it.key(); id = oldLastPersistentID + it.key();
@ -128,7 +130,7 @@ template<class T> inline void RepeatedValueStreamer<T>::persistTransientValues(c
_idStreamer.setBitsFromValue(_lastPersistentID); _idStreamer.setBitsFromValue(_lastPersistentID);
} }
template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::operator<<(T value) { template<class K, class V> inline RepeatedValueStreamer<K, V>& RepeatedValueStreamer<K, V>::operator<<(K value) {
int id = _persistentIDs.value(value); int id = _persistentIDs.value(value);
if (id == 0) { if (id == 0) {
int& offset = _transientOffsets[value]; int& offset = _transientOffsets[value];
@ -145,7 +147,7 @@ template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::ope
return *this; return *this;
} }
template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::operator>>(T& value) { template<class K, class V> inline RepeatedValueStreamer<K, V>& RepeatedValueStreamer<K, V>::operator>>(V& value) {
int id; int id;
_idStreamer >> id; _idStreamer >> id;
if (id <= _lastPersistentID) { if (id <= _lastPersistentID) {
@ -153,7 +155,7 @@ template<class T> inline RepeatedValueStreamer<T>& RepeatedValueStreamer<T>::ope
} else { } else {
int offset = id - _lastPersistentID; int offset = id - _lastPersistentID;
typename QHash<int, T>::iterator it = _transientValues.find(offset); typename QHash<int, V>::iterator it = _transientValues.find(offset);
if (it == _transientValues.end()) { if (it == _transientValues.end()) {
_stream > value; _stream > value;
_transientValues.insert(offset, value); _transientValues.insert(offset, value);
@ -177,7 +179,7 @@ public:
QHash<const TypeStreamer*, int> typeStreamerOffsets; QHash<const TypeStreamer*, int> typeStreamerOffsets;
QHash<AttributePointer, int> attributeOffsets; QHash<AttributePointer, int> attributeOffsets;
QHash<QScriptString, int> scriptStringOffsets; QHash<QScriptString, int> scriptStringOffsets;
QHash<SharedObjectPointer, int> sharedObjectOffsets; QHash<SoftSharedObjectPointer, int> sharedObjectOffsets;
}; };
class ReadMappings { class ReadMappings {
@ -306,7 +308,7 @@ public:
Bitstream& operator<(const QScriptString& string); Bitstream& operator<(const QScriptString& string);
Bitstream& operator>(QScriptString& string); Bitstream& operator>(QScriptString& string);
Bitstream& operator<(const SharedObjectPointer& object); Bitstream& operator<(const SoftSharedObjectPointer& object);
Bitstream& operator>(SharedObjectPointer& object); Bitstream& operator>(SharedObjectPointer& object);
signals: signals:
@ -315,7 +317,7 @@ signals:
private slots: private slots:
void clearSharedObject(); void clearSharedObject(QObject* object);
private: private:
@ -329,7 +331,7 @@ private:
RepeatedValueStreamer<const TypeStreamer*> _typeStreamerStreamer; RepeatedValueStreamer<const TypeStreamer*> _typeStreamerStreamer;
RepeatedValueStreamer<AttributePointer> _attributeStreamer; RepeatedValueStreamer<AttributePointer> _attributeStreamer;
RepeatedValueStreamer<QScriptString> _scriptStringStreamer; RepeatedValueStreamer<QScriptString> _scriptStringStreamer;
RepeatedValueStreamer<SharedObjectPointer> _sharedObjectStreamer; RepeatedValueStreamer<SoftSharedObjectPointer, SharedObjectPointer> _sharedObjectStreamer;
QHash<int, QPointer<SharedObject> > _transientSharedObjects; QHash<int, QPointer<SharedObject> > _transientSharedObjects;

View file

@ -18,19 +18,30 @@
REGISTER_META_OBJECT(SharedObject) REGISTER_META_OBJECT(SharedObject)
SharedObject::SharedObject() : _id(++_lastID), _referenceCount(0) { static int sharedObjectPointerMetaTypeId = qRegisterMetaType<SharedObjectPointer>();
static int softSharedObjectPointerMetaTypeId = qRegisterMetaType<SoftSharedObjectPointer>();
SharedObject::SharedObject() :
_id(++_lastID),
_hardReferenceCount(0),
_softReferenceCount(0) {
} }
void SharedObject::incrementReferenceCount() { void SharedObject::decrementHardReferenceCount() {
_referenceCount++; _hardReferenceCount--;
if (_hardReferenceCount == 0) {
if (_softReferenceCount == 0) {
delete this;
} else {
emit allHardReferencesCleared(this);
}
}
} }
void SharedObject::decrementReferenceCount() { void SharedObject::decrementSoftReferenceCount() {
if (--_referenceCount == 0) { _softReferenceCount--;
if (_hardReferenceCount == 0 && _softReferenceCount == 0) {
delete this; delete this;
} else if (_referenceCount == 1) {
emit referenceCountDroppedToOne();
} }
} }

View file

@ -27,10 +27,14 @@ public:
int getID() { return _id; } int getID() { return _id; }
int getReferenceCount() const { return _referenceCount; } int getHardReferenceCount() const { return _hardReferenceCount; }
void incrementReferenceCount();
void decrementReferenceCount(); void incrementHardReferenceCount() { _hardReferenceCount++; }
void decrementHardReferenceCount();
void incrementSoftReferenceCount() { _softReferenceCount++; }
void decrementSoftReferenceCount();
/// Creates a new clone of this object. /// Creates a new clone of this object.
virtual SharedObject* clone() const; virtual SharedObject* clone() const;
@ -42,23 +46,29 @@ public:
signals: signals:
/// Emitted when the reference count drops to one. /// Emitted when only soft reference counts remain.
void referenceCountDroppedToOne(); void allHardReferencesCleared(QObject* object);
private: private:
int _id; int _id;
int _referenceCount; int _hardReferenceCount;
int _softReferenceCount;
static int _lastID; static int _lastID;
}; };
typedef void (SharedObject::*SharedObjectFn)();
/// A pointer to a shared object. /// A pointer to a shared object.
template<class T> class SharedObjectPointerTemplate { template<class T, SharedObjectFn Inc = &SharedObject::incrementHardReferenceCount,
SharedObjectFn Dec = &SharedObject::decrementHardReferenceCount> class SharedObjectPointerTemplate {
public: public:
SharedObjectPointerTemplate(T* data = NULL); SharedObjectPointerTemplate(T* data = NULL);
SharedObjectPointerTemplate(const SharedObjectPointerTemplate<T>& other); SharedObjectPointerTemplate(const SharedObjectPointerTemplate<T, Inc, Dec>& other);
template<class X, SharedObjectFn Inc2, SharedObjectFn Dec2> SharedObjectPointerTemplate(
const SharedObjectPointerTemplate<X, Inc2, Dec2>& other);
~SharedObjectPointerTemplate(); ~SharedObjectPointerTemplate();
T* data() const { return _data; } T* data() const { return _data; }
@ -66,7 +76,7 @@ public:
/// "Detaches" this object, making a new copy if its reference count is greater than one. /// "Detaches" this object, making a new copy if its reference count is greater than one.
bool detach(); bool detach();
void swap(SharedObjectPointerTemplate<T>& other) { qSwap(_data, other._data); } void swap(SharedObjectPointerTemplate<T, Inc, Dec>& other) { qSwap(_data, other._data); }
void reset(); void reset();
@ -75,81 +85,91 @@ public:
T& operator*() const { return *_data; } T& operator*() const { return *_data; }
T* operator->() const { return _data; } T* operator->() const { return _data; }
template<class X> SharedObjectPointerTemplate<X> staticCast() const; template<class X> SharedObjectPointerTemplate<X, Inc, Dec> staticCast() const;
SharedObjectPointerTemplate<T>& operator=(T* data); SharedObjectPointerTemplate<T, Inc, Dec>& operator=(T* data);
SharedObjectPointerTemplate<T>& operator=(const SharedObjectPointerTemplate<T>& other); SharedObjectPointerTemplate<T, Inc, Dec>& operator=(const SharedObjectPointerTemplate<T, Inc, Dec>& other) {
return *this = other.data(); }
template<class X, SharedObjectFn Inc2, SharedObjectFn Dec2> SharedObjectPointerTemplate<T, Inc, Dec>& operator=(
const SharedObjectPointerTemplate<X, Inc2, Dec2>& other) { return *this = other.data(); }
bool operator==(const SharedObjectPointerTemplate<T>& other) const { return _data == other._data; } bool operator==(T* data) const { return _data == data; }
bool operator!=(const SharedObjectPointerTemplate<T>& other) const { return _data != other._data; } bool operator!=(T* data) const { return _data != data; }
template<class X, SharedObjectFn Inc2, SharedObjectFn Dec2> bool operator==(
const SharedObjectPointerTemplate<X, Inc2, Dec2>& other) const { return _data == other.data(); }
template<class X, SharedObjectFn Inc2, SharedObjectFn Dec2> bool operator!=(
const SharedObjectPointerTemplate<X, Inc2, Dec2>& other) const { return _data != other.data(); }
private: private:
T* _data; T* _data;
}; };
template<class T> inline SharedObjectPointerTemplate<T>::SharedObjectPointerTemplate(T* data) : _data(data) { template<class T, SharedObjectFn Inc, SharedObjectFn Dec> inline
SharedObjectPointerTemplate<T, Inc, Dec>::SharedObjectPointerTemplate(T* data) : _data(data) {
if (_data) { if (_data) {
_data->incrementReferenceCount(); (_data->*Inc)();
} }
} }
template<class T> inline SharedObjectPointerTemplate<T>::SharedObjectPointerTemplate(const SharedObjectPointerTemplate<T>& other) : template<class T, SharedObjectFn Inc, SharedObjectFn Dec> inline
_data(other._data) { SharedObjectPointerTemplate<T, Inc, Dec>::SharedObjectPointerTemplate(
const SharedObjectPointerTemplate<T, Inc, Dec>& other) : _data(other.data()) {
if (_data) { if (_data) {
_data->incrementReferenceCount(); (_data->*Inc)();
} }
} }
template<class T> inline SharedObjectPointerTemplate<T>::~SharedObjectPointerTemplate() { template<class T, SharedObjectFn Inc, SharedObjectFn Dec> template<class X, SharedObjectFn Inc2, SharedObjectFn Dec2> inline
SharedObjectPointerTemplate<T, Inc, Dec>::SharedObjectPointerTemplate(
const SharedObjectPointerTemplate<X, Inc2, Dec2>& other) : _data(other.data()) {
if (_data) { if (_data) {
_data->decrementReferenceCount(); (_data->*Inc)();
} }
} }
template<class T> inline bool SharedObjectPointerTemplate<T>::detach() { template<class T, SharedObjectFn Inc, SharedObjectFn Dec> inline
if (_data && _data->getReferenceCount() > 1) { SharedObjectPointerTemplate<T, Inc, Dec>::~SharedObjectPointerTemplate() {
_data->decrementReferenceCount(); if (_data) {
(_data = _data->clone())->incrementReferenceCount(); (_data->*Dec)();
}
}
template<class T, SharedObjectFn Inc, SharedObjectFn Dec> inline bool SharedObjectPointerTemplate<T, Inc, Dec>::detach() {
if (_data && _data->getHardReferenceCount() > 1) {
(_data->*Dec)();
((_data = _data->clone())->*Inc)();
return true; return true;
} }
return false; return false;
} }
template<class T> inline void SharedObjectPointerTemplate<T>::reset() { template<class T, SharedObjectFn Inc, SharedObjectFn Dec> inline void SharedObjectPointerTemplate<T, Inc, Dec>::reset() {
if (_data) { if (_data) {
_data->decrementReferenceCount(); (_data->*Dec)();
} }
_data = NULL; _data = NULL;
} }
template<class T> template<class X> inline SharedObjectPointerTemplate<X> SharedObjectPointerTemplate<T>::staticCast() const { template<class T, SharedObjectFn Inc, SharedObjectFn Dec> template<class X> inline
return SharedObjectPointerTemplate<X>(static_cast<X*>(_data)); SharedObjectPointerTemplate<X, Inc, Dec> SharedObjectPointerTemplate<T, Inc, Dec>::staticCast() const {
return SharedObjectPointerTemplate<X, Inc, Dec>(static_cast<X*>(_data));
} }
template<class T> inline SharedObjectPointerTemplate<T>& SharedObjectPointerTemplate<T>::operator=(T* data) { template<class T, SharedObjectFn Inc, SharedObjectFn Dec> inline
SharedObjectPointerTemplate<T, Inc, Dec>& SharedObjectPointerTemplate<T, Inc, Dec>::operator=(T* data) {
if (_data) { if (_data) {
_data->decrementReferenceCount(); (_data->*Dec)();
} }
if ((_data = data)) { if ((_data = data)) {
_data->incrementReferenceCount(); (_data->*Inc)();
} }
return *this; return *this;
} }
template<class T> inline SharedObjectPointerTemplate<T>& SharedObjectPointerTemplate<T>::operator=( template<class T, SharedObjectFn Inc, SharedObjectFn Dec> uint qHash(
const SharedObjectPointerTemplate<T>& other) { const SharedObjectPointerTemplate<T, Inc, Dec>& pointer, uint seed = 0) {
if (_data) {
_data->decrementReferenceCount();
}
if ((_data = other._data)) {
_data->incrementReferenceCount();
}
return *this;
}
template<class T> uint qHash(const SharedObjectPointerTemplate<T>& pointer, uint seed = 0) {
return qHash(pointer.data(), seed); return qHash(pointer.data(), seed);
} }
@ -161,6 +181,11 @@ typedef QSet<SharedObjectPointer> SharedObjectSet;
Q_DECLARE_METATYPE(SharedObjectSet) Q_DECLARE_METATYPE(SharedObjectSet)
typedef SharedObjectPointerTemplate<SharedObject, &SharedObject::incrementSoftReferenceCount,
&SharedObject::decrementSoftReferenceCount> SoftSharedObjectPointer;
Q_DECLARE_METATYPE(SoftSharedObjectPointer)
/// Allows editing shared object instances. /// Allows editing shared object instances.
class SharedObjectEditor : public QWidget { class SharedObjectEditor : public QWidget {
Q_OBJECT Q_OBJECT