1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#ifndef QMUTEX_H
6#define QMUTEX_H
7
8#include <QtCore/qglobal.h>
9#include <QtCore/qatomic.h>
10#include <QtCore/qdeadlinetimer.h>
11#include <QtCore/qtsan_impl.h>
12
13#include <chrono>
14
15QT_BEGIN_NAMESPACE
16
17#if QT_CONFIG(thread) || defined(Q_QDOC)
18
19class QMutex;
20class QRecursiveMutex;
21class QMutexPrivate;
22
23class QT6_ONLY(Q_CORE_EXPORT) QBasicMutex
24{
25 Q_DISABLE_COPY_MOVE(QBasicMutex)
26protected:
27 static constexpr bool FutexAlwaysAvailable =
28#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX) || defined(Q_OS_WIN) // these platforms use futex
29 true
30#else
31 false
32#endif
33 ;
34
35public:
36 constexpr QBasicMutex()
37 : d_ptr(nullptr)
38 {}
39
40 // BasicLockable concept
41 inline void lock() noexcept(FutexAlwaysAvailable) {
42 QtTsan::mutexPreLock(this, 0u);
43
44 if (!fastTryLock())
45 lockInternal();
46
47 QtTsan::mutexPostLock(this, 0u, 0);
48 }
49
50 // BasicLockable concept
51 inline void unlock() noexcept {
52 Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked
53
54 QtTsan::mutexPreUnlock(this, 0u);
55
56 if constexpr (FutexAlwaysAvailable) {
57 // we always unlock if we have futexes
58 if (QMutexPrivate *d = d_ptr.fetchAndStoreRelease(newValue: nullptr); d != dummyLocked())
59 unlockInternalFutex(d); // was contended
60 } else {
61 // if we don't have futexes, we can only unlock if not contended
62 if (QMutexPrivate *d; !d_ptr.testAndSetRelease(expectedValue: dummyLocked(), newValue: nullptr, currentValue&: d))
63 unlockInternal(d); // was contended
64 }
65
66 QtTsan::mutexPostUnlock(this, 0u);
67 }
68
69 bool tryLock() noexcept {
70 unsigned tsanFlags = QtTsan::TryLock;
71 QtTsan::mutexPreLock(this, tsanFlags);
72
73 const bool success = fastTryLock();
74
75 if (!success)
76 tsanFlags |= QtTsan::TryLockFailed;
77 QtTsan::mutexPostLock(this, tsanFlags, 0);
78
79 return success;
80 }
81
82 // Lockable concept
83 bool try_lock() noexcept { return tryLock(); }
84
85private:
86 inline bool fastTryLock() noexcept
87 {
88 if (d_ptr.loadRelaxed() != nullptr)
89 return false;
90 return d_ptr.testAndSetAcquire(expectedValue: nullptr, newValue: dummyLocked());
91 }
92#if QT_CORE_REMOVED_SINCE(6, 10)
93 inline bool fastTryUnlock() noexcept {
94 return d_ptr.testAndSetRelease(dummyLocked(), nullptr);
95 }
96#endif
97
98 QT7_ONLY(Q_CORE_EXPORT)
99 void lockInternal() noexcept(FutexAlwaysAvailable);
100 QT7_ONLY(Q_CORE_EXPORT)
101 bool lockInternal(QDeadlineTimer timeout) noexcept(FutexAlwaysAvailable);
102#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
103 bool lockInternal(int timeout) noexcept(FutexAlwaysAvailable);
104 void unlockInternal() noexcept;
105#endif
106 QT7_ONLY(Q_CORE_EXPORT)
107 void unlockInternalFutex(void *d) noexcept;
108 QT7_ONLY(Q_CORE_EXPORT)
109 void unlockInternal(void *d) noexcept;
110#if QT_CORE_REMOVED_SINCE(6, 9)
111 void destroyInternal(QMutexPrivate *d);
112#endif
113 QT7_ONLY(Q_CORE_EXPORT)
114 void destroyInternal(void *d);
115
116 QBasicAtomicPointer<QMutexPrivate> d_ptr;
117 static inline QMutexPrivate *dummyLocked() {
118 return reinterpret_cast<QMutexPrivate *>(quintptr(1));
119 }
120
121 friend class QMutex;
122 friend class QMutexPrivate;
123};
124
125class QT6_ONLY(Q_CORE_EXPORT) QMutex : public QBasicMutex
126{
127public:
128 constexpr QMutex() = default;
129 ~QMutex()
130 {
131 QMutexPrivate *d = d_ptr.loadRelaxed();
132 if (d)
133 destroyInternal(d);
134 }
135
136#ifdef Q_QDOC
137 inline void lock() noexcept(FutexAlwaysAvailable);
138 inline void unlock() noexcept;
139 bool tryLock() noexcept;
140#endif
141
142 // Lockable concept
143 bool try_lock() noexcept { return tryLock(); }
144
145
146 using QBasicMutex::tryLock;
147 bool tryLock(int timeout) noexcept(FutexAlwaysAvailable)
148 {
149 return tryLock(timeout: QDeadlineTimer(timeout));
150 }
151
152 bool tryLock(QDeadlineTimer timeout) noexcept(FutexAlwaysAvailable)
153 {
154 unsigned tsanFlags = QtTsan::TryLock;
155 QtTsan::mutexPreLock(this, tsanFlags);
156
157 bool success = fastTryLock();
158
159 if (success) {
160 QtTsan::mutexPostLock(this, tsanFlags, 0);
161 return success;
162 }
163
164 success = lockInternal(timeout);
165
166 if (!success)
167 tsanFlags |= QtTsan::TryLockFailed;
168 QtTsan::mutexPostLock(this, tsanFlags, 0);
169
170 return success;
171 }
172
173 // TimedLockable concept
174 template <class Rep, class Period>
175 bool try_lock_for(std::chrono::duration<Rep, Period> duration)
176 {
177 return tryLock(timeout: QDeadlineTimer(duration));
178 }
179
180 // TimedLockable concept
181 template<class Clock, class Duration>
182 bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
183 {
184 return tryLock(timeout: QDeadlineTimer(timePoint));
185 }
186};
187
188class QT6_ONLY(Q_CORE_EXPORT) QRecursiveMutex
189{
190 Q_DISABLE_COPY_MOVE(QRecursiveMutex)
191 // written to by the thread that first owns 'mutex';
192 // read during attempts to acquire ownership of 'mutex' from any other thread:
193 QAtomicPointer<void> owner = nullptr;
194 // only ever accessed from the thread that owns 'mutex':
195 uint count = 0;
196 QMutex mutex;
197 static constexpr bool LockIsNoexcept = noexcept(std::declval<QMutex>().lock());
198
199public:
200 constexpr QRecursiveMutex() = default;
201 QT7_ONLY(Q_CORE_EXPORT)
202 ~QRecursiveMutex();
203
204
205 // BasicLockable concept
206 void lock() noexcept(LockIsNoexcept)
207 { tryLock(timer: QDeadlineTimer(QDeadlineTimer::Forever)); }
208 QT_CORE_INLINE_SINCE(6, 6)
209 bool tryLock(int timeout) noexcept(LockIsNoexcept);
210 QT7_ONLY(Q_CORE_EXPORT)
211 bool tryLock(QDeadlineTimer timer = {}) noexcept(LockIsNoexcept);
212 // BasicLockable concept
213 QT7_ONLY(Q_CORE_EXPORT)
214 void unlock() noexcept;
215
216 // Lockable concept
217 bool try_lock() noexcept(LockIsNoexcept) { return tryLock(); }
218
219 // TimedLockable concept
220 template <class Rep, class Period>
221 bool try_lock_for(std::chrono::duration<Rep, Period> duration)
222 {
223 return tryLock(timer: QDeadlineTimer(duration));
224 }
225
226 // TimedLockable concept
227 template<class Clock, class Duration>
228 bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
229 {
230 return tryLock(timer: QDeadlineTimer(timePoint));
231 }
232};
233
234#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
235bool QRecursiveMutex::tryLock(int timeout) noexcept(LockIsNoexcept)
236{
237 return tryLock(timer: QDeadlineTimer(timeout));
238}
239#endif
240
241template <typename Mutex>
242class QMutexLocker
243{
244#ifdef Q_CC_GHS
245 // internal compiler error otherwise
246 static constexpr bool LockIsNoexcept = false;
247#else
248 static constexpr bool LockIsNoexcept = noexcept(std::declval<Mutex>().lock());
249#endif
250public:
251 Q_NODISCARD_CTOR
252 inline explicit QMutexLocker(Mutex *mutex) noexcept(LockIsNoexcept)
253 {
254 m_mutex = mutex;
255 if (Q_LIKELY(mutex)) {
256 mutex->lock();
257 m_isLocked = true;
258 }
259 }
260
261 Q_NODISCARD_CTOR
262 inline QMutexLocker(QMutexLocker &&other) noexcept
263 : m_mutex(std::exchange(other.m_mutex, nullptr)),
264 m_isLocked(std::exchange(obj&: other.m_isLocked, new_val: false))
265 {}
266
267 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QMutexLocker)
268
269 inline ~QMutexLocker()
270 {
271 if (m_isLocked)
272 unlock();
273 }
274
275 inline bool isLocked() const noexcept
276 {
277 return m_isLocked;
278 }
279
280 inline void unlock() noexcept
281 {
282 Q_ASSERT(m_isLocked);
283 m_mutex->unlock();
284 m_isLocked = false;
285 }
286
287 inline void relock() noexcept(LockIsNoexcept)
288 {
289 Q_ASSERT(!m_isLocked);
290 m_mutex->lock();
291 m_isLocked = true;
292 }
293
294 inline void swap(QMutexLocker &other) noexcept
295 {
296 qt_ptr_swap(m_mutex, other.m_mutex);
297 std::swap(a&: m_isLocked, b&: other.m_isLocked);
298 }
299
300 Mutex *mutex() const
301 {
302 return m_mutex;
303 }
304private:
305 Q_DISABLE_COPY(QMutexLocker)
306
307 Mutex *m_mutex;
308 bool m_isLocked = false;
309};
310
311#else // !QT_CONFIG(thread) && !Q_QDOC
312
313class QMutex
314{
315public:
316
317 constexpr QMutex() noexcept { }
318
319 inline void lock() noexcept {}
320 inline bool tryLock(int timeout = 0) noexcept { Q_UNUSED(timeout); return true; }
321 inline bool try_lock() noexcept { return true; }
322 inline void unlock() noexcept {}
323
324 template <class Rep, class Period>
325 inline bool try_lock_for(std::chrono::duration<Rep, Period> duration) noexcept
326 {
327 Q_UNUSED(duration);
328 return true;
329 }
330
331 template<class Clock, class Duration>
332 inline bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint) noexcept
333 {
334 Q_UNUSED(timePoint);
335 return true;
336 }
337
338private:
339 Q_DISABLE_COPY(QMutex)
340};
341
342class QRecursiveMutex : public QMutex {};
343
344template <typename Mutex>
345class QMutexLocker
346{
347public:
348 Q_NODISCARD_CTOR
349 inline explicit QMutexLocker(Mutex *) noexcept {}
350 inline ~QMutexLocker() noexcept {}
351
352 inline void unlock() noexcept {}
353 void relock() noexcept {}
354 inline Mutex *mutex() const noexcept { return nullptr; }
355
356private:
357 Q_DISABLE_COPY(QMutexLocker)
358};
359
360typedef QMutex QBasicMutex;
361
362#endif // !QT_CONFIG(thread) && !Q_QDOC
363
364QT_END_NAMESPACE
365
366#endif // QMUTEX_H
367

source code of qtbase/src/corelib/thread/qmutex.h