1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QORDEREDMUTEXLOCKER_P_H
41#define QORDEREDMUTEXLOCKER_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <QtCore/private/qglobal_p.h>
55#include <QtCore/qmutex.h>
56
57#include <functional>
58
59QT_BEGIN_NAMESPACE
60
61#if QT_CONFIG(thread)
62
63/*
64 Locks 2 mutexes in a defined order, avoiding a recursive lock if
65 we're trying to lock the same mutex twice.
66*/
67class QOrderedMutexLocker
68{
69public:
70 QOrderedMutexLocker(QBasicMutex *m1, QBasicMutex *m2)
71 : mtx1((m1 == m2) ? m1 : (std::less<QBasicMutex *>()(m1, m2) ? m1 : m2)),
72 mtx2((m1 == m2) ? nullptr : (std::less<QBasicMutex *>()(m1, m2) ? m2 : m1)),
73 locked(false)
74 {
75 relock();
76 }
77
78 Q_DISABLE_COPY(QOrderedMutexLocker)
79
80 void swap(QOrderedMutexLocker &other) noexcept
81 {
82 qSwap(value1&: this->mtx1, value2&: other.mtx1);
83 qSwap(value1&: this->mtx2, value2&: other.mtx2);
84 qSwap(value1&: this->locked, value2&: other.locked);
85 }
86
87 QOrderedMutexLocker &operator=(QOrderedMutexLocker &&other) noexcept {
88 QOrderedMutexLocker moved(std::move(other));
89 swap(other&: moved);
90 return *this;
91 }
92
93 QOrderedMutexLocker(QOrderedMutexLocker &&other) noexcept
94 : mtx1(qExchange(t&: other.mtx1, newValue: nullptr))
95 , mtx2(qExchange(t&: other.mtx2, newValue: nullptr))
96 , locked(qExchange(t&: other.locked, newValue: false))
97 {}
98
99 ~QOrderedMutexLocker()
100 {
101 unlock();
102 }
103
104 void relock()
105 {
106 if (!locked) {
107 if (mtx1) mtx1->lock();
108 if (mtx2) mtx2->lock();
109 locked = true;
110 }
111 }
112
113 /*!
114 \internal
115 Can be called if the mutexes have been unlocked manually, and sets the
116 state of the QOrderedMutexLocker to unlocked.
117 The caller is expected to have unlocked both of them if they
118 are not the same. Calling this method when the QOrderedMutexLocker is
119 unlocked or when the provided mutexes have not actually been unlocked is
120 UB.
121 */
122 void dismiss()
123 {
124 Q_ASSERT(locked);
125 locked = false;
126 }
127
128 void unlock()
129 {
130 if (locked) {
131 if (mtx2) mtx2->unlock();
132 if (mtx1) mtx1->unlock();
133 locked = false;
134 }
135 }
136
137 static bool relock(QBasicMutex *mtx1, QBasicMutex *mtx2)
138 {
139 // mtx1 is already locked, mtx2 not... do we need to unlock and relock?
140 if (mtx1 == mtx2)
141 return false;
142 if (std::less<QBasicMutex *>()(mtx1, mtx2)) {
143 mtx2->lock();
144 return true;
145 }
146 if (!mtx2->tryLock()) {
147 mtx1->unlock();
148 mtx2->lock();
149 mtx1->lock();
150 }
151 return true;
152 }
153
154private:
155 QBasicMutex *mtx1, *mtx2;
156 bool locked;
157};
158
159class QBasicMutexLocker
160{
161public:
162 inline explicit QBasicMutexLocker(QBasicMutex *m) QT_MUTEX_LOCK_NOEXCEPT
163 : m(m), isLocked(true)
164 {
165 m->lock();
166 }
167 inline ~QBasicMutexLocker() { if (isLocked) unlock(); }
168
169 inline void unlock() noexcept
170 {
171 isLocked = false;
172 m->unlock();
173 }
174
175 inline void relock() QT_MUTEX_LOCK_NOEXCEPT
176 {
177 isLocked = true;
178 m->lock();
179 }
180
181private:
182 Q_DISABLE_COPY(QBasicMutexLocker)
183
184 QBasicMutex *m;
185 bool isLocked;
186};
187
188#else
189
190class QOrderedMutexLocker
191{
192public:
193 Q_DISABLE_COPY(QOrderedMutexLocker)
194 QOrderedMutexLocker(QBasicMutex *, QBasicMutex *) {}
195 QOrderedMutexLocker(QOrderedMutexLocker &&) = default;
196 QOrderedMutexLocker& operator=(QOrderedMutexLocker &&other) = default;
197 ~QOrderedMutexLocker() {}
198
199 void relock() {}
200 void unlock() {}
201 void dismiss() {}
202
203 static bool relock(QBasicMutex *, QBasicMutex *) { return false; }
204};
205
206using QBasicMutexLocker = QMutexLocker;
207
208#endif
209
210
211QT_END_NAMESPACE
212
213#endif
214

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