/************************************************************************ ** ** @file VLockGuard.h ** @author Alex Zaharov ** @author Roman Telezhynskyi ** @date 14 9, 2015 ** ** @brief ** @copyright ** This source code is part of the Valentina project, a pattern making ** program, whose allow create and modeling patterns of clothing. ** Copyright (C) 2015 Valentina project ** 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 . ** *************************************************************************/ #ifndef VLOCKGUARD_H #define VLOCKGUARD_H #include #ifdef Q_OS_WIN #include #endif /*Q_OS_WIN*/ #include #include #include #include #include /*@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 class VLockGuard { public: explicit VLockGuard(const QString &lockName, int stale = 0, int timeout = 0); ~VLockGuard() = default; template VLockGuard(const QString &lockName, Alloc a, int stale = 0, int timeout = 0); template VLockGuard(const QString &lockName, Alloc a, Delete d, int stale = 0, int timeout = 0); auto GetProtected() const -> const QSharedPointer &; auto GetLockError() const -> int; auto IsLocked() const -> bool; void Unlock(); auto GetLockFile() const -> QString; private: // cppcheck-suppress unknownMacro Q_DISABLE_COPY_MOVE(VLockGuard) // NOLINT QSharedPointer holder{}; int lockError{0}; QString lockFile{}; QSharedPointer lock{}; auto TryLock(const QString &lockName, int stale, int timeout) -> bool; }; //--------------------------------------------------------------------------------------------------------------------- template VLockGuard::VLockGuard(const QString &lockName, int stale, int timeout) { 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 template VLockGuard::VLockGuard(const QString &lockName, Alloc a, int stale, int timeout) { if (TryLock(lockName, stale, timeout)) { holder.reset(a()); } } //--------------------------------------------------------------------------------------------------------------------- template template VLockGuard::VLockGuard(const QString &lockName, Alloc a, Delete d, int stale, int timeout) { if (TryLock(lockName, stale, timeout)) { holder.reset(a(), d); } } //--------------------------------------------------------------------------------------------------------------------- template inline auto VLockGuard::GetProtected() const -> const QSharedPointer & { return holder; } //--------------------------------------------------------------------------------------------------------------------- template inline auto VLockGuard::GetLockError() const -> int { return lockError; } //--------------------------------------------------------------------------------------------------------------------- template inline auto VLockGuard::IsLocked() const -> bool { return !holder.isNull(); } //--------------------------------------------------------------------------------------------------------------------- template inline void VLockGuard::Unlock() { if (IsLocked()) { lock->unlock(); } } //--------------------------------------------------------------------------------------------------------------------- template auto VLockGuard::GetLockFile() const -> QString { return lockFile; } //--------------------------------------------------------------------------------------------------------------------- template auto VLockGuard::TryLock(const QString &lockName, int stale, int timeout) -> bool { bool res = true; lockFile = lockName + QLatin1String(".lock"); #if defined(Q_OS_UNIX) || defined(Q_OS_MACOS) { QFileInfo info(lockFile); lockFile = info.absolutePath() + QLatin1String("/.") + info.fileName(); } #endif lock.reset(new QLockFile(lockFile)); lock->setStaleLockTime(stale); lock->tryLock(timeout); if (QLockFile::LockFailedError == lock->error()) { // 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(); } #if defined(Q_OS_WIN) else { SetFileAttributesW(lockFile.toStdWString().c_str(), FILE_ATTRIBUTE_HIDDEN); } #endif return res; } // 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 QT_WARNING_PUSH QT_WARNING_DISABLE_INTEL(1418) template void VlpCreateLock(QSharedPointer> &r, const QString &lockName, int stale = 0, int timeout = 0) { r.reset(new VLockGuard(lockName, stale, timeout)); } template void VlpCreateLock(QSharedPointer> &r, const QString &lockName, Alloc a, int stale = 0, int timeout = 0) { r.reset(new VLockGuard(lockName, a, stale, timeout)); } template void VlpCreateLock(QSharedPointer> &r, const QString &lockName, Alloc a, Del d, int stale = 0, int timeout = 0) { r.reset(new VLockGuard(lockName, a, d, stale, timeout)); } QT_WARNING_POP #endif // VLOCKGUARD_H