Topic: Preserving reads/writes of atomic types


Author: Scott Meyers <usenet@aristeia.com>
Date: Wed, 1 Jul 2009 23:06:01 CST
Raw View
James Kanze wrote:
> But might if the thread which is to update *pDoneFlag has a
> lower priority than this thread:-).

As if the standard has any notion of thread priority :-)

Scott

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: James Kanze <james.kanze@gmail.com>
Date: Thu, 2 Jul 2009 17:13:10 CST
Raw View
On Jul 2, 7:06 am, Scott Meyers <use...@aristeia.com> wrote:
> James Kanze wrote:
> > But might if the thread which is to update *pDoneFlag has a
> > lower priority than this thread:-).

> As if the standard has any notion of thread priority :-)

The standard leaves it up to the implementation.  Under Solaris,
and I think Linux, at least one of the scheduling policies does
have strict priority: a lower priority thread will never execute
as long as a higher priority thread has something to do.  (Note
that this is NOT the default scheduling policy.  You have to
take special steps to enable it.  And that if you do, a lot of
software will suddenly stop working, because it is subject to
priority inversion.)

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient   e objet/
                    Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Scott Meyers <usenet@aristeia.com>
Date: Tue, 30 Jun 2009 17:56:45 CST
Raw View
Consider:

    void f(std::atomic<int>* p)
    {
       int x = *p;
       int y = *p;
    }

Must the generated code read *p twice?  Or could the compiler transform to this?

    void f(std::atomic<int>* p)
    {
       int x = *p;
       int y = x;       // okay?
    }

My understanding is that atomics are designed to be valid for lock-free
interthread communication, e.g.,

    void f(std::atomic<bool>* pDoneFlag)
    {
      while (!*pDoneFlag) {    // shouldn't loop forever
       ...
      }
    }

but this can only work if all source code reads and writes must be reflected at
runtime.  I suspect there are supposed to be the necessary guarantees somewhere
in Clause 29 of the draft standard, but I can't find them.  If they are there,
can somebody please point me to them?

Thanks,

Scott

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: darkmx <micael.dark@gmail.com>
Date: Wed, 1 Jul 2009 09:59:18 CST
Raw View
On 30 jun, 18:56, Scott Meyers <use...@aristeia.com> wrote:
> Consider:
>
>     void f(std::atomic<int>* p)
>     {
>        int x = *p;
>        int y = *p;
>     }
>
> Must the generated code read *p twice?  Or could the compiler transform to this?
>
>     void f(std::atomic<int>* p)
>     {
>        int x = *p;
>        int y = x;       // okay?
>     }
>
> My understanding is that atomics are designed to be valid for lock-free
> interthread communication, e.g.,
>
>     void f(std::atomic<bool>* pDoneFlag)
>     {
>       while (!*pDoneFlag) {    // shouldn't loop forever
>        ...
>       }
>     }
>
> but this can only work if all source code reads and writes must be reflected at
> runtime.  I suspect there are supposed to be the necessary guarantees somewhere
> in Clause 29 of the draft standard, but I can't find them.  If they are there,
> can somebody please point me to them?
>
> Thanks,
>
> Scott
>
> --
> [ comp.std.c++ is moderated.  To submit articles, try just posting with ]
> [ your news-reader.  If that fails, use mailto:std-...@netlab.cs.rpi.edu]
> [              --- Please see the FAQ before posting. ---               ]
> [ FAQ:http://www.comeaucomputing.com/csc/faq.html                     ]

Hi Scott, check 1.10 Multi-threaded executions and data races
[intro.multithread]. It appears to have useful information related to
your question. Also Herb Sutter explains the main difference in
optimizations between std::atomic and volatile here
http://www.ddj.com/hpc-high-performance-computing/212701484

 From what I understand, the optimization is allowed since the program
couldn't tell the difference (int y = *p; doesn't depend from int x =
*p;)


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: James Kanze <james.kanze@gmail.com>
Date: Wed, 1 Jul 2009 10:01:33 CST
Raw View
On Jul 1, 1:56 am, Scott Meyers <use...@aristeia.com> wrote:
> Consider:

>     void f(std::atomic<int>* p)
>     {
>        int x = *p;
>        int y = *p;
>     }

