Coverage Report

Created: 2025-09-08 07:52

/usr/include/QtCore/qmutex.h
Line
Count
Source (jump to first uncovered line)
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
15
QT_BEGIN_NAMESPACE
16
17
#if QT_CONFIG(thread) || defined(Q_QDOC)
18
19
class QMutex;
20
class QRecursiveMutex;
21
class QMutexPrivate;
22
23
class QT6_ONLY(Q_CORE_EXPORT) QBasicMutex
24
{
25
    Q_DISABLE_COPY_MOVE(QBasicMutex)
26
protected:
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
35
public:
36
    constexpr QBasicMutex()
37
        : d_ptr(nullptr)
38
    {}
39
40
    // BasicLockable concept
41
3.42M
    inline void lock() noexcept(FutexAlwaysAvailable) {
42
3.42M
        QtTsan::mutexPreLock(this, 0u);
43
44
3.42M
        if (!fastTryLock())
45
873
            lockInternal();
46
47
3.42M
        QtTsan::mutexPostLock(this, 0u, 0);
48
3.42M
    }
49
50
    // BasicLockable concept
51
3.42M
    inline void unlock() noexcept {
52
3.42M
        Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked
53
54
3.42M
        QtTsan::mutexPreUnlock(this, 0u);
55
56
3.42M
        if constexpr (FutexAlwaysAvailable) {
57
            // we always unlock if we have futexes
58
3.42M
            if (QMutexPrivate *d = d_ptr.fetchAndStoreRelease(nullptr); Q_UNLIKELY(d != dummyLocked()))
59
1.47k
                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(dummyLocked(), nullptr, d))
63
                unlockInternal(d);          // was contended
64
        }
65
66
3.42M
        QtTsan::mutexPostUnlock(this, 0u);
67
3.42M
    }
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
0
    bool try_lock() noexcept { return tryLock(); }
84
85
private:
86
    inline bool fastTryLock() noexcept
87
3.42M
    {
88
3.42M
        if (Q_UNLIKELY(d_ptr.loadRelaxed() != nullptr))
89
821
            return false;
90
3.42M
        return d_ptr.testAndSetAcquire(nullptr, dummyLocked());
91
3.42M
    }
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
6.85M
    static inline QMutexPrivate *dummyLocked() {
118
6.85M
        return reinterpret_cast<QMutexPrivate *>(quintptr(1));
119
6.85M
    }
120
121
    friend class QMutex;
122
    friend class QMutexPrivate;
123
};
124
125
class QT6_ONLY(Q_CORE_EXPORT) QMutex : public QBasicMutex
126
{
127
public:
128
    constexpr QMutex() = default;
129
    ~QMutex()
130
3.47k
    {
131
3.47k
        QMutexPrivate *d = d_ptr.loadRelaxed();
132
3.47k
        if (d)
133
0
            destroyInternal(d);
134
3.47k
    }
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
0
    bool try_lock() noexcept { return tryLock(); }
144
145
146
    using QBasicMutex::tryLock;
147
    bool tryLock(int timeout) noexcept(FutexAlwaysAvailable)
148
0
    {
149
0
        return tryLock(QDeadlineTimer(timeout));
150
0
    }
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(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(QDeadlineTimer(timePoint));
185
    }
186
};
187
188
class 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
199
public:
200
    constexpr QRecursiveMutex() = default;
201
    QT7_ONLY(Q_CORE_EXPORT)
202
    ~QRecursiveMutex();
203
204
205
    // BasicLockable concept
206
    void lock() noexcept(LockIsNoexcept)
207
    { tryLock(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
0
    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(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(QDeadlineTimer(timePoint));
231
    }
232
};
233
234
#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
235
bool QRecursiveMutex::tryLock(int timeout) noexcept(LockIsNoexcept)
236
{
237
    return tryLock(QDeadlineTimer(timeout));
238
}
239
#endif
240
241
template <typename Mutex>
242
class 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
250
public:
251
    Q_NODISCARD_CTOR
252
    inline explicit QMutexLocker(Mutex *mutex) noexcept(LockIsNoexcept)
253
97.9k
    {
254
97.9k
        m_mutex = mutex;
255
97.9k
        if (Q_LIKELY(mutex)) {
256
97.9k
            mutex->lock();
257
97.9k
            m_isLocked = true;
258
97.9k
        }
259
97.9k
    }
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(other.m_isLocked, false))
265
    {}
266
267
    QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QMutexLocker)
268
269
    inline ~QMutexLocker()
270
97.9k
    {
271
97.9k
        if (m_isLocked)
272
84.5k
            unlock();
273
97.9k
    }
274
275
    inline bool isLocked() const noexcept
276
    {
277
        return m_isLocked;
278
    }
279
280
    inline void unlock() noexcept
281
138k
    {
282
138k
        Q_ASSERT(m_isLocked);
283
138k
        m_mutex->unlock();
284
138k
        m_isLocked = false;
285
138k
    }
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(m_isLocked, other.m_isLocked);
298
    }
299
300
    Mutex *mutex() const
301
    {
302
        return m_mutex;
303
    }
304
private:
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
313
class QMutex
314
{
315
public:
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
338
private:
339
    Q_DISABLE_COPY(QMutex)
340
};
341
342
class QRecursiveMutex : public QMutex {};
343
344
template <typename Mutex>
345
class QMutexLocker
346
{
347
public:
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
356
private:
357
    Q_DISABLE_COPY(QMutexLocker)
358
};
359
360
typedef QMutex QBasicMutex;
361
362
#endif // !QT_CONFIG(thread) && !Q_QDOC
363
364
QT_END_NAMESPACE
365
366
#endif // QMUTEX_H