Moved lock-guarding abilities to separated template class.

Changed application/mainwindow to use it. Most likelly mainwindow may be
upgraded more, since lock can hold and manage memory for complex object
like QFile, also it can have stored deallocator for the object
(lambdas), so this is effectively should simple code, when you have
allocation and freening resources declared on the same screen.

--HG--
branch : develop
This commit is contained in:
Roman Telezhynskyi 2015-09-14 14:53:49 +03:00
parent 1e806ccc08
commit cf0d1981ca
10 changed files with 268 additions and 200 deletions

View File

@ -37,6 +37,7 @@
#include "../ifc/xml/vvitconverter.h"
#include "../ifc/xml/vvstconverter.h"
#include "../ifc/xml/vpatternconverter.h"
#include "../vmisc/vlockguard.h"
#include "vlitepattern.h"
#include "../qmuparser/qmudef.h"
#include "../vtools/dialogs/support/dialogeditwrongformula.h"
@ -48,9 +49,6 @@
#include <QMessageBox>
#include <QComboBox>
#include <QProcess>
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
# include <QLockFile>
#endif
#define DIALOG_MAX_FORMULA_HEIGHT 64
@ -98,10 +96,6 @@ TMainWindow::~TMainWindow()
delete m;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
delete lock; // Unlock pattern file
#endif
delete ui;
}
@ -182,18 +176,13 @@ void TMainWindow::LoadFile(const QString &path)
}
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
lock = new QLockFile(QFileInfo(path).fileName()+".lock");
lock->setStaleLockTime(0);
if (not MApplication::TryLock(lock))
{
if (lock->error() == QLockFile::LockFailedError)
VlpCreateLock(lock, QFileInfo(path).fileName()+".lock");
if (lock->GetLockError() == QLockFile::LockFailedError)
{
qCCritical(tMainWindow, "%s", tr("This file already opened in another window.").toUtf8().constData());
return;
}
}
#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
try
{
@ -839,18 +828,13 @@ void TMainWindow::ImportFromPattern()
return;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
QLockFile *lock = new QLockFile(QFileInfo(mPath).fileName()+".lock");
lock->setStaleLockTime(0);
if (not MApplication::TryLock(lock))
{
if (lock->error() == QLockFile::LockFailedError)
VLockGuard<char> tmp(QFileInfo(mPath).fileName()+".lock");
if (tmp.GetLockError() == QLockFile::LockFailedError)
{
qCCritical(tMainWindow, "%s", tr("This file already opened in another window.").toUtf8().constData());
return;
}
}
#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
#ifdef Q_OS_WIN32
qt_ntfs_permission_lookup++; // turn checking on
@ -878,8 +862,6 @@ void TMainWindow::ImportFromPattern()
qt_ntfs_permission_lookup--; // turn it off again
#endif /*Q_OS_WIN32*/
delete lock; // release a pattern file
measurements = FilterMeasurements(measurements, m->ListAll());
qint32 currentRow;

View File

@ -33,6 +33,7 @@
#include <QTableWidget>
#include "../vmisc/def.h"
#include "../vmisc/vlockguard.h"
#include "../vformat/vmeasurements.h"
namespace Ui
@ -43,9 +44,6 @@ namespace Ui
class QComboBox;
class QTableWidgetItem;
class QLabel;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
class QLockFile;
#endif
class TMainWindow : public QMainWindow
{
@ -133,10 +131,7 @@ private:
QComboBox *gradationSizes;
QComboBox *comboBoxUnits;
int formulaBaseHeight;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
QLockFile *lock;
#endif
VLockGuardPtr<char> lock;
void SetupMenu();
void InitWindow();

View File

@ -53,6 +53,8 @@
Q_LOGGING_CATEGORY(vApp, "v.application")
constexpr auto DAYS_TO_KEEP_LOGS = 3;
//---------------------------------------------------------------------------------------------------------------------
inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
@ -170,12 +172,8 @@ const QString VApplication::GistFileName = QStringLiteral("gist.json");
VApplication::VApplication(int &argc, char **argv)
: VAbstractApplication(argc, argv),
trVars(nullptr), autoSaveTimer(nullptr),
log(nullptr),
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
out(nullptr), logLock(nullptr)
#else
lockLog(),
out(nullptr)
#endif
{
VCommandLine::Reset(); // making sure will create new instance...just in case we will ever do 2 objects of VApplication
VCommandLine::Get(*this);
@ -187,16 +185,6 @@ VApplication::~VApplication()
{
qCDebug(vApp, "Application closing.");
qInstallMessageHandler(0); // Resore the message handler
delete out;
if (log != nullptr)
{
log->close();
delete log;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
delete logLock;
#endif
}
delete trVars;
VCommandLine::Reset();
}
@ -418,32 +406,25 @@ void VApplication::CreateLogDir() const
//---------------------------------------------------------------------------------------------------------------------
void VApplication::BeginLogging()
{
log = new QFile(LogPath());
if (log->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
{
out = new QTextStream(log);
qInstallMessageHandler(noisyFailureMsgHandler);
VlpCreateLock(lockLog, LogPath()+".lock", [this](){return new QFile(LogPath());});
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
logLock = new QLockFile(LogPath()+".lock");
logLock->setStaleLockTime(0);
if (TryLock(logLock))
if (lockLog->IsLocked())
{
if (lockLog->GetProtected()->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text))
{
out.reset(new QTextStream(lockLog->GetProtected().get()));
qInstallMessageHandler(noisyFailureMsgHandler);
qCDebug(vApp, "Log file %s was locked.", LogPath().toUtf8().constData());
}
else
{
qCDebug(vApp, "Failed to lock %s", LogPath().toUtf8().constData());
qCDebug(vApp, "Error type: %d", logLock->error());
qCDebug(vApp, "Error opening log file \'%s\'. All debug output redirected to console.",
LogPath().toUtf8().constData());
}
#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
}
else
{
delete log;
log = nullptr;
qCDebug(vApp, "Error opening log file \'%s\'. All debug output redirected to console.",
LogPath().toUtf8().constData());
qCDebug(vApp, "Failed to lock %s", LogPath().toUtf8().constData());
}
}
@ -459,16 +440,16 @@ void VApplication::ClearOldLogs() const
if (allFiles.isEmpty() == false)
{
qCDebug(vApp, "Clearing old logs");
for (int i = 0; i < allFiles.size(); ++i)
for (int i = 0, sz = allFiles.size(); i < sz; ++i)
{
QFileInfo info(allFiles.at(i));
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
QLockFile *lock = new QLockFile(info.absoluteFilePath() + ".lock");
if (TryLock(lock))
auto fn = allFiles.at(i);
QFileInfo info(fn);
if (info.created().daysTo(QDateTime::currentDateTime()) >= DAYS_TO_KEEP_LOGS)
{
qCDebug(vApp, "Locked file %s", info.absoluteFilePath().toUtf8().constData());
QFile oldLog(allFiles.at(i));
if (oldLog.remove())
VLockGuard<QFile> tmp(info.absoluteFilePath() + ".lock", [&fn](){return new QFile(fn);});
if (tmp.GetProtected() != nullptr)
{
if (tmp.GetProtected()->remove())
{
qCDebug(vApp, "Deleted %s", info.absoluteFilePath().toUtf8().constData());
}
@ -481,23 +462,7 @@ void VApplication::ClearOldLogs() const
{
qCDebug(vApp, "Failed to lock %s", info.absoluteFilePath().toUtf8().constData());
}
delete lock;
lock = nullptr;
#else
if (info.created().daysTo(QDateTime::currentDateTime()) >= 3)
{
QFile oldLog(allFiles.at(i));
if (oldLog.remove())
{
qCDebug(vApp, "Deleted %s", info.absoluteFilePath().toUtf8().constData());
}
else
{
qCDebug(vApp, "Could not delete %s", info.absoluteFilePath().toUtf8().constData());
}
}
#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
}
}
else
@ -614,7 +579,7 @@ void VApplication::StartLogging()
//---------------------------------------------------------------------------------------------------------------------
QTextStream *VApplication::LogFile()
{
return out;
return out.get();
}
//---------------------------------------------------------------------------------------------------------------------
@ -707,27 +672,28 @@ void VApplication::GatherLogs() const
const QStringList allFiles = logsDir.entryList(QDir::NoDotAndDotDot | QDir::Files);
if (allFiles.isEmpty() == false)
{
for (int i = 0; i < allFiles.size(); ++i)
for (int i = 0, sz = allFiles.size(); i < sz; ++i)
{
QFileInfo info(allFiles.at(i));
auto fn = allFiles.at(i);
QFileInfo info(fn);
if (info.fileName() == "valentina.log")
{
continue;
}
QLockFile *logLock = new QLockFile(info.absoluteFilePath()+".lock");
logLock->setStaleLockTime(0);
if (TryLock(logLock))
VLockGuard<QFile> tmp(info.absoluteFilePath() + ".lock", [&fn](){return new QFile(fn);});
if (tmp.IsLocked())
{
*out <<"--------------------------" << endl;
QFile logFile(info.absoluteFilePath());
if (logFile.open(QIODevice::ReadOnly | QIODevice::Text))
if (tmp.GetProtected()->open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream in(&logFile);
QTextStream in(&tmp.GetProtected());
while (!in.atEnd())
{
*out << in.readLine() << endl;
}
logFile.close();
tmp.GetProtected()->close();
}
else
{
@ -736,9 +702,8 @@ void VApplication::GatherLogs() const
}
else
{
*out << "Could not lock" << info.absoluteFilePath() << ".";
qCDebug(vApp, "Failed to lock %s", info.absoluteFilePath().toUtf8().constData());
}
delete logLock;
}
}
else

View File

@ -36,7 +36,6 @@
#include "vsettings.h"
#include "vcmdexport.h"
class VApplication;// use in define
class VMainGraphicsView;
class VPattern;
@ -95,11 +94,9 @@ private:
VTranslateVars *trVars;
QTimer *autoSaveTimer;
QFile *log;
QTextStream *out;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
QLockFile *logLock;
#endif
VLockGuardPtr<QFile> lockLog;
std::shared_ptr<QTextStream> out;
void InitLineWidth();
#if defined(Q_OS_WIN) && defined(Q_CC_GNU)

View File

@ -62,9 +62,6 @@
#include <QtGlobal>
#include <QDesktopWidget>
#include <QDesktopServices>
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
# include <QLockFile>
#endif
#include <chrono>
#include <thread>
@ -1960,13 +1957,8 @@ void MainWindow::OnlineHelp()
void MainWindow::Clear()
{
qCDebug(vMainWindow, "Reseting main window.");
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
delete lock; // Unlock pattern file
lock = nullptr;
lock.reset();
qCDebug(vMainWindow, "Unlocked pattern file.");
#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
ui->actionDetails->setChecked(true);
ui->actionDraw->setChecked(true);
ui->actionLayout->setEnabled(true);
@ -3034,9 +3026,6 @@ MainWindow::~MainWindow()
CancelTool();
CleanLayout();
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
delete lock; // Unlock pattern file
#endif
delete doc;
delete sceneDetails;
delete sceneDraw;
@ -3065,26 +3054,24 @@ bool MainWindow::LoadPattern(const QString &fileName, const QString& customMeasu
return false;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
qCDebug(vMainWindow, "Loking file");
lock = new QLockFile(fileName+".lock");
lock->setStaleLockTime(0);
if (VApplication::TryLock(lock))
VlpCreateLock(lock, fileName+".lock");
if (lock->IsLocked())
{
qCDebug(vMainWindow, "Pattern file %s was locked.", fileName.toUtf8().constData());
}
else
{
qCDebug(vMainWindow, "Failed to lock %s", fileName.toUtf8().constData());
qCDebug(vMainWindow, "Error type: %d", lock->error());
if (lock->error() == QLockFile::LockFailedError)
qCDebug(vMainWindow, "Error type: %d", lock->GetLockError());
if (lock->GetLockError() == QLockFile::LockFailedError)
{
qCCritical(vMainWindow, "%s", tr("This file already opened in another window.").toUtf8().constData());
Clear();
return false;
}
}
#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
// On this stage scene empty. Fit scene size to view size
VMainGraphicsView::NewSceneRect(sceneDraw, ui->view);
@ -3187,14 +3174,11 @@ QStringList MainWindow::GetUnlokedRestoreFileList() const
for (int i = 0; i < files.size(); ++i)
{
// Seeking file that realy need reopen
QLockFile *lock = new QLockFile(files.at(i)+".lock");
lock->setStaleLockTime(0);
if (VApplication::TryLock(lock))
VLockGuard<char> tmp(files.at(i)+".lock");
if (tmp.IsLocked())
{
restoreFiles.append(files.at(i));
}
delete lock;
lock = nullptr;
}
// Clearing list after filtering
@ -3204,7 +3188,6 @@ QStringList MainWindow::GetUnlokedRestoreFileList() const
}
qApp->ValentinaSettings()->SetRestoreFileList(files);
}
return restoreFiles;
#else

View File

@ -37,7 +37,7 @@
#include "tools/vtooluniondetails.h"
#include "tools/drawTools/drawtools.h"
#include "core/vcmdexport.h"
#include <QLockFile>
#include "../vmisc/vlockguard.h"
#include <QFileSystemWatcher>
@ -47,9 +47,6 @@ namespace Ui
}
class VToolOptionsPropertyBrowser;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
class QLockFile;
#endif
/**
* @brief The MainWindow class main windows.
@ -230,9 +227,7 @@ private:
QComboBox *gradationHeights;
QComboBox *gradationSizes;
VToolOptionsPropertyBrowser *toolOptions;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
QLockFile *lock;
#endif
VLockGuardPtr<char> lock;
void ToolBarOption();
void ToolBarStages();

View File

@ -29,10 +29,6 @@
#include "vabstractapplication.h"
#include "../vmisc/def.h"
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
# include <QLockFile>
#endif
//---------------------------------------------------------------------------------------------------------------------
VAbstractApplication::VAbstractApplication(int &argc, char **argv)
:QApplication(argc, argv),
@ -116,43 +112,3 @@ double VAbstractApplication::fromPixel(double pix) const
{
return FromPixel(pix, _patternUnit);
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
//---------------------------------------------------------------------------------------------------------------------
bool VAbstractApplication::TryLock(QLockFile *lock)
{
if (lock == nullptr)
{
return false;
}
if (lock->tryLock())
{
return true;
}
else
{
if (lock->error() == QLockFile::LockFailedError)
{
// This happens if a stale lock file exists and another process uses that PID.
// Try removing the stale file, which will fail if a real process is holding a
// file-level lock. A false error is more problematic than not locking properly
// on corner-case systems.
if (lock->removeStaleLockFile() == false || lock->tryLock() == false)
{
return false;
}
else
{
return true;
}
}
else
{
return false;
}
return false;
}
}
#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)

View File

@ -33,15 +33,13 @@
#include <QGraphicsScene>
#include "def.h"
#include "vsettings.h"
#include "vlockguard.h"
class VAbstractApplication;// use in define
class VTranslateVars;
class VAbstractPattern;
class VMainGraphicsView;
class QUndoStack;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
class QLockFile;
#endif
#if defined(qApp)
#undef qApp
@ -89,10 +87,6 @@ public:
QUndoStack *getUndoStack() const;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
static bool TryLock(QLockFile *lock);
#endif //QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
protected:
QUndoStack *undoStack;

200
src/libs/vmisc/vlockguard.h Normal file
View File

@ -0,0 +1,200 @@
/************************************************************************
**
** @file VLockGuard.h
** @author Alex Zaharov <alexzkhr@gmail.com>
** @author Roman Telezhynskyi <dismine(at)gmail.com>
** @date 14 9, 2015
**
** @brief
** @copyright
** This source code is part of the Valentine project, a pattern making
** program, whose allow create and modeling patterns of clothing.
** Copyright (C) 2015 Valentina project
** <https://bitbucket.org/dismine/valentina> All Rights Reserved.
**
** Valentina is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** Valentina is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
**
*************************************************************************/
#ifndef VLOCKGUARD_H
#define VLOCKGUARD_H
#include <QString>
#include <stdint.h>
#include <memory>
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
#include <QLockFile>
#define PEDANT_COMPILER ,lock(nullptr)
#else
#define PEDANT_COMPILER
#warning To have lock-file support you must use Qt 5.1+. Expect collissions when run 2 copies of the program.
#endif
/*@brief
* This class creates Guarded object if and only if lock file taken. It keeps shared_ptr to object and lock-file.
* Can use optional object allocator and deleter.
*
* On older Qt lock assumed always taken and compile-time warning is shown.
*
*/
template <typename Guarded>
class VLockGuard
{
public:
VLockGuard(const QString& lockName, int stale = 0, int timeout = 0);
template <typename Alloc>
VLockGuard(const QString& lockName, Alloc a, int stale = 0, int timeout=0);
template <typename Alloc, typename Delete>
VLockGuard(const QString& lockName, Alloc a, Delete d, int stale = 0, int timeout=0);
const std::shared_ptr<Guarded> &GetProtected() const;
int GetLockError() const;
bool IsLocked() const;
private:
Q_DISABLE_COPY(VLockGuard<Guarded>)
std::shared_ptr<Guarded> holder;
int lockError;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
std::shared_ptr<QLockFile> lock;
#endif
bool TryLock(const QString &lockName, int stale, int timeout);
};
//---------------------------------------------------------------------------------------------------------------------
template <typename Guarded>
VLockGuard<Guarded>::VLockGuard(const QString &lockName, int stale, int timeout)
: holder(nullptr), lockError(0) PEDANT_COMPILER
{
if (TryLock(lockName, stale, timeout))
{
holder.reset(new Guarded());
}
}
//---------------------------------------------------------------------------------------------------------------------
//using allocator lambdas seems logically better than supplying pointer, because we will take ownership of allocated
//object
template <typename Guarded> template <typename Alloc>
VLockGuard<Guarded>::VLockGuard(const QString& lockName, Alloc a, int stale, int timeout)
: holder(nullptr), lockError(0) PEDANT_COMPILER
{
if (TryLock(lockName, stale, timeout))
{
holder.reset(a());
}
}
//---------------------------------------------------------------------------------------------------------------------
template <typename Guarded> template <typename Alloc, typename Delete>
VLockGuard<Guarded>::VLockGuard(const QString& lockName, Alloc a, Delete d, int stale, int timeout)
: holder(nullptr), lockError(0) PEDANT_COMPILER
{
if (TryLock(lockName, stale, timeout))
{
holder.reset(a(), d);
}
}
//---------------------------------------------------------------------------------------------------------------------
template <typename Guarded>
const std::shared_ptr<Guarded> &VLockGuard<Guarded>::GetProtected() const
{
return holder;
}
//---------------------------------------------------------------------------------------------------------------------
template <typename Guarded>
int VLockGuard<Guarded>::GetLockError() const
{
return lockError;
}
//---------------------------------------------------------------------------------------------------------------------
template <typename Guarded>
bool VLockGuard<Guarded>::IsLocked() const
{
return holder != nullptr;
}
//---------------------------------------------------------------------------------------------------------------------
template <typename Guarded>
bool VLockGuard<Guarded>::TryLock(const QString &lockName, int stale, int timeout)
{
bool res = true;
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)
lock.reset(new QLockFile(lockName));
lock->setStaleLockTime(stale);
for (int i = 0; i < 2 && !lock->tryLock(timeout); i++)
{
if (QLockFile::LockFailedError != lock->error())
{
break;
}
else
{
// This happens if a stale lock file exists and another process uses that PID.
// Try removing the stale file, which will fail if a real process is holding a
// file-level lock. A false error is more problematic than not locking properly
// on corner-case systems.
lock->removeStaleLockFile();
lock->tryLock(timeout);
}
}
res = QLockFile::NoError == (lockError = lock->error());
if (!res)
{
lock.reset();
}
#endif
return res;
}
#undef PEDANT_COMPILER
//use pointer and function below to persistent things like class-member, because lock is taken by constructor
//helper functions allow to write shorter creating and setting new lock-pointer
//new C++11 - "template typedef" http://stackoverflow.com/questions/2795023/c-template-typedef
template <typename Guarded>
using VLockGuardPtr = std::shared_ptr<VLockGuard<Guarded>>;
template <typename Guarded>
void VlpCreateLock(VLockGuardPtr<Guarded>& r, const QString& lockName, int stale = 0, int timeout = 0)
{
r.reset(new VLockGuard<Guarded>(lockName, stale, timeout));
}
template <typename Guarded, typename Alloc>
void VlpCreateLock(VLockGuardPtr<Guarded>& r, const QString& lockName, Alloc a, int stale = 0, int timeout = 0)
{
r.reset(new VLockGuard<Guarded>(lockName, a, stale, timeout));
}
template <typename Guarded, typename Alloc, typename Del>
void VlpCreateLock(VLockGuardPtr<Guarded>& r, const QString& lockName, Alloc a, Del d, int stale = 0, int timeout = 0)
{
r.reset(new VLockGuard<Guarded>(lockName, a, d, stale, timeout));
}
#endif // VLOCKGUARD_H

View File

@ -26,4 +26,5 @@ HEADERS += \
$$PWD/projectversion.h \
$$PWD/vcommonsettings.h \
$$PWD/vtapesettings.h \
debugbreak.h
$$PWD/debugbreak.h \
$$PWD/vlockguard.h