From: Joshua Maurice on
On Mar 21, 2: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.
>
> 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.
>
> 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. This requirement happens to be very useful for multi-threaded
> programs that can augment volatile with hardware fences to produce
> meaningful results.

That is one interpretation. Unfortunately / fortunately (?), that
interpretation is not the prevailing interpretation. Thus far in this
thread, we have members of the C++ standards committee or its
affiliates explicitly disagreeing on the committee's website with that
interpretation (linked else-thread). The POSIX standard explicitly
disagrees with your interpretation (see google). The
comp.programming.threads FAQ explicitly disagrees with you several
times (linked else-thread). We have gcc docs and implementation
disagreeing with your interpretation (see google). We have an official
blog from intel, the biggest maker of chips in the world, and a major
compiler writer, explicitly disagreeing with your interpretation
(linked else-thread). We have experts in the C++ community explicitly
disagreeing with your interpretation. (Thanks Andrei, and his paper "C+
+ And The Perils Of Double Checked Locking". Andy, have you even read
it?)

Basically, everyone in positions of authority are against you, from
the experts, the standards writers, and the implementation coders.
(Except for Microsoft Visual Studios, who actually make volatile reads
and writes like a mutex acquire and mutex release.) I don't know what
else I can do to dissuade you from this statement of fact concerning
the real world. As a practical matter, in the real world on real
implementations, volatile has no use as a correct, portable
synchronization primitive.


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

