Gui: AutoSaver and recovery changes

* Fix AutoSaver inconsistent BinaryBrep setting

* Use temperary name when auto saving, so that it won't overwrite the
  original file, which may cause corruption when crashing in the middel
  of auto saving, especially if auto saving in a separate thread.

* Support auto recovery document containing external links

* Do not mark success after auto recover, in case the program crash
  again before the user remember to save the just recovered file. Only
  mark when user saves the document.
This commit is contained in:
Zheng, Lei
2019-07-10 09:03:31 +08:00
committed by wmayer
parent c8891be856
commit 8227103ceb
4 changed files with 109 additions and 39 deletions

View File

@@ -26,6 +26,7 @@
#ifndef _PreComp_
# include <QApplication>
# include <QFile>
# include <QDir>
# include <QRunnable>
# include <QTextStream>
# include <QThreadPool>
@@ -49,6 +50,8 @@
#include "MainWindow.h"
#include "ViewProvider.h"
FC_LOG_LEVEL_INIT("App",true,true)
using namespace Gui;
AutoSaver* AutoSaver::self = 0;
@@ -71,6 +74,15 @@ AutoSaver* AutoSaver::instance()
return self;
}
void AutoSaver::renameFile(QString dirName, QString file, QString tmpFile)
{
FC_LOG("auto saver rename " << tmpFile.toUtf8().constData()
<< " -> " << file.toUtf8().constData());
QDir dir(dirName);
dir.remove(file);
dir.rename(tmpFile,file);
}
void AutoSaver::setTimeout(int ms)
{
timeout = Base::clamp<int>(ms, 0, 3600000); // between 0 and 60 min
@@ -122,7 +134,7 @@ void AutoSaver::saveDocument(const std::string& name, AutoSaveProperty& saver)
{
Gui::WaitCursor wc;
App::Document* doc = App::GetApplication().getDocument(name.c_str());
if (doc) {
if (doc && !doc->testStatus(App::Document::PartialDoc)) {
// Set the document's current transient directory
std::string dirName = doc->TransientDir.getValue();
dirName += "/fc_recovery_files";
@@ -159,8 +171,11 @@ void AutoSaver::saveDocument(const std::string& name, AutoSaveProperty& saver)
{
if (!this->compressed) {
RecoveryWriter writer(saver);
if (hGrp->GetBool("SaveBinaryBrep", true))
writer.setMode("BinaryBrep");
// We will be using thread pool if not compressed.
// So, always force binary format because ASCII
// is not reentrant. See PropertyPartShape::SaveDocFile
writer.setMode("BinaryBrep");
writer.putNextEntry("Document.xml");
@@ -310,10 +325,11 @@ public:
, writer(dir)
{
writer.setModes(modes);
// always force binary format because ASCII
// is not reentrant. See PropertyPartShape::SaveDocFile
writer.setMode("BinaryBrep");
writer.putNextEntry(file);
dirName = QString::fromUtf8(dir);
fileName = QString::fromUtf8(file);
tmpName = QString::fromLatin1("%1.tmp%2").arg(fileName).arg(rand());
writer.putNextEntry(tmpName.toUtf8().constData());
}
virtual ~RecoveryRunnable()
{
@@ -322,11 +338,24 @@ public:
virtual void run()
{
prop->SaveDocFile(writer);
writer.close();
// We could have renamed the file in this thread. However, there is
// still chance of crash when we deleted the original and before rename
// the new file. So we ask the main thread to do it. There is still
// possibility of crash caused by thread other than the main, but
// that's the best we can do for now.
QMetaObject::invokeMethod(AutoSaver::instance(), "renameFile",
Qt::QueuedConnection, Q_ARG(QString,dirName)
,Q_ARG(QString,fileName),Q_ARG(QString,tmpName));
}
private:
App::Property* prop;
Base::FileWriter writer;
QString dirName;
QString fileName;
QString tmpName;
};
}