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