From: Michael Doubez on
On 23 mar, 00:22, "Bo Persson" <b...(a)gmb.dk> wrote:
> Leigh Johnston wrote:
> > "Andy Venikov" <swojchelo...(a)gmail.com> wrote in message
> >news:ho5s8u$52u$1(a)news.eternal-september.org...
> >>> I still must ask, really? That would mean that all shared state
> >>> must be volatile qualified, including internal class members for
> >>> shared data. Wouldn't that big a huge performance hit when the
> >>> compiler can't optimize any of that? Could you even use prebuilt
> >>> classes (which usually don't have volatile overloads) in the
> >>> shared data, like say std::string, std::vector, std::map, etc.?
>
> >> Not at all!
> >> Most multi-threading issues are solved with mutexes, semaphores,
> >> conditional variables and such. All of these are library calls.
> >> That means that using volatile in those cases is not necessary.
> >> It's only when you get into more esotheric parallel computing
> >> problems where you'd like to avoid a heavy-handed approach of
> >> mutexes that you enter the realm of volatile. In normal
> >> multi-threading solved with regular means there is really no
> >> reason to use volatile.
>
> > Esoteric? I would have thought independent correctly aligned (and
> > therefore atomic) x86 variable reads (fundamental types) without
> > the use of a mutex are not uncommon making volatile not uncommon
> > also on that platform (on VC++) at least. I have exactly one
> > volatile in my entire codebase and that is such a variable. From
> > MSDN (VC++) docs:
> > "The volatile keyword is a type qualifier used to declare that an
> > object can be modified in the program by something such as the
> > operating system, the hardware, or a concurrently executing thread."
>
> > That doesn't seem esoteric to me! :)
>
> The esoteric thing is that this is a compiler specific extension, not something guaranteed by the language. Currently there are no threads at all in C++.

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.

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.

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

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.

[snip]

--
Michael


--
[ 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 Mon, 22 Mar 2010 17:23:57 CST
Joshua Maurice <joshuamaurice(a)gmail.com> wrote:
[snip]
> Basically, everyone in positions of authority are against you, from
> the experts, the standards writers, and the implementation coders.
> (Except for Microsoft Visual Studios, who actually make volatile reads
> and writes like a mutex acquire and mutex release.) I don't know what
> else I can do to dissuade you from this statement of fact concerning
> the real world. As a practical matter, in the real world on real
> implementations, volatile has no use as a correct, portable
> synchronization primitive.

I think you and Andy Venikov are at cross purposes. If you want to
write portable threaded code conforming to a particular standard (such
as POSIX) which has defined synchronisation objects such as mutexes,
semaphores or condition variables, then the volatile keyword is
useless. It achieves nothing in terms of thread safety (accessing these
synchronisation objects in code comprises so far as relevant a compiler
barrier as well as a memory barrier on platforms which conform to the
standard), and inhibits optimisations which may still be available to
the compiler in multi-threaded code but not in single thread
asynchronous (interrupt driven) code.

If however you want to write non-portable code working with only one
particular processor type on one particular compiler, then you might
achieve some efficiency improvements by using processor-dependent
memory barriers or store/load instructions combined with use of the
volatile keyword. It is easy to get it wrong doing this (as witness
the bogus double-checked-locking pattern which has been around so long).
And the code may cease to work reliably whenever you upgrade your
compiler or operating system version (unless it happens to be supported
by a compiler/platform writer's specific extension, such as
Microsoft's).

When c++0x atomic variables are available with their variety of
different synchronisation options, then the need for such non-portable
code (so far as it exists at all) will be largely eliminated.

Chris

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

From: Leigh Johnston on
"Joshua Maurice" <joshuamaurice(a)gmail.com> wrote in message
news:b897c547-0237-4d21-8a54-7c0cc80cd39a(a)u15g2000prd.googlegroups.com...
> On Mar 21, 2: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.
>>
>> 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.
>>
>> 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. This requirement happens to be very useful for multi-threaded
>> programs that can augment volatile with hardware fences to produce
>> meaningful results.
>
> That is one interpretation. Unfortunately / fortunately (?), that
> interpretation is not the prevailing interpretation. Thus far in this
> thread, we have members of the C++ standards committee or its
> affiliates explicitly disagreeing on the committee's website with that
> interpretation (linked else-thread). The POSIX standard explicitly
> disagrees with your interpretation (see google). The
> comp.programming.threads FAQ explicitly disagrees with you several
> times (linked else-thread). We have gcc docs and implementation
> disagreeing with your interpretation (see google). We have an official
> blog from intel, the biggest maker of chips in the world, and a major
> compiler writer, explicitly disagreeing with your interpretation
> (linked else-thread). We have experts in the C++ community explicitly
> disagreeing with your interpretation. (Thanks Andrei, and his paper "C+
> + And The Perils Of Double Checked Locking". Andy, have you even read
> it?)
>
> Basically, everyone in positions of authority are against you, from
> the experts, the standards writers, and the implementation coders.
> (Except for Microsoft Visual Studios, who actually make volatile reads
> and writes like a mutex acquire and mutex release.) I don't know what
> else I can do to dissuade you from this statement of fact concerning
> the real world. As a practical matter, in the real world on real
> implementations, volatile has no use as a correct, portable
> synchronization primitive.
>

Sometimes you have to use common sense:

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.

The behaviour of optimizing compilers in the real world can make volatile
necessary to get correct behaviour in multi-threaded designs. 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.

/Leigh



--
[ 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 08:05:28 CST
"Leigh Johnston" <leigh(a)i42.co.uk> wrote:
[snip]
> Sometimes you have to use common sense:
>
> 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.
>
> The behaviour of optimizing compilers in the real world can make
> volatile necessary to get correct behaviour in multi-threaded
> designs. 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.

It is never "necessary" to use the volatile keyword "in the real world"
to get correct behaviour because of "the behaviour of optimising
compilers". If it is, then the compiler does not conform to the
particular standard you are writing to. For example, all compilers
intended for POSIX platforms which support pthreads have a
configuration flag (usually "-pthread") which causes the locking
primitives to act also as compiler barriers, and the compiler would be
non-conforming if it did not both provide this facility and honour it.

Of course, there are circumstances when you can get away with the
volatile keyword, such as the rather contrived example you have given,
but in that case it is pretty well pointless because making the
variable volatile as opposed to using normal synchronisation objects
will not improve efficiency. In fact, it will hinder efficiency if
Thread A has run work before thread B, because thread A will depend on a
random future event on multi-processor systems, namely when the caches
happen to synchronise to achieve memory visibility, in order to proceed.

Chris

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