protect document against nested recomputes
+ convert ObjectStatusLocker into a template class to make its usage more flexible + add status flag 'Recomputing' and set in Document::recompute to detect and avoid nested calls of recompute
This commit is contained in:
@@ -2007,6 +2007,11 @@ void Document::renameObjectIdentifiers(const std::map<App::ObjectIdentifier, App
|
||||
#ifdef USE_OLD_DAG
|
||||
int Document::recompute()
|
||||
{
|
||||
if (testStatus(Document::Recomputing)) {
|
||||
// this is clearly a bug in the calling instance
|
||||
throw Base::RuntimeError("Nested recomputes of a document are not allowed");
|
||||
}
|
||||
|
||||
int objectCount = 0;
|
||||
|
||||
// The 'SkipRecompute' flag can be (tmp.) set to avoid to many
|
||||
@@ -2015,6 +2020,8 @@ int Document::recompute()
|
||||
if (skip)
|
||||
return 0;
|
||||
|
||||
ObjectStatusLocker<Document::Status, Document> exe(Document::Recomputing, this);
|
||||
|
||||
// delete recompute log
|
||||
for (std::vector<App::DocumentObjectExecReturn*>::iterator it=_RecomputeLog.begin();it!=_RecomputeLog.end();++it)
|
||||
delete *it;
|
||||
|
||||
@@ -66,7 +66,8 @@ public:
|
||||
SkipRecompute = 0,
|
||||
KeepTrailingDigits = 1,
|
||||
Closable = 2,
|
||||
Restoring = 3
|
||||
Restoring = 3,
|
||||
Recomputing = 4
|
||||
};
|
||||
|
||||
/** @name Properties */
|
||||
|
||||
@@ -74,7 +74,7 @@ DocumentObject::~DocumentObject(void)
|
||||
App::DocumentObjectExecReturn *DocumentObject::recompute(void)
|
||||
{
|
||||
// set/unset the execution bit
|
||||
ObjectStatusLocker exe(App::Recompute, this);
|
||||
ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::Recompute, this);
|
||||
return this->execute();
|
||||
}
|
||||
|
||||
|
||||
@@ -295,16 +295,17 @@ private:
|
||||
bool _isInOutListRecursive(const DocumentObject *act, const DocumentObject* test, const DocumentObject* checkObj, int depth) const;
|
||||
};
|
||||
|
||||
class AppExport ObjectStatusLocker
|
||||
template<typename Status, class Object>
|
||||
class ObjectStatusLocker
|
||||
{
|
||||
public:
|
||||
ObjectStatusLocker(ObjectStatus s, DocumentObject* o) : status(s), obj(o)
|
||||
ObjectStatusLocker(Status s, Object* o) : status(s), obj(o)
|
||||
{ obj->setStatus(status, true); }
|
||||
~ObjectStatusLocker()
|
||||
{ obj->setStatus(status, false); }
|
||||
private:
|
||||
ObjectStatus status;
|
||||
DocumentObject* obj;
|
||||
Status status;
|
||||
Object* obj;
|
||||
};
|
||||
|
||||
} //namespace App
|
||||
|
||||
@@ -403,9 +403,15 @@ PyObject* DocumentPy::clearUndos(PyObject * args)
|
||||
PyObject* DocumentPy::recompute(PyObject * args)
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "")) // convert args: Python->C
|
||||
return NULL; // NULL triggers exception
|
||||
int objectCount = getDocumentPtr()->recompute();
|
||||
return Py::new_reference_to(Py::Int(objectCount));
|
||||
return NULL; // NULL triggers exception
|
||||
try {
|
||||
int objectCount = getDocumentPtr()->recompute();
|
||||
return Py::new_reference_to(Py::Int(objectCount));
|
||||
}
|
||||
catch (const Base::RuntimeError& e) {
|
||||
PyErr_SetString(PyExc_RuntimeError, e.what());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject* DocumentPy::getObject(PyObject *args)
|
||||
|
||||
@@ -63,7 +63,7 @@ bool FeaturePythonImp::execute()
|
||||
Py::Object feature = static_cast<PropertyPythonObject*>(proxy)->getValue();
|
||||
if (feature.hasAttr(std::string("execute"))) {
|
||||
if (feature.hasAttr("__object__")) {
|
||||
ObjectStatusLocker exe(App::PythonCall, object);
|
||||
ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::PythonCall, object);
|
||||
Py::Callable method(feature.getAttr(std::string("execute")));
|
||||
Py::Tuple args;
|
||||
Py::Object res = method.apply(args);
|
||||
@@ -72,7 +72,7 @@ bool FeaturePythonImp::execute()
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
ObjectStatusLocker exe(App::PythonCall, object);
|
||||
ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::PythonCall, object);
|
||||
Py::Callable method(feature.getAttr(std::string("execute")));
|
||||
Py::Tuple args(1);
|
||||
args.setItem(0, Py::Object(object->getPyObject(), true));
|
||||
|
||||
Reference in New Issue
Block a user