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

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