Coverage Report

Created: 2026-08-14 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boost/boost/atomic/detail/futex.hpp
Line
Count
Source
1
/*
2
 * Distributed under the Boost Software License, Version 1.0.
3
 * (See accompanying file LICENSE_1_0.txt or copy at
4
 * http://www.boost.org/LICENSE_1_0.txt)
5
 *
6
 * Copyright (c) 2020-2025 Andrey Semashev
7
 */
8
/*!
9
 * \file   atomic/detail/futex.hpp
10
 *
11
 * This header defines wrappers around futex syscall.
12
 *
13
 * http://man7.org/linux/man-pages/man2/futex.2.html
14
 * https://man.openbsd.org/futex
15
 */
16
17
#ifndef BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_
18
#define BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_
19
20
#include <boost/atomic/detail/config.hpp>
21
22
#ifdef BOOST_HAS_PRAGMA_ONCE
23
#pragma once
24
#endif
25
26
#if defined(__linux__)
27
28
#include <sys/syscall.h>
29
30
#if defined(SYS_futex)
31
#define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS_futex
32
#elif defined(SYS_futex_time64)
33
// On some 32-bit targets (e.g. riscv32) SYS_futex is not defined and instead SYS_futex_time64 is implemented,
34
// which is equivalent to SYS_futex but uses 64-bit time_t.
35
#define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS_futex_time64
36
#define BOOST_ATOMIC_DETAIL_FUTEX_TIME64
37
#elif defined(__NR_futex)
38
// Some Android NDKs (Google NDK and older Crystax.NET NDK versions) don't define SYS_futex.
39
#define BOOST_ATOMIC_DETAIL_SYS_FUTEX __NR_futex
40
#endif
41
42
#elif defined(__OpenBSD__)
43
44
// OpenBSD provides futex(2) function wrapper since OpenBSD 6.2 (https://man.openbsd.org/OpenBSD-6.2/futex.2).
45
// It has also removed syscall(2) interface:
46
// https://github.com/openbsd/src/commit/cafeb892b121ee89c39c2b940e8ccd6950f50009
47
48
#include <sys/param.h>
49
#include <cerrno>
50
51
#if OpenBSD >= 201711
52
#define BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX
53
#endif // OpenBSD >= 201711
54
55
#elif defined(__NETBSD__) || defined(__NetBSD__)
56
57
#include <sys/syscall.h>
58
59
#if defined(SYS___futex)
60
// NetBSD defines SYS___futex, which has slightly different parameters. Basically, it has decoupled timeout and val2 parameters:
61
// int __futex(int *addr1, int op, int val1, const struct timespec *timeout, int *addr2, int val2, int val3);
62
// https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/syscall.h
63
// http://bxr.su/NetBSD/sys/kern/sys_futex.c
64
#define BOOST_ATOMIC_DETAIL_SYS_FUTEX SYS___futex
65
#define BOOST_ATOMIC_DETAIL_NETBSD_FUTEX
66
#endif // defined(SYS___futex)
67
68
#endif
69
70
#if defined(BOOST_ATOMIC_DETAIL_SYS_FUTEX) || defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX)
71
72
#if defined(__linux__)
73
#include <linux/futex.h>
74
#else
75
#include <sys/futex.h>
76
#endif
77
#include <time.h> // timespec
78
#include <cstdint>
79
#include <boost/atomic/detail/intptr.hpp>
80
#include <boost/atomic/detail/header.hpp>
81
82
#define BOOST_ATOMIC_DETAIL_HAS_FUTEX
83
84
// Note: On Android, futex.h is lacking many definitions, but the actual Linux kernel supports the API in full.
85
#if defined(FUTEX_WAIT_BITSET)
86
#define BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET FUTEX_WAIT_BITSET
87
#elif defined(__ANDROID__)
88
#define BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET 9
89
#endif
90
91
#if defined(FUTEX_PRIVATE_FLAG)
92
#define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG FUTEX_PRIVATE_FLAG
93
#elif defined(__ANDROID__)
94
#define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG 128
95
#else
96
#define BOOST_ATOMIC_DETAIL_FUTEX_PRIVATE_FLAG 0
97
#endif
98
99
#if defined(FUTEX_CLOCK_REALTIME)
100
#define BOOST_ATOMIC_DETAIL_FUTEX_CLOCK_REALTIME FUTEX_CLOCK_REALTIME
101
#elif defined(__ANDROID__)
102
#define BOOST_ATOMIC_DETAIL_FUTEX_CLOCK_REALTIME 256
103
#endif
104
105
namespace boost {
106
namespace atomics {
107
namespace detail {
108
109
#if defined(BOOST_ATOMIC_DETAIL_FUTEX_TIME64)
110
111
//! An equivalent of `timespec` that uses 64-bit members when the userland `timespec` is 32-bit
112
struct futex_timespec
113
{
114
    std::int64_t tv_sec;
115
    std::int64_t tv_nsec;
116
117
    futex_timespec() = default;
118
    explicit futex_timespec(::timespec ts) noexcept :
119
        tv_sec(ts.tv_sec), tv_nsec(ts.tv_nsec)
120
    {}
121
};
122
123
#else // defined(BOOST_ATOMIC_DETAIL_FUTEX_TIME64)
124
125
using futex_timespec = ::timespec;
126
127
#endif // defined(BOOST_ATOMIC_DETAIL_FUTEX_TIME64)
128
129
//! Invokes an operation on the futex
130
BOOST_FORCEINLINE int futex_invoke(void* addr1, int op, unsigned int val1, const futex_timespec* timeout = nullptr, void* addr2 = nullptr, unsigned int val3 = 0u) noexcept
131
0
{
132
0
#if defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX)
133
0
    return ::futex
134
0
    (
135
0
        static_cast< volatile std::uint32_t* >(addr1),
136
0
        op,
137
0
        static_cast< int >(val1),
138
0
        timeout,
139
0
        static_cast< volatile std::uint32_t* >(addr2)
140
0
    );
141
0
#elif defined(BOOST_ATOMIC_DETAIL_NETBSD_FUTEX)
142
0
    // Pass 0 in val2.
143
0
    return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, timeout, addr2, 0u, val3);
144
0
#else
145
0
    return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, timeout, addr2, val3);
146
0
#endif
147
0
}
148
149
//! Invokes an operation on the futex
150
BOOST_FORCEINLINE int futex_invoke(void* addr1, int op, unsigned int val1, unsigned int val2, void* addr2 = nullptr, unsigned int val3 = 0u) noexcept
151
0
{
152
0
#if defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX)
153
0
    return ::futex
154
0
    (
155
0
        static_cast< volatile std::uint32_t* >(addr1),
156
0
        op,
157
0
        static_cast< int >(val1),
158
0
        reinterpret_cast< const futex_timespec* >(static_cast< atomics::detail::uintptr_t >(val2)),
159
0
        static_cast< volatile std::uint32_t* >(addr2)
160
0
    );
161
0
#elif defined(BOOST_ATOMIC_DETAIL_NETBSD_FUTEX)
162
0
    // Pass nullptr in timeout.
163
0
    return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, static_cast< void* >(nullptr), addr2, val2, val3);
164
0
#else
165
0
    return ::syscall(BOOST_ATOMIC_DETAIL_SYS_FUTEX, addr1, op, val1, static_cast< atomics::detail::uintptr_t >(val2), addr2, val3);
166
0
#endif
167
0
}
168
169
//! Checks that the value \c pval is \c expected and blocks
170
BOOST_FORCEINLINE int futex_wait(void* pval, unsigned int expected, int flags) noexcept
171
0
{
172
0
    int res = futex_invoke(pval, FUTEX_WAIT | flags, expected);
173
0
#if defined(OpenBSD) && (OpenBSD < 202111)
174
0
    // In older OpenBSD versions, futex(2) returned error code directly instead of setting errno and returning -1.
175
0
    // This was fixed in OpenBSD 7.0 (https://github.com/openbsd/src/commit/3288ea8fbfe504db25b57dd18b664a1aa377e4bf).
176
0
    // This primarily affects FUTEX_WAIT. For FUTEX_WAKE and FUTEX_REQUEUE the returned value may be positive
177
0
    // on successful completion of the call and there seem to be no errors that can be returned. Other functions
178
0
    // are not supported on OpenBSD 7.0 and older.
179
0
    if (res > 0)
180
0
    {
181
0
        errno = res;
182
0
        res = -1;
183
0
    }
184
0
#endif // defined(OpenBSD) && (OpenBSD < 202111)
185
0
    return res;
186
0
}
187
188
//! Checks that the value \c pval is \c expected and blocks until timeout
189
BOOST_FORCEINLINE int futex_wait_for(void* pval, unsigned int expected, futex_timespec const& timeout, int flags) noexcept
190
0
{
191
0
    int res = futex_invoke(pval, FUTEX_WAIT | flags, expected, &timeout);
192
0
#if defined(OpenBSD) && (OpenBSD < 202111)
193
0
    // See the comment in futex_wait
194
0
    if (res > 0)
195
0
    {
196
0
        errno = res;
197
0
        res = -1;
198
0
    }
199
0
#endif // defined(OpenBSD) && (OpenBSD < 202111)
200
0
    return res;
201
0
}
202
203
#if defined(BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET)
204
205
//! Checks that the value \c pval is \c expected and blocks until timeout
206
BOOST_FORCEINLINE int futex_wait_until(void* pval, unsigned int expected, futex_timespec const& timeout, int flags) noexcept
207
0
{
208
0
    return futex_invoke(pval, BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET | flags, expected, &timeout, nullptr, ~static_cast< unsigned int >(0u));
209
0
}
210
211
#endif // defined(BOOST_ATOMIC_DETAIL_FUTEX_WAIT_BITSET)
212
213
//! Wakes the specified number of threads waiting on the futex
214
BOOST_FORCEINLINE int futex_signal(void* pval, int flags, unsigned int count = 1u) noexcept
215
0
{
216
0
    return futex_invoke(pval, FUTEX_WAKE | flags, count);
217
0
}
218
219
//! Wakes all threads waiting on the futex
220
BOOST_FORCEINLINE int futex_broadcast(void* pval, int flags) noexcept
221
0
{
222
0
    return futex_signal(pval, flags, (~static_cast< unsigned int >(0u)) >> 1u);
223
0
}
224
225
//! Wakes the wake_count threads waiting on the futex pval1 and requeues up to requeue_count of the blocked threads onto another futex pval2
226
BOOST_FORCEINLINE int futex_requeue
227
(
228
    void* pval1,
229
    void* pval2,
230
    int flags,
231
    unsigned int wake_count = 1u,
232
    unsigned int requeue_count = (~static_cast< unsigned int >(0u)) >> 1u
233
) noexcept
234
0
{
235
0
    return futex_invoke(pval1, FUTEX_REQUEUE | flags, wake_count, requeue_count, pval2);
236
0
}
237
238
} // namespace detail
239
} // namespace atomics
240
} // namespace boost
241
242
#include <boost/atomic/detail/footer.hpp>
243
244
#endif // defined(BOOST_ATOMIC_DETAIL_SYS_FUTEX) || defined(BOOST_ATOMIC_DETAIL_OPENBSD_FUTEX)
245
246
#endif // BOOST_ATOMIC_DETAIL_FUTEX_HPP_INCLUDED_