From: Joshua Maurice on 22 Mar 2010 08:23 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 22 Mar 2010 22:42 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 22 Mar 2010 23:05 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 22 Mar 2010 23:05 "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 23 Mar 2010 03:22
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! ] |