I'm trying to find where the comparison semantics for the type T
with std::atomic
is defined.
I know that beside the builtin specializations for integral types, T
can be any TriviallyCopyable
type. But how do operations like compare_and_exchange_X
know how to compare an instance of T
?
I imagine they must simply do a byte by byte comparison of the user defined object (like a memcmp
) but I don't see where in the standard this is explicitly mentioned.
So, suppose I have:
struct foo
{
std::uint64_t x;
std::uint64_t y;
};
How does the compiler know how to compare two std::atomic<foo>
instances when I call std::atomic<foo>::compare_and_exchange_weak()
?
In draft n3936, memcmp
semantics are explicitly described in section 29.6.5.
Note: For example, the effect of atomic_compare_exchange_strong is
if (memcmp(object, expected, sizeof(*object)) == 0)
memcpy(object, &desired, sizeof(*object));
else
memcpy(expected, object, sizeof(*object));
and
Note: The memcpy
and memcmp
semantics of the compare-and-exchange operations may result in failed comparisons for values that compare equal with operator==
if the underlying type has padding bits, trap bits, or alternate representations of the same value.
That wording has been present at least since n3485.
Note that only memcmp(p1, p2, sizeof(T)) != 0
is meaningful to compare_and_exchange_weak
(failure guaranteed). memcmp(p1, p2, sizeof(T)) == 0
allows but does not guarantee success.
It's implementation defined. It could just be using a mutex lock or it could be using some intrinsics on memory blobs. The standard simply defines it such that the latter might work as an implementation strategy.
The compiler doesn't know anything here. It'll all be in the library. Since it's a template you can go read how your implementation does it.