From: James Kanze on
On Mar 21, 9:32 pm, Andy Venikov <swojchelo...(a)gmail.com> wrote:
> Joshua Maurice wrote:
> >> So is volatile sufficient - absolutely not. Portable? - hardly.
> >> Is it necessary in certain cases - absolutely.

> > Perhaps I was a bit too strong in my statement. I did intent to say
> > "for portable uses".

> > However, for future reference, what compilers of what
> > versions on what platforms implement volatile with these
> > semantics? I would like to research these claims to be
> > better prepared in this discussion and future ones. Are any
> > of these implementations not x86? These implementations
> > really don't provide a sane threading interface and force
> > the use of the volatile keyword for threading? Weird.

> I'm sorry if I wasn't clear in my previous post, but I was
> talking about standard volatile behavior.

Which is practically entirely implementation defined.

> The standard places a requirement on conforming
> implementations that:

> 1.9.6
> The observable behavior of the abstract machine is its
> sequence of reads and writes to volatile data and calls to
> library I/O functions

> 1.9.7
> Accessing an object designated by a volatile lvalue (3.10),
> modifying an object, calling a library I/O function, or
> calling a function that does any of those operations are all
> side effects, which are changes in the state of the execution
> environment. Evaluation of an expression might produce side
> effects. At certain specified points in the execution sequence
> called sequence points, all side effects of previous
> evaluations shall be complete and no side effects of
> subsequent evaluations shall have taken place

> 1.9.11
> The least requirements on a conforming implementation are:
> At sequence points, volatile objects are stable in the sense
> that previous evaluations are complete and subsequent
> evaluations have not yet occurred.

It also fails to define what it means by an access (to a
volatile object) and what it means to be "stable". The C
standard addresses the first by saying that it's implementation
defined.

The C standard also mentions the one of the motivations behind
volatile: memory mapped I/O. Independantly of the standard,
from a QoI point of view, we can expect that it would at least
provide the necessary semantics to support this. Tough luck: it
isn't the case with g++ nor Sun CC (nor, as far as I can tell,
VC++, but I'm less familiar with the semantics of modern Intel
than I am of those of Sparc).

> That to me sounds like a complete enough requirement that
> compilers don't perform optimizations that produce
> "surprising" results in so far as observable behavior in an
> abstract (single-threaded) machine are concerned.

For single-threaded machines, volatile does have some defined
semantics. For example, in a signal handler, you can assign to
a variable of type sigatomic_t volatile (and that's about it, as
far as the C++ standard is concerned---Posix guarantees
considerably more, but still a lot less than is often assumed).
Volatile is significant with regards to communication between
signal handlers and the rest of the program.

> This requirement happens to be very useful for multi-threaded
> programs that can augment volatile with hardware fences to
> produce meaningful results.

How do you specify a hardware fence in C++? Anything in the
respect is implementation defined. And all of the
implementations do the right thing, in one way or another:
whatever you do to specify a fence also inhibits (or can be made
to inhibit) code movement accross the fence.

--
James Kanze

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Chris Vine on
On Tue, 23 Mar 2010 17:04:39 CST
"Leigh Johnston" <leigh(a)i42.co.uk> wrote:
[snip]
> It is not a contrived example, I have the following code in my
> codebase which is similar:
> ....
> lock();
> while (iSockets.empty() && is_running())
> {
> unlock();
> Sleep(100);
> if (!is_running())
> return;
> lock();
> }
> ....
>
> is_running() is an inline member function which returns the value of a
> volatile member variable and shouldn't require a lock to query as it
> is atomic on the platform I target (x86). It makes sense for this
> platform and compiler (VC++) that I use volatile. Admittedly I could
> use an event/wait primitive instead but that doesn't make the above
> code wrong for the particular use-case in question. I agree that for
> other platforms and compilers this might be different. From what I
> understand and I agree with the advent of C++0x should see such
> volatiles disappear in favour of std::atomic<>. Not everyone in the
> real world is using C++0x as the standard has not even been published
> yet.

This will work on windows (which I think is your platform) because of
the Microsoft extension. Whether it works on other platforms depends
on whether anything critical depends on the accuracy of is_running(),
because if it is an inline member function returning the value of a
volatile variable it might/will be reporting out-of-date state which
could be inconsistent with other relevant variables, as there is no
cache synchronisation. As it happens, C++0x atomic variables in ordinary
usage will provide memory synchronisation, unless you deliberately
choose relaxed memory ordering.

