Line data Source code
1 : /****************************************************************************
2 : **
3 : ** Copyright (C) 2016 The Qt Company Ltd.
4 : ** Contact: https://www.qt.io/licensing/
5 : **
6 : ** This file is part of the QtCore module of the Qt Toolkit.
7 : **
8 : ** $QT_BEGIN_LICENSE:LGPL$
9 : ** Commercial License Usage
10 : ** Licensees holding valid commercial Qt licenses may use this file in
11 : ** accordance with the commercial license agreement provided with the
12 : ** Software or, alternatively, in accordance with the terms contained in
13 : ** a written agreement between you and The Qt Company. For licensing terms
14 : ** and conditions see https://www.qt.io/terms-conditions. For further
15 : ** information use the contact form at https://www.qt.io/contact-us.
16 : **
17 : ** GNU Lesser General Public License Usage
18 : ** Alternatively, this file may be used under the terms of the GNU Lesser
19 : ** General Public License version 3 as published by the Free Software
20 : ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 : ** packaging of this file. Please review the following information to
22 : ** ensure the GNU Lesser General Public License version 3 requirements
23 : ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 : **
25 : ** GNU General Public License Usage
26 : ** Alternatively, this file may be used under the terms of the GNU
27 : ** General Public License version 2.0 or (at your option) the GNU General
28 : ** Public license version 3 or any later version approved by the KDE Free
29 : ** Qt Foundation. The licenses are as published by the Free Software
30 : ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 : ** included in the packaging of this file. Please review the following
32 : ** information to ensure the GNU General Public License requirements will
33 : ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 : ** https://www.gnu.org/licenses/gpl-3.0.html.
35 : **
36 : ** $QT_END_LICENSE$
37 : **
38 : ****************************************************************************/
39 :
40 : #ifndef QMUTEX_H
41 : #define QMUTEX_H
42 :
43 : #include <QtCore/qglobal.h>
44 : #include <QtCore/qatomic.h>
45 : #include <new>
46 :
47 : #if QT_HAS_INCLUDE(<chrono>)
48 : # include <chrono>
49 : # include <limits>
50 : #endif
51 :
52 : class tst_QMutex;
53 :
54 : QT_BEGIN_NAMESPACE
55 :
56 :
57 : #if !defined(QT_NO_THREAD) || defined(Q_CLANG_QDOC)
58 :
59 : #ifdef Q_OS_LINUX
60 : # define QT_MUTEX_LOCK_NOEXCEPT Q_DECL_NOTHROW
61 : #else
62 : # define QT_MUTEX_LOCK_NOEXCEPT
63 : #endif
64 :
65 : class QMutexData;
66 :
67 : class Q_CORE_EXPORT QBasicMutex
68 : {
69 : public:
70 : #ifdef Q_COMPILER_CONSTEXPR
71 : constexpr QBasicMutex()
72 : : d_ptr(nullptr)
73 : {}
74 : #endif
75 :
76 : // BasicLockable concept
77 : inline void lock() QT_MUTEX_LOCK_NOEXCEPT {
78 : if (!fastTryLock())
79 : lockInternal();
80 : }
81 :
82 : // BasicLockable concept
83 : inline void unlock() Q_DECL_NOTHROW {
84 : Q_ASSERT(d_ptr.load()); //mutex must be locked
85 : if (!fastTryUnlock())
86 : unlockInternal();
87 : }
88 :
89 : bool tryLock() Q_DECL_NOTHROW {
90 : return fastTryLock();
91 : }
92 :
93 : // Lockable concept
94 : bool try_lock() Q_DECL_NOTHROW { return tryLock(); }
95 :
96 : bool isRecursive() Q_DECL_NOTHROW; //### Qt6: remove me
97 : bool isRecursive() const Q_DECL_NOTHROW;
98 :
99 : private:
100 : inline bool fastTryLock() Q_DECL_NOTHROW {
101 : return d_ptr.testAndSetAcquire(Q_NULLPTR, dummyLocked());
102 : }
103 : inline bool fastTryUnlock() Q_DECL_NOTHROW {
104 : return d_ptr.testAndSetRelease(dummyLocked(), Q_NULLPTR);
105 : }
106 : inline bool fastTryLock(QMutexData *¤t) Q_DECL_NOTHROW {
107 : return d_ptr.testAndSetAcquire(Q_NULLPTR, dummyLocked(), current);
108 : }
109 : inline bool fastTryUnlock(QMutexData *¤t) Q_DECL_NOTHROW {
110 : return d_ptr.testAndSetRelease(dummyLocked(), Q_NULLPTR, current);
111 : }
112 :
113 : void lockInternal() QT_MUTEX_LOCK_NOEXCEPT;
114 : bool lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT;
115 : void unlockInternal() Q_DECL_NOTHROW;
116 :
117 : QBasicAtomicPointer<QMutexData> d_ptr;
118 : static inline QMutexData *dummyLocked() {
119 : return reinterpret_cast<QMutexData *>(quintptr(1));
120 : }
121 :
122 : friend class QMutex;
123 : friend class QMutexData;
124 : };
125 :
126 : class Q_CORE_EXPORT QMutex : public QBasicMutex
127 : {
128 : public:
129 : enum RecursionMode { NonRecursive, Recursive };
130 : explicit QMutex(RecursionMode mode = NonRecursive);
131 : ~QMutex();
132 :
133 : // BasicLockable concept
134 : void lock() QT_MUTEX_LOCK_NOEXCEPT;
135 : bool tryLock(int timeout = 0) QT_MUTEX_LOCK_NOEXCEPT;
136 : // BasicLockable concept
137 : void unlock() Q_DECL_NOTHROW;
138 :
139 : // Lockable concept
140 : bool try_lock() QT_MUTEX_LOCK_NOEXCEPT { return tryLock(); }
141 :
142 : #if QT_HAS_INCLUDE(<chrono>)
143 : // TimedLockable concept
144 : template <class Rep, class Period>
145 : bool try_lock_for(std::chrono::duration<Rep, Period> duration)
146 : {
147 : return tryLock(convertToMilliseconds(duration));
148 : }
149 :
150 : // TimedLockable concept
151 : template<class Clock, class Duration>
152 : bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
153 : {
154 : // Implemented in terms of try_lock_for to honor the similar
155 : // requirement in N4606 § 30.4.1.3 [thread.timedmutex.requirements]/12.
156 :
157 : return try_lock_for(timePoint - Clock::now());
158 : }
159 : #endif
160 :
161 : bool isRecursive() const Q_DECL_NOTHROW
162 : { return QBasicMutex::isRecursive(); }
163 :
164 : private:
165 : Q_DISABLE_COPY(QMutex)
166 : friend class QMutexLocker;
167 : friend class ::tst_QMutex;
168 :
169 : #if QT_HAS_INCLUDE(<chrono>)
170 : template<class Rep, class Period>
171 : static int convertToMilliseconds(std::chrono::duration<Rep, Period> duration)
172 : {
173 : // N4606 § 30.4.1.3.5 [thread.timedmutex.requirements] specifies that a
174 : // duration less than or equal to duration.zero() shall result in a
175 : // try_lock, unlike QMutex's tryLock with a negative duration which
176 : // results in a lock.
177 :
178 : if (duration <= duration.zero())
179 : return 0;
180 :
181 : // when converting from 'duration' to milliseconds, make sure that
182 : // the result is not shorter than 'duration':
183 : std::chrono::milliseconds wait = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
184 : if (wait < duration)
185 : wait += std::chrono::milliseconds(1);
186 : Q_ASSERT(wait >= duration);
187 : const auto ms = wait.count();
188 : const auto maxInt = (std::numeric_limits<int>::max)();
189 :
190 : return ms < maxInt ? int(ms) : maxInt;
191 : }
192 : #endif
193 : };
194 :
195 : class Q_CORE_EXPORT QMutexLocker
196 : {
197 : public:
198 : #ifndef Q_CLANG_QDOC
199 21 : inline explicit QMutexLocker(QBasicMutex *m) QT_MUTEX_LOCK_NOEXCEPT
200 21 : {
201 21 : Q_ASSERT_X((reinterpret_cast<quintptr>(m) & quintptr(1u)) == quintptr(0),
202 21 : "QMutexLocker", "QMutex pointer is misaligned");
203 21 : val = quintptr(m);
204 21 : if (Q_LIKELY(m)) {
205 : // call QMutex::lock() instead of QBasicMutex::lock()
206 21 : static_cast<QMutex *>(m)->lock();
207 21 : val |= 1;
208 : }
209 21 : }
210 : #else
211 : QMutexLocker(QMutex *) { }
212 : #endif
213 21 : inline ~QMutexLocker() { unlock(); }
214 :
215 21 : inline void unlock() Q_DECL_NOTHROW
216 : {
217 21 : if ((val & quintptr(1u)) == quintptr(1u)) {
218 21 : val &= ~quintptr(1u);
219 21 : mutex()->unlock();
220 : }
221 21 : }
222 :
223 : inline void relock() QT_MUTEX_LOCK_NOEXCEPT
224 : {
225 : if (val) {
226 : if ((val & quintptr(1u)) == quintptr(0u)) {
227 : mutex()->lock();
228 : val |= quintptr(1u);
229 : }
230 : }
231 : }
232 :
233 : #if defined(Q_CC_MSVC)
234 : #pragma warning( push )
235 : #pragma warning( disable : 4312 ) // ignoring the warning from /Wp64
236 : #endif
237 :
238 21 : inline QMutex *mutex() const
239 : {
240 21 : return reinterpret_cast<QMutex *>(val & ~quintptr(1u));
241 : }
242 :
243 : #if defined(Q_CC_MSVC)
244 : #pragma warning( pop )
245 : #endif
246 :
247 : private:
248 : Q_DISABLE_COPY(QMutexLocker)
249 :
250 : quintptr val;
251 : };
252 :
253 : #else // QT_NO_THREAD && !Q_CLANG_QDOC
254 :
255 : class Q_CORE_EXPORT QMutex
256 : {
257 : public:
258 : enum RecursionMode { NonRecursive, Recursive };
259 :
260 : inline Q_DECL_CONSTEXPR explicit QMutex(RecursionMode = NonRecursive) Q_DECL_NOTHROW { }
261 :
262 : inline void lock() Q_DECL_NOTHROW {}
263 : inline bool tryLock(int timeout = 0) Q_DECL_NOTHROW { Q_UNUSED(timeout); return true; }
264 : inline bool try_lock() Q_DECL_NOTHROW { return true; }
265 : inline void unlock() Q_DECL_NOTHROW {}
266 : inline bool isRecursive() const Q_DECL_NOTHROW { return true; }
267 :
268 : #if QT_HAS_INCLUDE(<chrono>)
269 : template <class Rep, class Period>
270 : inline bool try_lock_for(std::chrono::duration<Rep, Period> duration) Q_DECL_NOTHROW
271 : {
272 : Q_UNUSED(duration);
273 : return true;
274 : }
275 :
276 : template<class Clock, class Duration>
277 : inline bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint) Q_DECL_NOTHROW
278 : {
279 : Q_UNUSED(timePoint);
280 : return true;
281 : }
282 : #endif
283 :
284 : private:
285 : Q_DISABLE_COPY(QMutex)
286 : };
287 :
288 : class Q_CORE_EXPORT QMutexLocker
289 : {
290 : public:
291 : inline explicit QMutexLocker(QMutex *) Q_DECL_NOTHROW {}
292 : inline ~QMutexLocker() Q_DECL_NOTHROW {}
293 :
294 : inline void unlock() Q_DECL_NOTHROW {}
295 : void relock() Q_DECL_NOTHROW {}
296 : inline QMutex *mutex() const Q_DECL_NOTHROW { return Q_NULLPTR; }
297 :
298 : private:
299 : Q_DISABLE_COPY(QMutexLocker)
300 : };
301 :
302 : typedef QMutex QBasicMutex;
303 :
304 : #endif // QT_NO_THREAD && !Q_CLANG_QDOC
305 :
306 : QT_END_NAMESPACE
307 :
308 : #endif // QMUTEX_H
|