App: make deleting dynamic property safer

Dynamic property can be removed at any time, even during triggering of
onChanged() signal of the removing property. This patch introduced
static function Property::destroy() to make it safer by queueing any
removed property, and only deleting them when no onChanged() call is
active.
This commit is contained in:
Zheng, Lei
2020-01-01 18:03:52 +08:00
committed by wmayer
parent 9860c741c8
commit d70ddf692e
4 changed files with 46 additions and 2 deletions

View File

@@ -1010,7 +1010,7 @@ bool Document::redo(int id)
void Document::addOrRemovePropertyOfObject(TransactionalObject* obj, Property *prop, bool add)
{
if (!prop || !obj)
if (!prop || !obj || !obj->isAttachedToDocument())
return;
if(d->iUndoMode && !isPerformingTransaction() && !d->activeUndoTransaction) {
if(!testStatus(Restoring) || testStatus(Importing)) {

View File

@@ -215,7 +215,7 @@ bool DynamicProperty::removeDynamicProperty(const char* name)
throw Base::RuntimeError("property is not dynamic");
Property *prop = it->property;
GetApplication().signalRemoveDynamicProperty(*prop);
delete prop;
Property::destroy(prop);
index.erase(it);
return true;
}

View File

@@ -138,8 +138,48 @@ ObjectIdentifier Property::canonicalPath(const ObjectIdentifier &p) const
return p;
}
static std::vector<Property*> _RemovedProps;
static int _PropCleanerCounter;
struct PropertyCleaner {
PropertyCleaner(Property *p)
: prop(p)
{
++_PropCleanerCounter;
}
~PropertyCleaner() {
if(--_PropCleanerCounter)
return;
bool found = false;
while(_RemovedProps.size()) {
auto p = _RemovedProps.back();
_RemovedProps.pop_back();
if(p != prop)
delete p;
else
found = true;
}
if(found)
_RemovedProps.push_back(prop);
}
Property *prop;
};
void Property::destroy(Property *p) {
if(p) {
// Is it necessary to nullify the container? May cause crash if any
// onChanged() caller assumes a non-null container.
//
// p->setContainer(0);
_RemovedProps.push_back(p);
}
}
void Property::touch()
{
PropertyCleaner guard(this);
if (father)
father->onChanged(this);
StatusBits.set(Touched);
@@ -152,6 +192,7 @@ void Property::setReadOnly(bool readOnly)
void Property::hasSetValue(void)
{
PropertyCleaner guard(this);
if (father)
father->onChanged(this);
StatusBits.set(Touched);

View File

@@ -104,6 +104,9 @@ public:
Property();
virtual ~Property();
/// For safe deleting of a dynamic property
static void destroy(Property *p);
/** This method is used to get the size of objects
* It is not meant to have the exact size, it is more or less an estimation
* which runs fast! Is it two bytes or a GB?