Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qorderedmutexlocker_p.h
Go to the documentation of this file.
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 QORDEREDMUTEXLOCKER_P_H
6#define QORDEREDMUTEXLOCKER_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <QtCore/private/qglobal_p.h>
20#include <QtCore/qmutex.h>
21
22#include <functional>
23
24QT_BEGIN_NAMESPACE
25
26#if QT_CONFIG(thread)
27
28/*
29 Locks 2 mutexes in a defined order, avoiding a recursive lock if
30 we're trying to lock the same mutex twice.
31*/
32class QOrderedMutexLocker
33{
34public:
35 Q_NODISCARD_CTOR
36 QOrderedMutexLocker(QBasicMutex *m1, QBasicMutex *m2)
37 : mtx1((m1 == m2) ? m1 : (std::less<QBasicMutex *>()(m1, m2) ? m1 : m2)),
38 mtx2((m1 == m2) ? nullptr : (std::less<QBasicMutex *>()(m1, m2) ? m2 : m1)),
39 locked(false)
40 {
41 relock();
42 }
43
44 Q_DISABLE_COPY(QOrderedMutexLocker)
45
46 void swap(QOrderedMutexLocker &other) noexcept
47 {
48 qSwap(this->mtx1, other.mtx1);
49 qSwap(this->mtx2, other.mtx2);
50 qSwap(this->locked, other.locked);
51 }
52
53 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QOrderedMutexLocker)
54
55 Q_NODISCARD_CTOR
56 QOrderedMutexLocker(QOrderedMutexLocker &&other) noexcept
57 : mtx1(std::exchange(other.mtx1, nullptr))
58 , mtx2(std::exchange(other.mtx2, nullptr))
59 , locked(std::exchange(other.locked, false))
60 {}
61
62 ~QOrderedMutexLocker()
63 {
64 unlock();
65 }
66
67 void relock()
68 {
69 if (!locked) {
70 if (mtx1) mtx1->lock();
71 if (mtx2) mtx2->lock();
72 locked = true;
73 }
74 }
75
76 /*!
77 \internal
78 Can be called if the mutexes have been unlocked manually, and sets the
79 state of the QOrderedMutexLocker to unlocked.
80 The caller is expected to have unlocked both of them if they
81 are not the same. Calling this method when the QOrderedMutexLocker is
82 unlocked or when the provided mutexes have not actually been unlocked is
83 UB.
84 */
85 void dismiss()
86 {
87 Q_ASSERT(locked);
88 locked = false;
89 }
90
91 void unlock()
92 {
93 if (locked) {
94 if (mtx2) mtx2->unlock();
95 if (mtx1) mtx1->unlock();
96 locked = false;
97 }
98 }
99
100 static bool relock(QBasicMutex *mtx1, QBasicMutex *mtx2)
101 {
102 // mtx1 is already locked, mtx2 not... do we need to unlock and relock?
103 if (mtx1 == mtx2)
104 return false;
105 if (std::less<QBasicMutex *>()(mtx1, mtx2)) {
106 mtx2->lock();
107 return true;
108 }
109 if (!mtx2->tryLock()) {
110 mtx1->unlock();
111 mtx2->lock();
112 mtx1->lock();
113 }
114 return true;
115 }
116
117private:
118 QBasicMutex *mtx1, *mtx2;
119 bool locked;
120};
121
122#else
123
124class QOrderedMutexLocker
125{
126public:
127 Q_DISABLE_COPY(QOrderedMutexLocker)
128 Q_NODISCARD_CTOR
129 QOrderedMutexLocker(QBasicMutex *, QBasicMutex *) {}
130 Q_NODISCARD_CTOR
131 QOrderedMutexLocker(QOrderedMutexLocker &&) = default;
132 QOrderedMutexLocker& operator=(QOrderedMutexLocker &&other) = default;
133 ~QOrderedMutexLocker() {}
134
135 void relock() {}
136 void unlock() {}
137 void dismiss() {}
138
139 static bool relock(QBasicMutex *, QBasicMutex *) { return false; }
140};
141
142#endif
143
144
145QT_END_NAMESPACE
146
147#endif