Date: Fri, 9 Oct 2026 17:28:38 +0800
>
> Unsynchronised access to the same address (with certain conditions) is a
> data race, and that is undefined behaviour.
It's not true if the accesses are applied to the same atomic object. You
don't seem to understand my question. What I was asking is: are these two
operations, under the given semantics as shown in this question, atomic
operations under [intro.race] p17? If not, what's the reason?
On Fri, Oct 9, 2026 at 5:19 PM David Brown <david.brown_at_[hidden]> wrote:
>
> 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_at_[hidden]
> > <mailto:david.brown_at_[hidden]>> 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.
> >
> >
>
>
> Unsynchronised access to the same address (with certain conditions) is a
> data race, and that is undefined behaviour.
It's not true if the accesses are applied to the same atomic object. You
don't seem to understand my question. What I was asking is: are these two
operations, under the given semantics as shown in this question, atomic
operations under [intro.race] p17? If not, what's the reason?
On Fri, Oct 9, 2026 at 5:19 PM David Brown <david.brown_at_[hidden]> wrote:
>
> 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_at_[hidden]
> > <mailto:david.brown_at_[hidden]>> 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.
> >
> >
>
>
Received on 2026-10-09 09:28:57
