Is there a way to set, clear, test and flip a single bit as an atomic operation in c++? For example bitwise variants to "compare_and_swap".
Manipulating bits atomically requires a compare_exchange
RMW to avoid touching other bits in the atomic
variable.
Testing a bit is not a modifying operation, therefore a load()
suffices.
You will have to add range error checking
template<typename T, typename OP>
T manipulate_bit(std::atomic<T> &a, unsigned n, OP bit_op)
{
static_assert(std::is_integral<T>::value, "atomic type not integral");
T val = a.load();
while (!a.compare_exchange_weak(val, bit_op(val, n)));
return val;
}
auto set_bit = [](auto val, unsigned n) { return val | (1 << n); };
auto clr_bit = [](auto val, unsigned n) { return val & ~(1 << n); };
auto tgl_bit = [](auto val, unsigned n) { return val ^ (1 << n); };
int main()
{
std::atomic<int> a{0x2216};
manipulate_bit(a, 3, set_bit); // set bit 3
manipulate_bit(a, 7, tgl_bit); // toggle bit 7
manipulate_bit(a, 13, clr_bit); // clear bit 13
bool isset = (a.load() >> 5) & 1; // testing bit 5
}
return val;
, as knowing what you transitioned from is often useful. –
Aubervilliers compare_exchange_weak
returns false, it updates its first argument with the current value. For that reason, the first argument (val
), has to be an lvalue reference. –
Developer 1ULL<<n
or T(1)<<n
part isn't part of the atomic operation, so fetch_and/or/xor
are better ways to implement this. Hopefully a compiler for x86 would turn a.fetch_or(1<<n)
into lock bts
, if the return value is only tested at bit-position n
. Or into bts reg, reg
/ lock or
if the return value is unused. Otherwise the compiler will use a lock cmpxchg
retry loop for you, but it's still better to use a single fetch_op
in the source since it can compile efficiently for other ISAs, especially ARMv8.1 –
Dripps To set a bit atomically in a std::atomic<int> a
or std::atomic<unsigned int> a
,
use a.fetch_or(bit)
(also |=
is overloaded, but that won't let you use an order weaker than seq_cst)
To atomically clear a bit in an atomic integer, you can use a.fetch_and(~bit)
(also &=
)
To atomically flip a bit in an atomic integer, you can use a.fetch_xor(bit)
(also ^=
)
There are also non-member-function versions of these, like std::atomic_fetch_or(&a, bit)
, but in ISO C++ it's not safe to point a std::atomic_int*
at an int
object.
To do atomic operations on a plain int
variable, use C++20 std::atomic_ref auto foo_atomic = std::atomic_ref<int>( foo )
and do foo_atomic.fetch_or(bit)
. This is only safe if no other threads are reading or writing the underlying object, only via their own atomic_ref
objects. Also, be sure to declare the underlying int
with alignas(std::atomic_ref<int>::required_alignment)
, especially for integer types wider than int
.
int
and bool
s, even bitwise operations. std::atomic<int>
and std::atomic<bool>
are atomic, but the question and answer don't directly mention those. –
Kain int
. If they do, that's what C++20 std::atomic_ref is for (as long as no other threads can be accessing the plain int while you do this.) Updated this answer to make that clearer. –
Dripps Flipping a bit in an integer is just a compare and exchange operation. That you're using it to test and flip a single bit doesn't change anything. So a simple compare_exchange_weak
loop will do this.
compare_exchange_weak
to turn any non-atomic transformation of a value to an atomic one. –
Whitewall © 2022 - 2024 — McMap. All rights reserved.
std::atomic_bool
? – Knelt