Unsynchronised access to the same address (with certain conditions) is a data race, and that is undefined behaviour.
I think you are mixing up terms and contexts a little.
The C++ standards define behaviour for C++. Unsynchronised access to
the same address (with certain conditions) is a data race, and that is
undefined behaviour. The C++ standards do not say what will happen.
That does not mean that a program with two threads writing the same
object will crash, or launch nasal daemons. It may work perfectly well
for the task required. That might be by luck, or by timing, or by
guarantees from the hardware, or guarantees from the toolchain
implementation. But it is still UB according to the C++ standard - the
C++ standard does not define the behaviour here.
It is /not/ implementation-defined behaviour, because the C++ standard
does not say that it /is/ implementation-defined behaviour. The
standard could not reasonably be changed to say that, since
implementations in general have no way to define what happens during a
data race, and saying it is "implementation-defined" means that /all/
implementations would have to say exactly how they behave here. The
standard could not reasonably be changed to say "the behaviour may be
undefined or implementation-defined" because doing so would be pointless
- portable C++ code must assume it is UB, while for non-portable code
the implementation is always free to give defined behaviours to things
that are UB in the standards.
The program may have well-defined semantics and behaviour on a
particular implementation, while still being UB as C++. The semantics
of the code are determined by the behaviours defined by the standard and
additional guarantees from the implementation.
On 09/10/2026 09:55, jim x wrote:
> So, what behavior of the program do you think? If you think the program
> should be undefined behavior because the program contains a data race,
> let's step-by-step to go through the rules in [intro.race] p17. It's
> true that `#1` and `#2` are two potentially concurrent conflicting
> actions, and neither happens before the other. The crux is whether they
> satisfy the condition: at least one of which is not atomic. If one of
> them is non-atomic, then the condition is satisfied; therefore, the
> program contains a data race. If they are both atomic operations, the
> condition is false; no data race is contained. As I commented above,
> since the standard lets implementations define the semantics of the
> access, and a hypothetical implementation can define `#1` as `|
> std::atomic_ref<int>(value).load()|` and `#2` as `|
> std::atomic_ref<int>(value).store(1)|`; i.e., they're atomic operations,
> I don't think the crux condition in [intro.race] p17 is true. So, the
> program under this specified semantics shouldn't be UB. How do you
> explain this point?
>
> On Fri, Oct 9, 2026 at 3:39 PM David Brown <david.brown@hesbynett.no
> <mailto:david.brown@hesbynett.no>> wrote:
>
>
>
> On 09/10/2026 09:16, jim x via Std-Discussion wrote:
> > If the implementation defines that accesses to volatile variables
> > are atomic, then they are atomic.
> >
> >
> > In this case, does the program contain a data race as per
> [intro.race]
> > p17? If not, since the behavior depends on how the implementation
> define
> > the semantics, does that mean the behavior of the program should be
> > implementation-defined?
> >
>
> "Implementation-defined" in the standards does not just mean that the
> implementation has to treat the construct in some particular way. It
> means that the standard gives a range of options of how the
> implementation should handle the construct, and that the implementation
> must document how it handles them.
>
> If an implementation's manual says that volatile accesses are atomic
> (not unreasonable on single-core non-superscaler targets like most
> microcontrollers), then the programmer can rely on the volatile
> accesses
> being atomic. But that does not mean unsynchronised volatile accesses
> to the same address in different threads is implementation-defined
> behaviour, or should be called such in the C++ standards.
>
>