App: add class TransactionLocker
To protect some critical transaction from being closed prematurely. It is currently used to protect transaction of object deletion.
This commit is contained in:
@@ -21,7 +21,9 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Interpreter.h>
|
||||
#include "Application.h"
|
||||
#include "Transactions.h"
|
||||
#include "Document.h"
|
||||
@@ -31,6 +33,9 @@ FC_LOG_LEVEL_INIT("App",true,true)
|
||||
|
||||
using namespace App;
|
||||
|
||||
static int _TransactionLock;
|
||||
static int _TransactionClosed;
|
||||
|
||||
AutoTransaction::AutoTransaction(const char *name, bool tmpName) {
|
||||
auto &app = GetApplication();
|
||||
if(name && app._activeTransactionGuard>=0) {
|
||||
@@ -125,7 +130,11 @@ int Application::setActiveTransaction(const char *name, bool persist) {
|
||||
AutoTransaction::setEnable(false);
|
||||
return 0;
|
||||
}
|
||||
}else{
|
||||
} else if (_TransactionLock) {
|
||||
if (FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))
|
||||
FC_WARN("Transaction locked, ignore new transaction '" << name << "'");
|
||||
return 0;
|
||||
} else {
|
||||
FC_LOG("set active transaction '" << name << "'");
|
||||
_activeTransactionID = 0;
|
||||
for(auto &v : DocMap)
|
||||
@@ -156,10 +165,17 @@ void Application::closeActiveTransaction(bool abort, int id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(_TransactionLock) {
|
||||
if(_TransactionClosed >= 0)
|
||||
_TransactionLock = abort?-1:1;
|
||||
FC_LOG("pending " << (abort?"abort":"close") << " transaction");
|
||||
return;
|
||||
}
|
||||
|
||||
FC_LOG("close transaction '" << _activeTransactionName << "' " << abort);
|
||||
_activeTransactionID = 0;
|
||||
|
||||
TransactionSignaller siganller(abort,false);
|
||||
TransactionSignaller signaller(abort,false);
|
||||
for(auto &v : DocMap) {
|
||||
if(v.second->getTransactionID(true) != id)
|
||||
continue;
|
||||
@@ -170,3 +186,57 @@ void Application::closeActiveTransaction(bool abort, int id) {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TransactionLocker::TransactionLocker(bool lock)
|
||||
:active(lock)
|
||||
{
|
||||
if(lock)
|
||||
++_TransactionLock;
|
||||
}
|
||||
|
||||
TransactionLocker::~TransactionLocker()
|
||||
{
|
||||
if(active) {
|
||||
try {
|
||||
activate(false);
|
||||
return;
|
||||
} catch (Base::Exception &e) {
|
||||
e.ReportException();
|
||||
} catch (Py::Exception &) {
|
||||
Base::PyException e;
|
||||
e.ReportException();
|
||||
} catch (std::exception &e) {
|
||||
FC_ERR(e.what());
|
||||
} catch (...) {
|
||||
}
|
||||
FC_ERR("Exception when unlocking transaction");
|
||||
}
|
||||
}
|
||||
|
||||
void TransactionLocker::activate(bool enable)
|
||||
{
|
||||
if(active == enable)
|
||||
return;
|
||||
|
||||
active = enable;
|
||||
if(active) {
|
||||
++_TransactionLock;
|
||||
return;
|
||||
}
|
||||
|
||||
if(--_TransactionLock != 0)
|
||||
return;
|
||||
|
||||
if(_TransactionClosed) {
|
||||
bool abort = (_TransactionClosed<0);
|
||||
_TransactionClosed = 0;
|
||||
GetApplication().closeActiveTransaction(abort);
|
||||
}
|
||||
}
|
||||
|
||||
bool TransactionLocker::isLocked() {
|
||||
return _TransactionLock > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
namespace App {
|
||||
|
||||
class Application;
|
||||
|
||||
/// Helper class to manager transaction (i.e. undo/redo)
|
||||
class AppExport AutoTransaction {
|
||||
private:
|
||||
@@ -79,6 +81,51 @@ private:
|
||||
int tid = 0;
|
||||
};
|
||||
|
||||
|
||||
/** Helper class to lock a transaction from being closed or aborted.
|
||||
*
|
||||
* The helper class is used to protect some critical transaction from being
|
||||
* closed prematurely, e.g. when deleting some object.
|
||||
*/
|
||||
class AppExport TransactionLocker {
|
||||
public:
|
||||
|
||||
/** Constructor
|
||||
* @param lock: whether to activate the lock
|
||||
*/
|
||||
TransactionLocker(bool lock=true);
|
||||
|
||||
/** Destructor
|
||||
* Unlock the transaction is this locker is active
|
||||
*/
|
||||
~TransactionLocker();
|
||||
|
||||
/** Activate or deactivate this locker
|
||||
* @param enable: whether to activate the locker
|
||||
*
|
||||
* An internal counter is used to support recursive locker. When activated,
|
||||
* the current active transaction cannot be closed or aborted. But the
|
||||
* closing call (Application::closeActiveTransaction()) will be remembered,
|
||||
* and performed when the internal lock counter reaches zero.
|
||||
*/
|
||||
void activate(bool enable);
|
||||
|
||||
/// Check if the locker is active
|
||||
bool isActive() const {return active;}
|
||||
|
||||
/// Check if transaction is being locked
|
||||
static bool isLocked();
|
||||
|
||||
friend class Application;
|
||||
|
||||
private:
|
||||
/// Private new operator to prevent heap allocation
|
||||
void* operator new(size_t size);
|
||||
|
||||
private:
|
||||
bool active;
|
||||
};
|
||||
|
||||
} // namespace App
|
||||
|
||||
#endif // APP_AUTOTRANSACTION_H
|
||||
|
||||
@@ -91,6 +91,7 @@ recompute path. Also, it enables more complicated dependencies beyond trees.
|
||||
#include <QCoreApplication>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#include "AutoTransaction.h"
|
||||
#include "Document.h"
|
||||
#include "Application.h"
|
||||
#include "DocumentObject.h"
|
||||
@@ -3764,6 +3765,8 @@ void Document::removeObject(const char* sName)
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionLocker tlock;
|
||||
|
||||
_checkTransaction(pos->second,0,__LINE__);
|
||||
|
||||
#if 0
|
||||
@@ -3865,6 +3868,8 @@ void Document::_removeObject(DocumentObject* pcObject)
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionLocker tlock;
|
||||
|
||||
// TODO Refactoring: share code with Document::removeObject() (2015-09-01, Fat-Zer)
|
||||
_checkTransaction(pcObject,0,__LINE__);
|
||||
|
||||
|
||||
@@ -1130,6 +1130,9 @@ void StdCmdDelete::activated(int iMsg)
|
||||
commitCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
App::TransactionLocker tlock;
|
||||
|
||||
Gui::getMainWindow()->setUpdatesEnabled(false);
|
||||
auto editDoc = Application::Instance->editDocument();
|
||||
ViewProviderDocumentObject *vpedit = 0;
|
||||
|
||||
Reference in New Issue
Block a user