Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qlatch.cpp
Go to the documentation of this file.
1
// Copyright (C) 2025 Intel Corporation.
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
#
include
"qlatch_p.h"
6
7
#
include
"qatomicwait_p.h"
8
#
include
"qfutex_p.h"
9
10
QT_BEGIN_NAMESPACE
11
12
using
namespace
QtFutex;
13
14
#
if
defined
(
QATOMICWAIT_USE_FALLBACK
)
15
static
constexpr
bool
ForcedFallbackAtomicWait =
true
;
16
namespace
atomicwait = QtFallbackAtomicWait;
17
#
else
18
static
constexpr
bool
ForcedFallbackAtomicWait
=
false
;
19
namespace
atomicwait =
q20
;
20
#
endif
21
22
/*!
23
\class QLatch
24
\internal
25
26
Implements the same API as \c std::latch (C++20), allowing a single
27
synchronization between threads.
28
29
\section2 Typical uses
30
\section3 Waiting for threaded work to finish
31
32
For this use-case, one or more threads perform some work, which needs to
33
finish before the caller thread can proceed. For this, each worker thread
34
calls countDown() once they have finished their work, while the caller
35
thread suspends execution by calling wait().
36
37
The operation is best seen in:
38
\code
39
QLatch latch(segments);
40
int y = 0;
41
for (int i = 0; i < segments; ++i) {
42
int yn = (data->height - y) / (segments - i);
43
threadPool->start([&, y, yn]() {
44
convertSegment(y, y + yn);
45
latch.countDown();
46
});
47
y += yn;
48
}
49
latch.wait();
50
\endcode
51
52
Or, for a single thread:
53
\code
54
QLatch latch(1);
55
QMetaObject::invokeMethod(object, [&]() {
56
doSomething();
57
latch.countDown();
58
}, Qt::QueuedConnection);
59
latch.wait();
60
\endcode
61
62
In fact, the above is exactly what Qt::BlockingQueued connection does.
63
64
\section3 Synchronizing execution
65
66
For this use-case, multiple threads must reach a particular state before
67
any of them may proceed. In this case, all of them call arriveAndWait(),
68
causing all but the last one of them to suspend execution until that last
69
one also arrives.
70
71
\code
72
QLatch latch(n);
73
for (int i = 0; i < n; ++i) {
74
threadPool->start([] {
75
latch.arriveAndWait();
76
doStressfulWork();
77
});
78
}
79
\endcode
80
81
\section2 Differences from \c std::latch
82
83
\list
84
\li Uses \c{int} in the API instead of \c{ptrdiff_t} (note that the max()
85
is the same as libstdc++'s on Linux).
86
\li count_down() is not \c{const} (libstdc++ implementation is).
87
\endlist
88
89
\omit
90
\section2 Implementation details
91
92
countDown() must call wakeUp() if the latch counter reaches zero and there
93
are threads waiting to be woken up. Or, conversely, countDown() needs to do
94
nothing after decrementing if the latch counter is still non-zero or there
95
are no waiters. Therefore, we choose the bits so that a non-zero
96
\c{counter} member implies no action required.
97
98
\endomit
99
*/
100
101
/*!
102
\fn QLatch::QLatch(int expected) noexcept
103
104
Initializes the QLatch to indicate that countDown() will be called \a
105
expected times. You probably want to pass a value greater than zero.
106
*/
107
108
/*!
109
\fn int QLatch::pending() noexcept
110
\internal
111
112
Returns the counter.
113
114
Don't use; for the unit test only.
115
*/
116
117
/*!
118
\fn void QLatch::countDown(int n) noexcept
119
\fn void QLatch::count_down(int n) noexcept
120
121
Decrements the internal counter by \a n. If the internal counter drops to
122
zero after this operation, any threads currently waiting will be woken up.
123
If \a n is greater than the value of the internal counter or is negative,
124
the behavior is undefined.
125
126
This function does not block and may be used to notify waiters that this
127
thread has reached a particular point and they may proceed. To synchronize
128
all threads so they all resume work at the same time, use arriveAndWait().
129
130
This function implements release memory ordering.
131
132
\sa arriveAndWait(), wait()
133
*/
134
135
/*!
136
\fn bool QLatch::tryWait() const noexcept
137
\fn void QLatch::try_wait() const noexcept
138
139
Returns true if the internal counter in this latch has dropped to zero,
140
false otherwise. This function does not block.
141
142
This function implements acquire memory ordering.
143
144
\sa wait(), countDown()
145
*/
146
147
/*!
148
\fn void QLatch::wait() noexcept
149
150
Waits for the internal counter in this latch to drop to zero.
151
152
This function implements acquire memory ordering.
153
154
\sa tryWait(), arriveAndWait(), countDown()
155
*/
156
157
/*!
158
\fn void QLatch::arriveAndWait(int n) noexcept
159
\fn void QLatch::arrive_and_wait(int n) noexcept
160
161
This function decrements the internal counter by \a n. If the counter
162
remains non-zero after this operation, it suspends the current thread until
163
it does become zero. Otherwise it wakes all other current waiters.
164
165
This function is useful to synchronize multiple threads so they may start
166
some execution at (nearly) exactly the same time.
167
168
This function is exactly equivalent to:
169
\code
170
countDown(n);
171
wait();
172
\endcode
173
174
This function implements acquire-and-release memory ordering.
175
176
\sa countDown(), wait()
177
*/
178
179
/*!
180
\fn int QLatch::max() noexcept
181
182
Returns the maximum number that can be passed to the constructor.
183
*/
184
185
void
QLatch::waitInternal(
int
current)
noexcept
186
{
187
// mark that there is a waiter -> clear the bit that there are no waiters
188
if
(current & NoWaiters) {
189
#
if
__has_builtin
(
__atomic_and_fetch
)
190
// Modern GCC and Clang are able to generate loop-free code for this
191
// operation on x86-64, ARMv8.1 and RISC-V.
192
if
(__atomic_and_fetch(
reinterpret_cast
<
int
*>(&counter._q_value), ~NoWaiters,
193
int
(std::memory_order_relaxed)) == 0)
194
return
;
195
#
else
196
// Do it in two steps, which is usually better than a compare_exchange
197
// loop. This is not exactly the same as above (it's not atomic!) but
198
// is correct for our purposes because the counter never changes from 0
199
// once it reaches that.
200
counter.fetchAndAndRelaxed(~NoWaiters);
201
if
(counter.loadRelaxed() == 0)
202
return
;
// no need to wait!
203
#
endif
204
}
205
current &= ~NoWaiters;
206
207
auto
waitLoop = [&](
auto
waiter) {
208
do
{
209
waiter(current);
210
}
while
((current = counter.loadAcquire()) != 0);
211
};
212
213
if
(futexAvailable() && !
ForcedFallbackAtomicWait
)
214
waitLoop([&](
int
current) { futexWait(counter, current); });
215
else
216
waitLoop([&](
int
current) {
217
atomicwait::atomic_wait_explicit(&counter._q_value, current, std::memory_order_relaxed);
218
});
219
}
220
221
void
QLatch::wakeUp()
noexcept
222
{
223
if
(futexAvailable() && !
ForcedFallbackAtomicWait
)
224
futexWakeAll(counter);
225
else
226
atomicwait::atomic_notify_all(&counter._q_value);
227
}
228
229
QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
q20
Definition
qnumeric.cpp:467
__has_builtin
#define __has_builtin(x)
Definition
qcompilerdetection.h:444
ForcedFallbackAtomicWait
static constexpr bool ForcedFallbackAtomicWait
Definition
qlatch.cpp:18
qtbase
src
corelib
thread
qlatch.cpp
Generated on
for Qt by
1.18.0