1 /************************************************************************
2 **
3 ** @file VLockGuard.h
4 ** @author Alex Zaharov <alexzkhr@gmail.com>
5 ** @author Roman Telezhynskyi <dismine(at)gmail.com>
6 ** @date 14 9, 2015
7 **
8 ** @brief
9 ** @copyright
10 ** This source code is part of the Valentina project, a pattern making
11 ** program, whose allow create and modeling patterns of clothing.
12 ** Copyright (C) 2015 Valentina project
13 ** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
14 **
15 ** Valentina is free software: you can redistribute it and/or modify
16 ** it under the terms of the GNU General Public License as published by
17 ** the Free Software Foundation, either version 3 of the License, or
18 ** (at your option) any later version.
19 **
20 ** Valentina is distributed in the hope that it will be useful,
21 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 ** GNU General Public License for more details.
24 **
25 ** You should have received a copy of the GNU General Public License
26 ** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
27 **
28 *************************************************************************/
29
30 #ifndef VLOCKGUARD_H
31 #define VLOCKGUARD_H
32
33 #include <QtGlobal>
34 #ifdef Q_OS_WIN
35 # include <qt_windows.h>
36 #endif /*Q_OS_WIN*/
37
38 #include <QString>
39 #include <stdint.h>
40
41 #include "../vmisc/diagnostic.h"
42
43 #include <QFileInfo>
44 #include <QLockFile>
45 #include <QSharedPointer>
46
47 /*@brief
48 * This class creates Guarded object if and only if lock file taken. It keeps shared_ptr to object and lock-file.
49 * Can use optional object allocator and deleter.
50 *
51 * On older Qt lock assumed always taken and compile-time warning is shown.
52 *
53 */
54 template <typename Guarded>
55 class VLockGuard
56 {
57 public:
58 explicit VLockGuard(const QString& lockName, int stale = 0, int timeout = 0);
59
60 template <typename Alloc>
61 VLockGuard(const QString& lockName, Alloc a, int stale = 0, int timeout=0);
62
63 template <typename Alloc, typename Delete>
64 VLockGuard(const QString& lockName, Alloc a, Delete d, int stale = 0, int timeout=0);
65
66 const QSharedPointer<Guarded> &GetProtected() const;
67 int GetLockError() const;
68 bool IsLocked() const;
69 void Unlock();
70 QString GetLockFile() const;
71
72 private:
73 Q_DISABLE_COPY(VLockGuard<Guarded>)
74
75 QSharedPointer<Guarded> holder;
76 int lockError;
77 QString lockFile;
78 QSharedPointer<QLockFile> lock;
79
80 // cppcheck-suppress functionStatic
81 bool TryLock(const QString &lockName, int stale, int timeout);
82 };
83
84 //---------------------------------------------------------------------------------------------------------------------
85 template <typename Guarded>
VLockGuard(const QString & lockName,int stale,int timeout)86 VLockGuard<Guarded>::VLockGuard(const QString &lockName, int stale, int timeout)
87 : holder(nullptr), lockError(0), lockFile(), lock(nullptr)
88 {
89 if (TryLock(lockName, stale, timeout))
90 {
91 holder.reset(new Guarded());
92 }
93 }
94
95 //---------------------------------------------------------------------------------------------------------------------
96 //using allocator lambdas seems logically better than supplying pointer, because we will take ownership of allocated
97 //object
98 template <typename Guarded> template <typename Alloc>
VLockGuard(const QString & lockName,Alloc a,int stale,int timeout)99 VLockGuard<Guarded>::VLockGuard(const QString& lockName, Alloc a, int stale, int timeout)
100 : holder(nullptr), lockError(0), lockFile(), lock(nullptr)
101 {
102 if (TryLock(lockName, stale, timeout))
103 {
104 holder.reset(a());
105 }
106 }
107
108 //---------------------------------------------------------------------------------------------------------------------
109 template <typename Guarded> template <typename Alloc, typename Delete>
VLockGuard(const QString & lockName,Alloc a,Delete d,int stale,int timeout)110 VLockGuard<Guarded>::VLockGuard(const QString& lockName, Alloc a, Delete d, int stale, int timeout)
111 : holder(nullptr), lockError(0), lockFile(), lock(nullptr)
112 {
113 if (TryLock(lockName, stale, timeout))
114 {
115 holder.reset(a(), d);
116 }
117 }
118
119 //---------------------------------------------------------------------------------------------------------------------
120 template <typename Guarded>
GetProtected()121 inline const QSharedPointer<Guarded> &VLockGuard<Guarded>::GetProtected() const
122 {
123 return holder;
124 }
125
126 //---------------------------------------------------------------------------------------------------------------------
127 template <typename Guarded>
GetLockError()128 inline int VLockGuard<Guarded>::GetLockError() const
129 {
130 return lockError;
131 }
132
133 //---------------------------------------------------------------------------------------------------------------------
134 template <typename Guarded>
IsLocked()135 inline bool VLockGuard<Guarded>::IsLocked() const
136 {
137 return not holder.isNull();
138 }
139
140 //---------------------------------------------------------------------------------------------------------------------
141 template<typename Guarded>
Unlock()142 inline void VLockGuard<Guarded>::Unlock()
143 {
144 if (IsLocked())
145 {
146 lock->unlock();
147 }
148 }
149
150 //---------------------------------------------------------------------------------------------------------------------
151 template <typename Guarded>
GetLockFile()152 QString VLockGuard<Guarded>::GetLockFile() const
153 {
154 return lockFile;
155 }
156
157 //---------------------------------------------------------------------------------------------------------------------
158 template <typename Guarded>
TryLock(const QString & lockName,int stale,int timeout)159 bool VLockGuard<Guarded>::TryLock(const QString &lockName, int stale, int timeout)
160 {
161 bool res = true;
162
163 lockFile = lockName + QLatin1String(".lock");
164 #if defined(Q_OS_UNIX)
165 QFileInfo info(lockFile);
166 lockFile = info.absolutePath() + QLatin1String("/.") + info.fileName();
167 #endif
168 lock.reset(new QLockFile(lockFile));
169
170 lock->setStaleLockTime(stale);
171 lock->tryLock(timeout);
172
173 if (QLockFile::LockFailedError == lock->error())
174 {
175 // This happens if a stale lock file exists and another process uses that PID.
176 // Try removing the stale file, which will fail if a real process is holding a
177 // file-level lock. A false error is more problematic than not locking properly
178 // on corner-case systems.
179 lock->removeStaleLockFile();
180 lock->tryLock(timeout);
181 }
182 res = QLockFile::NoError == (lockError = lock->error());
183 if (!res)
184 {
185 lock.reset();
186 }
187 #if defined(Q_OS_WIN)
188 else
189 {
190 SetFileAttributesW(lockFile.toStdWString().c_str(), FILE_ATTRIBUTE_HIDDEN);
191 }
192 #endif
193 return res;
194 }
195
196 //use pointer and function below to persistent things like class-member, because lock is taken by constructor
197 //helper functions allow to write shorter creating and setting new lock-pointer
198
199 QT_WARNING_PUSH
200 QT_WARNING_DISABLE_INTEL(1418)
201
202 template <typename Guarded>
203 void VlpCreateLock(QSharedPointer<VLockGuard<Guarded>>& r, const QString& lockName, int stale = 0, int timeout = 0)
204 {
205 r.reset(new VLockGuard<Guarded>(lockName, stale, timeout));
206 }
207
208 template <typename Guarded, typename Alloc>
209 void VlpCreateLock(QSharedPointer<VLockGuard<Guarded>>& r, const QString& lockName, Alloc a, int stale = 0,
210 int timeout = 0)
211 {
212 r.reset(new VLockGuard<Guarded>(lockName, a, stale, timeout));
213 }
214
215 template <typename Guarded, typename Alloc, typename Del>
216 void VlpCreateLock(QSharedPointer<VLockGuard<Guarded>>& r, const QString& lockName, Alloc a, Del d, int stale = 0,
217 int timeout = 0)
218 {
219 r.reset(new VLockGuard<Guarded>(lockName, a, d, stale, timeout));
220 }
221
222 QT_WARNING_POP
223
224 #endif // VLOCKGUARD_H
225