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