Subj : Re: double-checked locking in C To : comp.programming.threads From : Peter Dimov Date : Sat Jul 09 2005 04:24 am David Schwartz wrote: > "David Hopwood" wrote in > message news:_yAze.11062$O22.8179@fe1.news.blueyonder.co.uk... > >> Indeed. The whole idea of 'volatile' in C is incoherent; the language >> definition specifies by fiat that accesses to volatile objects are >> always observable side effects, when in many cases they manifestly >> are not. It's only incoherent if you think that the term "observable behavior" has a real-world meaning. It doesn't. Observable behavior is _defined_ in terms of volatile accesses and calls to I/O library functions. You can only state that these are manifestly non-observable if you are using your own definition of "observable", not the one from the standard. > And it doesn't specify where or how they are to be observed when > that can make all the difference in the world. An observer at the CPU > instruction stream will see different things from an observer on > another CPU who will see different things from an observer at the > memory bus. The standard, phrased in terms of an abstract machine, > never explains what "observable" means... "By design". You can't talk about CPU instruction streams, because there may be none (C interpreters exist.) You can't talk about a memory bus, because there may be none. It is impossible to define "observable" in a formal way on this level. The standard only defines a mapping of the language constructs to "observable behavior" and stops there. The mapping of "observable behavior" to real world events is implementation defined; it may or may not involve an observer. > ... and how it relates to the "as if" rule in the case of 'volatile'. "As if" means that the compiler can achieve the required observable behavior in whatever way it likes. If we assume an implementation where "volatile access" maps to a load instruction and "volatile write" maps to a store instruction, "as if" means that the compiler can elide or reorder all other memory accesses and optimize out or overlap all nonvolatile objects as long as the sequence of loads/stores corresponding to volatile accesses/writes remains in place. To get back to DCL: volatile int initialized = external_function(); The compiler is free to move external_function's writes to nonvolatile objects after the write to 'initialized'. .