Date: Mon, 21 Oct 2019 11:33:57 -0700
On Mon, Oct 21, 2019 at 10:33 AM Andrey Semashev via Std-Discussion <
std-discussion_at_[hidden]> wrote:
> On 2019-10-21 18:36, Thiago Macieira via Std-Discussion wrote:
> > On Monday, 21 October 2019 00:44:24 PDT Jeremy Ong via Std-Discussion
> wrote:
> >> You have now done an atomic load 3 times and it is possible that stores
> or
> >> loads to foo have occurred in between. The pattern for atomic usage is
> to
> >> typically load once, perform a change, and possibly compare/swap in a
> loop
> >> in a manner that prevents ABA bugs.
> >
> > If the compiler can inline those three function calls, it may also
> coalesce
> > the three loads into one. AFAIK, no compiler currently does it, but it
> could.
> > If it can't inline, then the regular rules of visibility apply and if the
> > atomic pointer's own address is not accessible by other threads and the
> called
> > functions, coalescing can happen again.
> >
> > But if it can change, then the code is actually optimal, since you do
> have tot
> > reload it every time. Except that you have to write:
> >
> > foo.load()->do_one_thing();
> > foo.load()->do_another_thing();
> > foo.load()->do_a_final_thing();
>
> I don't think the compiler can ever prove that the pointer is not
> accessed by any other threads or signal handlers. Even in case of LTO,
> the compiler can't be sure external threads spawned by OS or shared
> libraries or signal handlers don't modify the atomic pointer.
>
The compiler is always* free to transform atomic accesses such that they
are performed as if in isolation (as long as it doesn't break forward
progress). There's no way for another thread to observe the difference
between the compiler doing this transformation, and the hardware scheduling
the threads and memory accesses such that this happens. This is allowed
even if any of the do_* functions modify foo, as the compiler can just
insert an atomic store at the end of the sequence (again, as long as it
doesn't break forward progress).
A good way to think about this is to ask the question: "Is this specific
execution always valid according to the C++ memory model, regardless of
what other threads do?" If so, then it's valid for the compiler to
transform the code such that that execution does always happen.
- Michael Spencer
std-discussion_at_[hidden]> wrote:
> On 2019-10-21 18:36, Thiago Macieira via Std-Discussion wrote:
> > On Monday, 21 October 2019 00:44:24 PDT Jeremy Ong via Std-Discussion
> wrote:
> >> You have now done an atomic load 3 times and it is possible that stores
> or
> >> loads to foo have occurred in between. The pattern for atomic usage is
> to
> >> typically load once, perform a change, and possibly compare/swap in a
> loop
> >> in a manner that prevents ABA bugs.
> >
> > If the compiler can inline those three function calls, it may also
> coalesce
> > the three loads into one. AFAIK, no compiler currently does it, but it
> could.
> > If it can't inline, then the regular rules of visibility apply and if the
> > atomic pointer's own address is not accessible by other threads and the
> called
> > functions, coalescing can happen again.
> >
> > But if it can change, then the code is actually optimal, since you do
> have tot
> > reload it every time. Except that you have to write:
> >
> > foo.load()->do_one_thing();
> > foo.load()->do_another_thing();
> > foo.load()->do_a_final_thing();
>
> I don't think the compiler can ever prove that the pointer is not
> accessed by any other threads or signal handlers. Even in case of LTO,
> the compiler can't be sure external threads spawned by OS or shared
> libraries or signal handlers don't modify the atomic pointer.
>
The compiler is always* free to transform atomic accesses such that they
are performed as if in isolation (as long as it doesn't break forward
progress). There's no way for another thread to observe the difference
between the compiler doing this transformation, and the hardware scheduling
the threads and memory accesses such that this happens. This is allowed
even if any of the do_* functions modify foo, as the compiler can just
insert an atomic store at the end of the sequence (again, as long as it
doesn't break forward progress).
A good way to think about this is to ask the question: "Is this specific
execution always valid according to the C++ memory model, regardless of
what other threads do?" If so, then it's valid for the compiler to
transform the code such that that execution does always happen.
- Michael Spencer
Received on 2019-10-21 13:36:23