问题
By my reading of the standard,
*(_Atomic TYPE*)&(TYPE){0}
(in words, casting a pointer to a non-atomic to a pointer to a corresponding atomic and dereferencing) isn't supported.
Do gcc and/or clang recognize it as an extension if TYPE
is/isn't lock-free? (Question 1)
Second and related question: I was under the impression that if TYPE
couldn't be implemented as a lock free atomic, a lock would need to be embedded in the corresponding _Atomic TYPE
. But if I make TYPE
a largish struct, then on both clang
and gcc
it has the same size as _Atomic TYPE
.
Code for both problems:
#include <stdatomic.h>
#include <stdio.h>
#if STRUCT
typedef struct {
int x;
char bytes[50];
} TYPE;
#else
typedef int TYPE;
#endif
TYPE x;
void f (_Atomic TYPE *X)
{
*X = (TYPE){0};
}
void use_f()
{
f((_Atomic TYPE*)(&x));
}
#include <stdio.h>
int main()
{
printf("%zu %zu\n", sizeof(TYPE), sizeof(_Atomic TYPE));
}
Now, if I compile the above snippet with -DSTRUCT
, both gcc and clang keep the both the struct and its atomic variant at the same size, and they generate a call to a function named __atomic_store
for the store (resolved by linking with -latomic
).
How does this work if no lock is embedded in the _Atomic
version of the struct? (Question 2)
回答1:
_Atomic
changes alignment in some corner cases on Clang, and GCC will likely be fixed in the future as well (PR 65146). In these cases, adding _Atomic
through a cast does not work (which is fine from a C standard point of view because it is undefined behavior, as you pointed out).
If the alignment is correct, it is more appropriate to use the __atomic
builtins, which have been designed for exactly this use case:
- Built-in Functions for Memory Model Aware Atomic Operations
As described above, this will not work in cases where the ABI provides insufficient alignment for plain (non-atomic) types, and where _Atomic
would change alignment (with Clang only for now).
These builtins also work in case of non-atomic types because they use out-of-line locks. This is also the reason why no additional storage is required for _Atomic
types, which use the same mechanism. This means that there is some unnecessary contention due to unintentional sharing of the locks. How these locks are implemented is an implementation detail which could change in future versions of libatomic
.
In general, for types with atomic builtins that involve locking, using them with shared or aliased memory mappings does not work. These builtins are not async-signal-safe, either. (All these features are technically outside the C standard anyway.)
来源:https://stackoverflow.com/questions/55299525/casting-pointers-to-atomic-pointers-and-atomic-sizes