Submitter: | Hans-J. Boehm |
---|---|
Sumission Date: | 2021-05-20 |
Doc. No.: | WG14 N2741 |
Email: | [email protected] |
Atomics for C and C++ were originally developed jointly and designed to be compatible.
However, this vision was never fully realized. WG21/P0943
was recently adopted into the C++23 wording paper. It adds a <stdatomic.h>
header
to the C++ standard to support shared C and C++ headers that mention atomics.
In doing so, it makes some assumptions about eventual representation compatibility
between atomics implementations in C and C++. We call this to the attention of
WG14, and propose some implementation guidance wording for the C standard.
WG21/P0943 provides a more thorough discussion of some of the issues here.
C's atomics were based on a design by WG21 which, at the request of WG14, included a C-compatible subset. The goal was to make this subset usable inside headers that can be included from either C or C++. Unfortunately this vision was long delayed by several obstacles:
stdatomic.h
until recently._Atomic
was added after our original plan, and its role
in the overall vision remained unclear.T
and atomic<T>
did not improve matters.
WG21/P0943 addresses the first two points
by providing a C++ header stdatomic.h
that essentially introduces
C-compatible global name space aliases for the facilities already available in C++'s atomic
header. It is expected to effectively macro-define _Atomic(T)
to
std::atomic<T>
(with more delicate wording to avoid
requiring inclusion of the atomic
header).
Using this to share headers between C and C++ works only if C compilers implement
_Atomic(T)
in the same way that C++ compilers implement
std::atomic<T>
. The same runtime data must be accessible as either,
thus returning us to the third issue.
P0943 cannot fully address this third problem,
in part because it is formally outside the domain of any specific standard. In addition
current implementations of C and C++, while they share a common representation for frequently used
types like _Atomic(int)
, often do not do so for less frequently used cases,
such as _Atomic(struct{ char a, b, c; })
or _Atomic(double)
on 32-bit platforms. (We also observed some similar incompatibilities between different
compilers compiling the same language on the same platform.)
There is general agreement that this last issue should be fixed, in spite of the ABI changes i the more obscure cases. It causes surprises, even in the absence of P0943, but it may take time to do so. Hence P0943 added the following to the current C++23 working draft:
Recommended Practice: Implementations should ensure that C and C++ representations of atomic objects are compatible, so that the same object can be accessed as both an
_Atomic(T)
from C code andatomic<T>
from C++ code. The representations should be the same, and the mechanisms used to ensure atomicity and memory ordering should be compatible.
This topic was discussed in the May 7, 2021 C and C++ compatibility study group meeting, with consensus, and no negative votes in either group, in favor of this direction. It was observed that the C standard currently contains no "recommended practice" wording, but suggested that it be proposed to WG14 anyway. Thus the purpose of this paper is two-fold: To call WG14's attention to this issue, and to propose that the above "recommended practice" wording also appear in the C standard.
The same study group meeting also spent a bit of time discussing the role of
the _Atomic
type qualifier, and whether it might serve as a substitute
for the recently introduced C++ atomic_ref<>
. This might
involve restricting the type qualifier to cases in which the representations of
T
and _Atomic T
are sufficiently compatible. There was
no consensus on how to proceed. An earlier reflector discussion had a similar
outcome. Hence there is no proposal here in that area.
We propose to add the same "recommended practice text" that currently appears in the C++ working paper (N4885, [stdatomic.h.syn], page 1563, paragraph 4) to the C standard, at a place of the editor's choice.
I.e., we propose to add:
Recommended Practice: Implementations should ensure that C and C++ representations of atomic objects are compatible, so that the same object can be accessed as both an
_Atomic(T)
from C code andatomic<T>
from C++ code. The representations should be the same, and the mechanisms used to ensure atomicity and memory ordering should be compatible.
Note that the last clause: "the mechanisms used to ensure atomicity and memory ordering should be compatible" in fact applies to more than C and C++: It should apply to any pair of languages that can be linked in the same executable. For example, a release store needs to ensure visibility of all prior memory accesses, not just those performed by C and C++; anything else results in very surprising behaviors. But here it seems cleaner just to replicate the C++ wording here.