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

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