This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
GCC extension for atomic access to members
- From: Florian Weimer <fweimer at redhat dot com>
- To: GCC <gcc at gcc dot gnu dot org>
- Date: Mon, 18 Sep 2017 14:19:01 +0200
- Subject: GCC extension for atomic access to members
- Authentication-results: sourceware.org; auth=none
- Authentication-results: ext-mx01.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com
- Authentication-results: ext-mx01.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=fweimer at redhat dot com
- Dmarc-filter: OpenDMARC Filter v1.3.2 mx1.redhat.com BBD7B81DF4
I would like to see the GCC project to document that if the address of a
member is taken, this does not constitute an access to the object as a
whole.
That is, in the following code:
#include <stdatomic.h>
struct S {
_Atomic int a;
int b;
};
int
load_a (struct S *p)
{
return atomic_load_explicit (&p->a, memory_order_relaxed);
}
int
store_b (struct S *p, int b)
{
p->b = b;
}
If one thread calls load_a and another thread calls store_b on the same
struct S *, no data race happens.
This is an extension over the C standard because of the way “->” is
defined. C requires that E1->E2 it is evaluated as (*(E1))->E2, and *E1
is defined as an access to the entire struct, so there is a data race in
load_a with the assignment in store_b.
This is somewhat complicated to fix, wording-wise, because for the
purpose of aliasing analysis, we need the whole-struct access to *E1,
otherwise there is nothing that asserts the dynamic type of this memory
location. But without such an assertion, the alias analysis GCC
currently performs would be wrong.
A similar extension is needed for access to embedded synchronization
types (such as mutexes), but it is even harder to express properly.
Of course, there is a workaround to make this issue go away, like this:
#define member_type(type, member) __typeof__ (((type) {}).member)
#define member_address(this, member) \
(member_type (__typeof__ (*(this)), member) *) \
(((char *) (this) + offsetof (__typeof__ (*(this)), member)))
int
load_a_ (struct S *p)
{
return atomic_load_explicit (member_address (p, a),
memory_order_relaxed);
}
int
store_b_ (struct S *p, int b)
{
*member_address (p, b) = b;
}
But it seems to be silly to write code this way. (We actually use these
macros in libio, where we have to implement C++ class inheritance in C.)
For C++, we can probably fix this in the standard in some way, so that
no extension is required.
Thanks,
Florian