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
qatomicwait.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
"qatomicwait_p.h"
6
7
#
include
<
qendian
.
h
>
8
#
include
"qfutex_p.h"
9
#
include
"qwaitcondition_p.h"
10
11
#
include
<
array
>
12
#
include
<
condition_variable
>
13
#
include
<
mutex
>
14
15
QT_BEGIN_NAMESPACE
16
17
/*
18
* Implementation details:
19
*
20
* Normally, a call from atomic_notify_one() or atomic_notify_all() corresponds
21
* to a call to cond.notify_one() or cond.notify_all(). This simple
22
* implementation would require that we keep a distinct wait condition variable
23
* per atomic variable, of which there could be an arbitrary number.
24
*
25
* Instead, we have a limited set of std::condition_variable, which are
26
* selected based on the address of the variable being waited/notified on.
27
* Because multiple, distinct variables could be sharing the same condition
28
* variable, we need to perform a cond.notify_all() call in case the lock is
29
* contended by waiters to more than one address, as we can't be sure which one
30
* would be woken up by the cond.notify_one() call.
31
*
32
* We recover some of the performance of notifying a single waker by also
33
* storing the address of the variable that was being waited on. If it matches
34
* the address of the variable being notified, we can perform a notify_one()
35
* call. This also allows us to avoid any system call in case no waiter has yet
36
* joined the queue. In case of contention, we store a sentinel address
37
* instead, indicating there are waiters to multiple variables. The last waiter
38
* to leave a wait queue is responsible for resetting the watched address back
39
* to nullptr.
40
*
41
* Performance details:
42
*
43
* This implementation is designed for systems where neither the Standard
44
* Library's own std::atomic_wait nor operating system futexes are available
45
* (read: usually, not the systems Qt is used most frequently on). Therefore,
46
* it's designed for simplicity, not performance. Simplifications applied
47
* include:
48
* - the total number of possible condition variables
49
* - the hashing algorithm to select a condition variable (simple XOR)
50
* - no attempt to wait for a variable change before calling cond.wait()
51
* (no spinning, no HW-assisted wait)
52
*
53
* Other limitations:
54
*
55
* We only support 8-, 16-, 32- and 64-bit variables and we require bit-exact
56
* comparisons for all bits. This means we do not support objects with padding
57
* bits or without unique representations.
58
*/
59
60
namespace
{
61
struct
QAtomicWaitLocks
62
{
63
// Sentinel address used to indicate this Lock is being used by waiters to
64
// multiple addresses, implying we must make a notify_all() call.
65
static
void
*
contendedWatchAddress
() {
return
reinterpret_cast
<
void
*>(-1); }
66
67
struct
Lock
{
68
alignas
(QtPrivate::IdealMutexAlignment)
std
::mutex mutex;
69
alignas
(
QtPrivate
::
IdealMutexAlignment
)
std
::condition_variable
cond
;
70
71
// Can assume values:
72
// - nullptr: no waiter is waiting
73
// - contendedWatchAddress(): waiters to distinct addresses
74
// - any other value: all waiters are waiting on the same address
75
const
void
*
watchedAddress
=
nullptr
;
76
qsizetype
watcherCount
= 0;
77
};
78
79
static
constexpr
int
LockCount
= 16;
80
std
::array<
Lock
,
LockCount
>
locks
;
81
82
int
indexFor
(
const
void
*ptr)
83
{
84
static_assert
((
LockCount
& (
LockCount
- 1)) == 0,
"LockCount is not a power of two"
);
85
86
quintptr value = quintptr(ptr) /
sizeof
(
int
);
87
quintptr idx = value %
LockCount
;
88
#
ifndef
QATOMICWAIT_USE_FALLBACK
89
// XOR some higher bits too to avoid hashing collisions
90
// (we don't do it in the unit test because we *want* those collisions)
91
idx ^= (value /
LockCount
) %
LockCount
;
92
idx ^= (value /
LockCount
/
LockCount
) %
LockCount
;
93
#
endif
94
return
int
(idx);
95
}
96
97
Lock
&
lockFor
(
const
void
*ptr) {
return
locks[indexFor(ptr)]; }
98
};
99
}
// unnamed namespace
100
101
static
inline
void
checkFutexUse
()
102
{
103
#
if
!
defined
(
QATOMICWAIT_USE_FALLBACK
)
104
if
(QtFutex
::
futexAvailable
(
)
) {
105
// This will disable the code and data on systems where futexes are
106
// always available (currently: FreeBSD, Linux, Windows).
107
qFatal(
"Implementation should have used futex!"
);
108
}
109
#
endif
110
}
111
112
static
QAtomicWaitLocks
&
atomicLocks
()
noexcept
113
{
114
checkFutexUse
(
)
;
115
116
static
QAtomicWaitLocks
global {};
117
return
global;
118
}
119
120
template
<
typename
T>
static
bool
isEqual
(
const
void
*address,
const
void
*old)
121
{
122
auto
atomic =
static_cast
<
const
std::atomic<T> *>(address);
123
auto
expected =
static_cast
<
const
T *>(old);
124
return
atomic->load(std::memory_order_relaxed) == *expected;
125
}
126
127
static
bool
isEqual
(
const
void
*address,
const
void
*old, qsizetype size)
noexcept
128
{
129
switch
(size) {
130
case
1:
return
isEqual<quint8 >(address, old);
131
case
2:
return
isEqual<quint16>(address, old);
132
case
4:
return
isEqual<quint32>(address, old);
133
case
8:
return
isEqual<quint64>(address, old);
134
}
135
Q_UNREACHABLE_RETURN(
false
);
136
}
137
138
void
QtPrivate
::
_q_atomicWait
(
const
void
*address,
const
void
*old, qsizetype size)
noexcept
139
{
140
auto
&locker =
atomicLocks
(
)
.
lockFor
(
address
)
;
141
142
// NOT noexcept; we'll terminate if locking the mutex fails
143
std
::unique_lock lock(locker.mutex);
144
145
// is the value still current?
146
if
(!isEqual(address, old, size))
147
return
;
// no, failed to wait
148
149
if
(locker
.
watchedAddress
&& locker
.
watchedAddress
!= address)
150
locker
.
watchedAddress
=
QAtomicWaitLocks
::
contendedWatchAddress
(
)
;
151
else
152
locker
.
watchedAddress
= address;
153
++locker.watcherCount;
154
155
do
{
156
locker
.
cond
.wait(lock);
157
}
while
(isEqual(address, old, size));
158
159
if
(--locker.watcherCount == 0)
160
locker
.
watchedAddress
=
nullptr
;
161
}
162
163
void
QtPrivate
::
_q_atomicWake
(
void
*address,
WakeMode
mode)
noexcept
164
{
165
auto
&locker =
atomicLocks
(
)
.
lockFor
(
address
)
;
166
167
// NOT noexcept; we'll terminate if locking the mutex fails
168
std
::unique_lock lock(locker.mutex);
169
170
// can we wake just one?
171
if
(mode ==
WakeMode
::
One
&& locker
.
watchedAddress
== address)
172
locker
.
cond
.notify_one();
173
else
if
(locker
.
watchedAddress
!=
nullptr
)
174
locker
.
cond
.notify_all();
175
else
176
qt_noop();
// no one was waiting
177
}
178
179
QT_END_NAMESPACE
QtDummyFutex::futexAvailable
constexpr bool futexAvailable()
Definition
qfutex_p.h:25
QtPrivate
Definition
qalloc.h:29
QtPrivate::_q_atomicWake
Q_ATOMICWAIT_EXPORT void _q_atomicWake(void *address, WakeMode) noexcept
Definition
qatomicwait.cpp:163
QtPrivate::_q_atomicWait
Q_ATOMICWAIT_EXPORT void _q_atomicWait(const void *address, const void *old, qsizetype size) noexcept
Definition
qatomicwait.cpp:138
QtPrivate::WakeMode
WakeMode
Definition
qatomicwait_p.h:25
QtPrivate::WakeMode::One
@ One
Definition
qatomicwait_p.h:25
std
[33]
Definition
src_corelib_tools_qhash.cpp:421
isEqual
static bool isEqual(const void *address, const void *old)
Definition
qatomicwait.cpp:120
isEqual
static bool isEqual(const void *address, const void *old, qsizetype size) noexcept
Definition
qatomicwait.cpp:127
checkFutexUse
static void checkFutexUse()
Definition
qatomicwait.cpp:101
atomicLocks
static QAtomicWaitLocks & atomicLocks() noexcept
Definition
qatomicwait.cpp:112
QT_BEGIN_NAMESPACE::QAtomicWaitLocks::Lock
Definition
qatomicwait.cpp:67
QT_BEGIN_NAMESPACE::QAtomicWaitLocks::Lock::watchedAddress
const void * watchedAddress
Definition
qatomicwait.cpp:75
QT_BEGIN_NAMESPACE::QAtomicWaitLocks::Lock::cond
std::condition_variable cond
Definition
qatomicwait.cpp:69
QT_BEGIN_NAMESPACE::QAtomicWaitLocks::Lock::watcherCount
qsizetype watcherCount
Definition
qatomicwait.cpp:76
QT_BEGIN_NAMESPACE::QAtomicWaitLocks
Definition
qatomicwait.cpp:62
QT_BEGIN_NAMESPACE::QAtomicWaitLocks::indexFor
int indexFor(const void *ptr)
Definition
qatomicwait.cpp:82
QT_BEGIN_NAMESPACE::QAtomicWaitLocks::locks
std::array< Lock, LockCount > locks
Definition
qatomicwait.cpp:80
QT_BEGIN_NAMESPACE::QAtomicWaitLocks::contendedWatchAddress
static void * contendedWatchAddress()
Definition
qatomicwait.cpp:65
QT_BEGIN_NAMESPACE::QAtomicWaitLocks::LockCount
static constexpr int LockCount
Definition
qatomicwait.cpp:79
QT_BEGIN_NAMESPACE::QAtomicWaitLocks::lockFor
Lock & lockFor(const void *ptr)
Definition
qatomicwait.cpp:97
qtbase
src
corelib
thread
qatomicwait.cpp
Generated on
for Qt by
1.18.0