Since you already have a lock in operation it seems a bit perverse not
to use it: but this is just a code snippet so I imagine you have your
reasons in the larger picture.

Chris

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: James Kanze on
On Mar 20, 7:13 am, red floyd <redfl...(a)gmail.com> wrote:
> On Mar 19, 2:06 am, "Leigh Johnston" <le...(a)i42.co.uk> wrote:
> > That was my point, volatile whilst not a solution in itself
> > is a "part" of a solution for multi-threaded programming
> > when using a C++ (current standard) optimizing compiler:

> > thread A:
> > finished = false;
> > spawn_thread_B();
> > while(!finished)
> > {
> > /* do work */
> > }

> > thread B:
> > /* do work */
> > finished = true;

> > If finished is not volatile and compiler optimizations are
> > enabled thread A may loop forever.

> Agreed. I've seen this in non-threaded code with
> memory-mapped I/O.

Which is a different issue. That's what volatile was designed
for: I think it still works for that on Intel architecture. (It
doesn't on Sparc, at least with g++ or Sun CC:-(.) Threading is
a different issue.

Note that volatile is still relevant for communications between
a signal handler and the main (single threaded) application. At
least according to the standard.

--
James Kanze

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: James Kanze on
On Mar 23, 1:42 pm, Michael Doubez <michael.dou...(a)free.fr> wrote:
> On 23 mar, 00:22, "Bo Persson" <b...(a)gmb.dk> wrote:

[...]
> Still it does say something of the semantic of the memory
> location. In practice the compiler will cut the optimizations
> regarding the volatile location; I don't see a compiler
> ignoring this kind of notification.

Not really. It makes some vague statements concerning "access",
while not defining what it really means by access. And "memory
location", without further qualifiers, has no real meaning on
modern processors, with their five or six levels of memory---is
the memory the core specific cache, the memory shared by all the
cores, or the virtual backup store (which maintains its values
even after the machine has been shut down)?

And of course, what really counts is what the compilers
implement: neither g++, nor Sun CC, nor VC++ (at least through
8.0) give volatile any more semantics that issuing a load or
store instruction---which the hardware will execute when it gets
around to it. Maybe.

> Which means that the memory value will eventually (after an
> undetermined amount of time) be flushed to the location and
> not kept around in the stack or somewhere else for
> optimization reasons.

Sorry, but executing a store instruction (or a mov with a
destination in memory) does NOT guarantee that there will be a
write cycle in main memory, ever. At least not on modern Sparc
and Intel architectures. (I'm less familiar with others, but
from what I've heard, Sparc and Intel are among the most strict
in this regard.)

> My understanding is that it is the amount of optimization
> cutting that is implementation dependent.

The issue isn't compiler optimization. If that were the
problem, just turn off the optimizer: all of the compilers that
I know will then follow the rules of the abstract machine very
rigorously.

> Still, I have not understodd how this can be useful for
> multithreading with the next-to-be standard, AFAIS atomic
> types gives better guarantees and better optimization
> possibilities.

C++0x addresses threading. And the people who worked on that
part of it have learned from earlier attempts. (Java had to
rewrite their memory model at least once in order to provide
adequate guarantees without totally breaking performance.)

--
James Kanze

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: James Kanze on
On Mar 23, 2:05 pm, "Leigh Johnston" <le...(a)i42.co.uk> wrote:
> "Joshua Maurice" <joshuamaur...(a)gmail.com> wrote in message

> news:b897c547-0237-4d21-8a54-7c0cc80cd39a(a)u15g2000prd.googlegroups.com...

[...]
> Sometimes you have to use common sense:

Modern memory models don't respect common sense very much.

> thread A:
> finished = false;
> spawn_thread_B();
> while(!finished)
> {
> /* do work */
> }

> thread B:
> /* do work */
> finished = true;

> If finished is not volatile and compiler optimizations are
> enabled thread A may loop forever.

And making finished volatile doesn't change anything in this
regard. At least not with Sun CC or g++ under Solaris, g++
under Linux on PC, and VC++8.0 under Windows on a 64 bit PC.

> The behaviour of optimizing compilers in the real world can
> make volatile necessary to get correct behaviour in
> multi-threaded designs.

As has been pointed out: volatile is never sufficient, and when
you use whatever is sufficient, volatile ceases to be necessary.

> You don't always have to use a memory barriers or a mutexes
> when performing an atomic read of some state shared by more
> than one thread.

Only if you want it to work.

--
James Kanze

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]