Coverage Report

Created: 2025-07-16 07:53

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