Date: Sat, 10 Oct 2026 16:44:58 +0800
Or put another way, could you explain which rule in the standard you relied
on to determine that the program exhibits undefined behavior in this
example? If I had to guess, you would probably cite [intro.race] p17. In
that case, you should explain which conditions in p17 are satisfied by this
example that lead the rule to classify it as a data race. Once you go
through this step-by-step reasoning, you'll realize what my question was
actually getting at. That is, why are operations at `#1` and `#2`
considered non-atomic operations when their semantics are
implementation-defined?
On Sat, Oct 10, 2026 at 4:10 PM Tiago Freire <tmiguelf_at_[hidden]> wrote:
> What is undefined behavior? What's the difference between
> implementation-defined, and undefined behavior? And why would we want
> things to have undefined behavior?
>
> I think the confusion starts there.
>
> I don't know of any system that implements volatile as atomic, so this is
> theoretical, but sure you could technically make your own compiler and
> define your volatile to be atomic, ok great.
> You could even go one step further and find every single instance of
> undefined behavior and make it do something predictable. Ok,
> congratulations you made a C++ compiler, one that is well behaved always.
>
> But as far as the standard is concerned nothing has changed. I don't use
> your compiler, I use 6 other different ones, some of them are allowed to
> release flying monkeys from my hard drive, and they are still valid C++
> compilers.
>
> Me as a user cannot rely on code using undefined behavior to behave
> predictably on all compilers, as I may not use your "well defined"
> exclusively.
>
> If it was "implementation-defined" on the other hand, I may still get
> different behavior on different compilers but in theory I should be able to
> read the compiler's manual and get consistent behavior in every compiler
> (as long as I appropriately make all of the #if pre-compiler directives
> right) and there will be no flying monkeys.
>
> As a compiler vendor:
> implementation-defined = I must pick a behavior and do that consistently
> always.
> undefined behavior = I can do whatever I want, including having different
> results in the exact same circumstances.
>
> As a user:
> implementation-defined = It's not consistent in every compiler, but if I
> stick to one compiler it will do what it says in the documentation
> consistently.
> undefined means = this may not be documented, and may not have consistent
> behavior even in the same compiler.
>
>
>
>
>
>
> ------------------------------
> *From:* jim x <xmh970252187_at_[hidden]>
> *Sent:* Saturday, October 10, 2026 3:49:24 AM
> *To:* Tiago Freire <tmiguelf_at_[hidden]>
> *Cc:* std-discussion_at_[hidden] <std-discussion_at_[hidden]>;
> Jennifier Burnett <jenni_at_[hidden]>
> *Subject:* Re: [std-discussion] Is this program undefined-behavior or
> implementation-defined behavior?
>
> “volatile” is not atomic, I don’t know where you got that idea.
>
>
> The idea is sourced from, since the standard states that the semantics of
> access through volatile glvalue are implementation-defined, an
> implementation can define the operation at `#1` as equivalent to `
> std::atomic_ref<int>(value).load()`, and the operation at `#2` as
> equivalent to `std::atomic_ref<int>(value).store()`, both are atomic
> operations defined in <atomic>, so under the specified semantics, why don't
> we consider them atomic?
>
> On Fri, Oct 9, 2026 at 9:19 PM Tiago Freire <tmiguelf_at_[hidden]> wrote:
>
>> 1. “volatile” is not atomic, I don’t know where you got that idea.
>>
>> 2. Even with atomics there would be a data race, at least in the sense
>> that running it multiple times will yield different results. Atomics only
>> guarantee visibility of different types of side-effects (depending on the
>> type of atomic operation) they do not the synchronize the execution across
>> different threads. This is not exactly UB, what it does is actually very
>> well defined, but the outcome of the operation is dependent on a factor
>> that you neither control or the language specifies.
>>
>>
>>
>>
>>
>> *From:* Std-Discussion <std-discussion-bounces_at_[hidden]> *On
>> Behalf Of *jim x via Std-Discussion
>> *Sent:* Friday, October 9, 2026 08:28
>> *To:* Jennifier Burnett <jenni_at_[hidden]>
>> *Cc:* jim x <xmh970252187_at_[hidden]>; jim x via Std-Discussion <
>> std-discussion_at_[hidden]>
>> *Subject:* Re: [std-discussion] Is this program undefined-behavior or
>> implementation-defined behavior?
>>
>>
>>
>> The issue is not whether an implementation can guarantee a specific
>> behavior for an operation that has UB. The issue is why the operations at
>> `#1` and `#2` are still considered non-atomic under the concrete
>> semantics: An access to a volatile int glvalue value is defined to have
>> the exact semantics of std::atomic_ref<int>(value).load() for reads, and
>> std::atomic_ref<int>(value).store() for writes.
>>
>>
>>
>> The premise you think the program is UB is that you think such two
>> operations are non-atomic and therefore cause the program to contain a data
>> race as per [intro.race] p17. My question is why such two operations are
>> non-atomic under [intro.race] p17 when their semantics are specified as: An
>> access to a volatile int glvalue value is defined to have the exact
>> semantics of std::atomic_ref<int>(value).load() for reads, and
>> std::atomic_ref<int>(value).store() for writes.
>>
>>
>>
>> On Fri, Oct 9, 2026 at 2:16 PM Jennifier Burnett <jenni_at_[hidden]>
>> wrote:
>>
>> I'm not sure there's a conflict? Implementations are perfectly allowed to
>> define undefined behaviour to have some specific behaviour. I'm allowed to
>> create an implementation that has a defined behaviour for *all* data races,
>> for example. The standard says that anything can happen when undefined
>> behaviour occurs, and implementation-defined behaviour does fall under the
>> category of "anything".
>>
>>
>>
>> On 9 October 2026 06:11:15 BST, jim x via Std-Discussion <
>> std-discussion_at_[hidden]> wrote:
>>
>> Consider this example:
>>
>> ````cpp
>>
>> #include
>>
>>
>>
>> int main(){
>>
>> volatile int value = 0;
>>
>> std::jthread t1([&](){
>>
>> auto i = value; // #1
>>
>> });
>>
>> std::jthread t2([&](){
>>
>> value = 1; // #2
>>
>> });
>>
>> }
>>
>> ````
>>
>> People think this program should contain a data race because `#1` and
>> `#2` are *non-atomic* operations, which satisfies the condition in [intro.races]
>> p17
>> <https://eel.is/c++draft/intro.races#17>
>>
>> Two actions are *potentially concurrent*
>> <https://eel.is/c++draft/intro.races#def:potentially_concurrent> if
>>
>> they are performed by different threads, or
>> <https://eel.is/c++draft/intro.races#17>
>>
>> The execution of a program contains a data race if it contains two
>> potentially concurrent conflicting actions, *at least one of which is
>> not atomic*, and neither happens before the other, except for the
>> special case for signal handlers described below. Any such data race
>> results in undefined behavior ([ub:intro.races.data]).
>>
>>
>>
>> However, this opinion is based on the premise that they consider the
>> operations at `#1` and `#2` to be non-atomic operations in any way.
>> However, [dcl.type.cv] p5 <https://eel.is/c++draft/dcl.type.cv#5>
>> simultaneously states:
>>
>> *The semantics of an access through a volatile glvalue are
>> implementation-defined.*
>>
>>
>>
>> How about if a hypothetical implementation specifies the semantics as
>> follows:
>>
>> An access to a volatile int glvalue value is defined to have the exact
>> semantics of std::atomic_ref<int>(value).load() for reads, and
>> std::atomic_ref<int>(value).store() for writes.
>>
>>
>>
>> Under these specified semantics, `#1` and `#2` should be considered as
>> atomic operations, which won't satisfy the condition in [intro.race] p17,
>> therefore no data race. From this perspective, isn't the program
>> implementation-defined behavior? That is, whether the program contains a
>> data race depends on how the implementation specifies the semantics
>>
>>
>
on to determine that the program exhibits undefined behavior in this
example? If I had to guess, you would probably cite [intro.race] p17. In
that case, you should explain which conditions in p17 are satisfied by this
example that lead the rule to classify it as a data race. Once you go
through this step-by-step reasoning, you'll realize what my question was
actually getting at. That is, why are operations at `#1` and `#2`
considered non-atomic operations when their semantics are
implementation-defined?
On Sat, Oct 10, 2026 at 4:10 PM Tiago Freire <tmiguelf_at_[hidden]> wrote:
> What is undefined behavior? What's the difference between
> implementation-defined, and undefined behavior? And why would we want
> things to have undefined behavior?
>
> I think the confusion starts there.
>
> I don't know of any system that implements volatile as atomic, so this is
> theoretical, but sure you could technically make your own compiler and
> define your volatile to be atomic, ok great.
> You could even go one step further and find every single instance of
> undefined behavior and make it do something predictable. Ok,
> congratulations you made a C++ compiler, one that is well behaved always.
>
> But as far as the standard is concerned nothing has changed. I don't use
> your compiler, I use 6 other different ones, some of them are allowed to
> release flying monkeys from my hard drive, and they are still valid C++
> compilers.
>
> Me as a user cannot rely on code using undefined behavior to behave
> predictably on all compilers, as I may not use your "well defined"
> exclusively.
>
> If it was "implementation-defined" on the other hand, I may still get
> different behavior on different compilers but in theory I should be able to
> read the compiler's manual and get consistent behavior in every compiler
> (as long as I appropriately make all of the #if pre-compiler directives
> right) and there will be no flying monkeys.
>
> As a compiler vendor:
> implementation-defined = I must pick a behavior and do that consistently
> always.
> undefined behavior = I can do whatever I want, including having different
> results in the exact same circumstances.
>
> As a user:
> implementation-defined = It's not consistent in every compiler, but if I
> stick to one compiler it will do what it says in the documentation
> consistently.
> undefined means = this may not be documented, and may not have consistent
> behavior even in the same compiler.
>
>
>
>
>
>
> ------------------------------
> *From:* jim x <xmh970252187_at_[hidden]>
> *Sent:* Saturday, October 10, 2026 3:49:24 AM
> *To:* Tiago Freire <tmiguelf_at_[hidden]>
> *Cc:* std-discussion_at_[hidden] <std-discussion_at_[hidden]>;
> Jennifier Burnett <jenni_at_[hidden]>
> *Subject:* Re: [std-discussion] Is this program undefined-behavior or
> implementation-defined behavior?
>
> “volatile” is not atomic, I don’t know where you got that idea.
>
>
> The idea is sourced from, since the standard states that the semantics of
> access through volatile glvalue are implementation-defined, an
> implementation can define the operation at `#1` as equivalent to `
> std::atomic_ref<int>(value).load()`, and the operation at `#2` as
> equivalent to `std::atomic_ref<int>(value).store()`, both are atomic
> operations defined in <atomic>, so under the specified semantics, why don't
> we consider them atomic?
>
> On Fri, Oct 9, 2026 at 9:19 PM Tiago Freire <tmiguelf_at_[hidden]> wrote:
>
>> 1. “volatile” is not atomic, I don’t know where you got that idea.
>>
>> 2. Even with atomics there would be a data race, at least in the sense
>> that running it multiple times will yield different results. Atomics only
>> guarantee visibility of different types of side-effects (depending on the
>> type of atomic operation) they do not the synchronize the execution across
>> different threads. This is not exactly UB, what it does is actually very
>> well defined, but the outcome of the operation is dependent on a factor
>> that you neither control or the language specifies.
>>
>>
>>
>>
>>
>> *From:* Std-Discussion <std-discussion-bounces_at_[hidden]> *On
>> Behalf Of *jim x via Std-Discussion
>> *Sent:* Friday, October 9, 2026 08:28
>> *To:* Jennifier Burnett <jenni_at_[hidden]>
>> *Cc:* jim x <xmh970252187_at_[hidden]>; jim x via Std-Discussion <
>> std-discussion_at_[hidden]>
>> *Subject:* Re: [std-discussion] Is this program undefined-behavior or
>> implementation-defined behavior?
>>
>>
>>
>> The issue is not whether an implementation can guarantee a specific
>> behavior for an operation that has UB. The issue is why the operations at
>> `#1` and `#2` are still considered non-atomic under the concrete
>> semantics: An access to a volatile int glvalue value is defined to have
>> the exact semantics of std::atomic_ref<int>(value).load() for reads, and
>> std::atomic_ref<int>(value).store() for writes.
>>
>>
>>
>> The premise you think the program is UB is that you think such two
>> operations are non-atomic and therefore cause the program to contain a data
>> race as per [intro.race] p17. My question is why such two operations are
>> non-atomic under [intro.race] p17 when their semantics are specified as: An
>> access to a volatile int glvalue value is defined to have the exact
>> semantics of std::atomic_ref<int>(value).load() for reads, and
>> std::atomic_ref<int>(value).store() for writes.
>>
>>
>>
>> On Fri, Oct 9, 2026 at 2:16 PM Jennifier Burnett <jenni_at_[hidden]>
>> wrote:
>>
>> I'm not sure there's a conflict? Implementations are perfectly allowed to
>> define undefined behaviour to have some specific behaviour. I'm allowed to
>> create an implementation that has a defined behaviour for *all* data races,
>> for example. The standard says that anything can happen when undefined
>> behaviour occurs, and implementation-defined behaviour does fall under the
>> category of "anything".
>>
>>
>>
>> On 9 October 2026 06:11:15 BST, jim x via Std-Discussion <
>> std-discussion_at_[hidden]> wrote:
>>
>> Consider this example:
>>
>> ````cpp
>>
>> #include
>>
>>
>>
>> int main(){
>>
>> volatile int value = 0;
>>
>> std::jthread t1([&](){
>>
>> auto i = value; // #1
>>
>> });
>>
>> std::jthread t2([&](){
>>
>> value = 1; // #2
>>
>> });
>>
>> }
>>
>> ````
>>
>> People think this program should contain a data race because `#1` and
>> `#2` are *non-atomic* operations, which satisfies the condition in [intro.races]
>> p17
>> <https://eel.is/c++draft/intro.races#17>
>>
>> Two actions are *potentially concurrent*
>> <https://eel.is/c++draft/intro.races#def:potentially_concurrent> if
>>
>> they are performed by different threads, or
>> <https://eel.is/c++draft/intro.races#17>
>>
>> The execution of a program contains a data race if it contains two
>> potentially concurrent conflicting actions, *at least one of which is
>> not atomic*, and neither happens before the other, except for the
>> special case for signal handlers described below. Any such data race
>> results in undefined behavior ([ub:intro.races.data]).
>>
>>
>>
>> However, this opinion is based on the premise that they consider the
>> operations at `#1` and `#2` to be non-atomic operations in any way.
>> However, [dcl.type.cv] p5 <https://eel.is/c++draft/dcl.type.cv#5>
>> simultaneously states:
>>
>> *The semantics of an access through a volatile glvalue are
>> implementation-defined.*
>>
>>
>>
>> How about if a hypothetical implementation specifies the semantics as
>> follows:
>>
>> An access to a volatile int glvalue value is defined to have the exact
>> semantics of std::atomic_ref<int>(value).load() for reads, and
>> std::atomic_ref<int>(value).store() for writes.
>>
>>
>>
>> Under these specified semantics, `#1` and `#2` should be considered as
>> atomic operations, which won't satisfy the condition in [intro.race] p17,
>> therefore no data race. From this perspective, isn't the program
>> implementation-defined behavior? That is, whether the program contains a
>> data race depends on how the implementation specifies the semantics
>>
>>
>
Received on 2026-10-10 08:45:19