> Must the generated code read *p twice?

It must call std::atomic<int>::operator int() twice, at least at
the level of the abstract machine.

Also: all of the member functions of the atomic types are
declared volatile.  Does this mean that we are expected to
declare instances volatile as well (and that your code should
read:

     void f( std::atomic< int > volatile* p ) ...

)?  Or only that we can, and still use the functions?  (I would
have expected that the volatile not be necessary, since the
actual semantics of the type subsumes it.  If necessary, of
course, the implementation could use volatile in the
implementation code; on most modern machines, it will probably
have to resort to something in assembler, however.)

> Or could the compiler transform to this?

>     void f(std::atomic<int>* p)
>     {
>        int x = *p;
>        int y = x;       // okay?
>     }

Only if it can see that there is no change in the semantics.  If
the implementation of operator int(), here, just returns a
non-volatile local variable, then presumably yes (but I doubt
that such an implementation would ever be conforming).  If
std::atomic contains a volatile member, however, which is
accessed by operator int(), then probably not, and if it
contains assembler, the compiler would have to analyse the
assembler enough to determine that it had no side effects (i.e.
like issuing a membar or a fence instruction) in order to
perform the optimization.

> My understanding is that atomics are designed to be valid for
> lock-free interthread communication, e.g.,

>     void f(std::atomic<bool>* pDoneFlag)
>     {
>       while (!*pDoneFlag) {    // shouldn't loop forever ...

But might if the thread which is to update *pDoneFlag has a
lower priority than this thread:-).  (I know, that's not your
point.)

>       }
>     }

> but this can only work if all source code reads and writes
> must be reflected at runtime.  I suspect there are supposed to
> be the necessary guarantees somewhere in Clause 29 of the
> draft standard, but I can't find them.  If they are there, can
> somebody please point me to them?

I think the issue is treated through the specification of memory
ordering.  Which constrains the abstract machine.  And the
compiler must respect the semantics of the abstract machine
unless it can determine that there is no change in observable
behavior.

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient   e objet/
                    Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Anthony Williams <anthony.ajw@gmail.com>
Date: Wed, 1 Jul 2009 11:19:13 CST
Raw View
Scott Meyers <usenet@aristeia.com> writes:

> Consider:
>
>    void f(std::atomic<int>* p)
>    {
>       int x = *p;
>       int y = *p;
>    }
>
> Must the generated code read *p twice?

No.

> Or could the compiler transform to this?
>
>    void f(std::atomic<int>* p)
>    {
>       int x = *p;
>       int y = x;       // okay?
>    }

In this case, yes. The note in 29.6p3 of N2914 says it all:

"[ Note: Many operations are volatile-quali      ed. The       volatile as device
register       semantics have not changed in the standard. This quali      cation
means that volatility is preserved when applying these operations to
volatile objects. It does not mean that operations on non-volatile
objects become volatile. Thus, volatile quali      ed operations on
non-volatile objects may be merged under some conditions.        end note ]"

> My understanding is that atomics are designed to be valid for lock-free
> interthread communication, e.g.,
>
>    void f(std::atomic<bool>* pDoneFlag)
>    {
>      while (!*pDoneFlag) {    // shouldn't loop forever
>       ...
>      }
>    }

Yes.

> but this can only work if all source code reads and writes must be reflected at
> runtime.  I suspect there are supposed to be the necessary guarantees somewhere
> in Clause 29 of the draft standard, but I can't find them.  If they are there,
> can somebody please point me to them?

This particular case is disallowed by 29.3p9:

"Implementations should make atomic stores visible to atomic loads
within a reasonable amount of time.  Implementations shall not move an
atomic operation out of an unbounded loop."

This essentially disallows moving the load out of the loop --- to do so
would invalidate the first sentence.

In general such things are covered by the memory model (1.10 and 29.3)
--- the compiler must ensure that all happens-before relationships are
correctly preserved.

Anthony
--
Author of C++ Concurrency in Action | http://www.manning.com/williams
just::thread C++0x thread library   | http://www.stdthread.co.uk
Just Software Solutions Ltd         | http://www.justsoftwaresolutions.co.uk
15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]