I'm using _Atomic integers inside of unions, such as:
union dedup_option_seq {
struct {
uint32_t seq :18; // enough bits for seq
uint32_t not_empty :1; // inverted so that zero is empty
};
_Atomic uint32_t value;
};
I can then happily use &foo.value
as a parameter in the atomic_...()
functions.
I can set unions' values using the syntax:
union dedup_option_seq foo = {
.seq = 42,
.not_empty = 1
};
It's all working nicely, but am I causing any undefined behaviour, or other issues, when using unions with an _Atomic member?
It's all working nicely, but am I causing any undefined behaviour, or other issues, when using unions with an _Atomic member?
C allows you to read back a different member of a union than the one that was last set. This much is ok in C (but not in C++). C does not restrict that based on the qualifiers (such as _Atomic
) of the types involved, so that is not directly a problem.
However,
_Atomic
types are not guaranteed to have the same size or representation as their corresponding non-_Atomic
counterparts, and
the layout of bitfields within a struct
is much less constrained than you probably think it is.
Between these, C allows of your example that
The representation of value
might not overlap the entirety of the representations of seq
and not_empty
. In an implementation where it does not, your attempts to manipulate those bits atomically via value
would not have the effect you expect.
Even if value
does overlap the entirety of seq
and not_empty
, some bits of the latter two might not correspond to value bits of the former. In that case, your attempts to manipulate those bits atomically via value
might not have the effect you expect.
Assigning to seq
and / or not_empty
might cause value
to contain a trap representation, even though ordinary uint32_t
does not have any trap representations. In an implementation where that can be the case, attempting to manipulate those bits atomically via value
could cause a trap, possibly crashing the program. Whether and when this happens could be data dependent.
Additionally, manipulating _Atomic
object value
non-atomically via other members of the union seems fraught. At minimum, such manipulations do not enjoy any of the atomicity guarantees that manipulating value
directly provides.
So is there undefined behavior here? Hard to say. There are definitely questions about the behavior that the language spec does not directly address. Some or all of that might be better characterized as unspecified than as undefined, but the former is not that much better than the latter if you require your program to reproducibly yield the same results on diverse C implementations.