Topic: Scott & Andrei article on DCLP and threading... Flawed ?


Author: hsutter@gotw.ca (Herb Sutter)
Date: Thu, 17 Jun 2004 22:11:29 +0000 (UTC)
Raw View
On Tue, 15 Jun 2004 23:39:07 +0000 (UTC), Usenet@aristeia.com (Scott
Meyers) wrote:
>On Mon, 14 Jun 2004 22:36:20 +0000 (UTC), Herb Sutter wrote:
>> On Sun, 13 Jun 2004 07:42:48 +0000 (UTC), Usenet@aristeia.com (Scott
>> Meyers) wrote:
>> >Which reminds me.  It's my understanding that standard C++ programs can be run
>> >in the .NET environment, and when so executed, code generation takes place at
>> >runtime using JIT technology, just like Java.  Which means that the program's
>> >runtime and the runtime of the back end of the compiler are the same time.
>>
>> I don't think you said that quite the right way:
>>
>> 1. The C++ compiler emits .NET IL instead of x86 instructions, and at that
>> point the result is a .NET assembly just like that created by any .NET
>> language.
>
>Right.  The front end of the C++ compiler generates MSIL as its IL.

No, that's not what I said. The C++ code goes through the WHOLE C++
compiler -- front end and back end, including the full optimizer. The back
end then spits out IL instead of x86.

Incidentally, that's why if you compile the equivalent .NET code using
both C# and C++ in our compilers now going into beta, the C++ code is
typically 25% faster because we generate better IL -- the code is going
through a mature and heavy optimizer. The C# compiler does nearly no
optimizations (IIRC it does only constant folding), leaving all the
optimization to the JIT which of course has to be fast and so doesn't have
as much time to do a great job of optimization (never mind that it's a
younger product and so hasn't had a chance to get as mature as the C++
optimizer). For example, the JIT does do inlining, but it's not very
aggressive right now and only deals with very small functions.

BTW, I say "if you compile the equivalent .NET code using both C# and C++"
because, in the release of Visual Studio now going into beta, the two
languages are equally easy and natural to use for pure .NET programs. In
C++, think of it as programming in the C# subset, using just .NET types
and features. (Of course you can do all that and native stuff too, but
then you can't compare directly to C# any more because C# isn't designed
to handle that.)

>> 2. Then, when you try to load the .NET assembly, the jitter JIT-compiles
>> the assembly's IL to the specific processor's instruction set. That jitter
>> is a compiler, but it's an IL compiler and not a C++ compiler.
>
>Again, right.  The back end of the C++ compiler is shared with all the
>other .NET compilers and translates the IL (which is MSIL) into executable
>code.

Again, no, I didn't say that. The .NET jitter compiles the IL to the
native instruction set. There's no C++ tool chain at all at this point;
we're way past that. This is a completely separate compiler, an IL
compiler.

When I say "the jitter", to be precise there are two IL compilers: The
normal JIT compiles as you load each class, and NGEN which runs at app
install time and can spend more time on optimization (amusingly, NGEN has
the comical informal name of "pre-jit").


>In principle, this is no different from e.g., the gcc tool chain which, if
>I understand correctly, has many front ends that generate a common IL.  For
>each target system, there is then one back end that translates IL into
>executable instructions for the target.

BTW, I'm no gcc expert, but my understanding is that gcc is not actually
that well compartmentalized; I'm told the front and and back end are
pretty wired-together. I understand there's a project at SGI doing a lot
of work to break them apart, particularly so that it's more feasible to
stick other stuff in the middle, or replace the back-end. But that's not
in the main gcc distribution yet, and may never be. (Anyone who knows
better should feel free to jump in and correct anything I said wrong in
this paragraph. I'm not a gcc implementation expert.)

Herb

---
Herb Sutter (http://blogs.gotdotnet.com/hsutter)     (www.gotw.ca)

Convener, ISO WG21 (C++ standards committee)     (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal         (www.gotw.ca/cuj)
Visual C++ architect, Microsoft            (www.gotw.ca/microsoft)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gdr@cs.tamu.edu (Gabriel Dos Reis)
Date: Sat, 19 Jun 2004 21:29:44 +0000 (UTC)
Raw View
hsutter@gotw.ca (Herb Sutter) writes:

[...]

| >In principle, this is no different from e.g., the gcc tool chain which, if
| >I understand correctly, has many front ends that generate a common IL.  For
| >each target system, there is then one back end that translates IL into
| >executable instructions for the target.
|
| BTW, I'm no gcc expert, but my understanding is that gcc is not actually
| that well compartmentalized; I'm told the front and and back end are
| pretty wired-together.

For an appropriate definition of "wired-together".

Compilers included in the GCC distribution share the same middle-end
and back-ends.  Front-ends tranforma textual representations of
programs into a data structure called "tree"; that data structure is
tranformed by the middle-end into a lower-level language called
Register Transfer Language (RTL), as pointed out by Scott Meyers; and
after that into assembly code.  That has been so for a while now.

| I understand there's a project at SGI doing a lot
| of work to break them apart, particularly so that it's more feasible to
| stick other stuff in the middle, or replace the back-end. But that's not
| in the main gcc distribution yet, and may never be. (Anyone who knows
| better should feel free to jump in and correct anything I said wrong in
| this paragraph. I'm not a gcc implementation expert.)

If you're looking ahead for the next major version:

        http://gcc.gnu.org/onlinedocs/gccint/

--
                                                        Gabriel Dos Reis
                                                         gdr@cs.tamu.edu
  Texas A&M University -- Computer Science Department
 301, Bright Building -- College Station, TX 77843-3112

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: hsutter@gotw.ca (Herb Sutter)
Date: Mon, 14 Jun 2004 22:36:20 +0000 (UTC)
Raw View
On Sun, 13 Jun 2004 07:42:48 +0000 (UTC), Usenet@aristeia.com (Scott
Meyers) wrote:

>> The days where compilation and optimization strictly preceded linking and
>> execution are already behind us for some environments, and I expect this trend
>> to continue.  I can't tell you when the first nail went into that coffin, but
>> I can say that when Java put a compiler into the language runtime system, the
>> line between compilation, linking, and runing blurred quite noticably.
>
>Which reminds me.  It's my understanding that standard C++ programs can be run
>in the .NET environment, and when so executed, code generation takes place at
>runtime using JIT technology, just like Java.  Which means that the program's
>runtime and the runtime of the back end of the compiler are the same time.

I don't think you said that quite the right way:

1. The C++ compiler emits .NET IL instead of x86 instructions, and at that
point the result is a .NET assembly just like that created by any .NET
language.

2. Then, when you try to load the .NET assembly, the jitter JIT-compiles
the assembly's IL to the specific processor's instruction set. That jitter
is a compiler, but it's an IL compiler and not a C++ compiler.

Check out also my recent article about interactions between C++ and JIT
environments:

  "Inline Redux"
  C/C++ Users Journal, 21(11), November 2003
  http://www.cuj.com/documents/s=8906/cujexp0311sutter/sutter.htm

>The tricks we're used to playing to hide things from compilers and linkers are
>based on assumptions that, in some environments, no longer hold.

BTW, nice DCL article, Scott and Andrei. I'm looking forward to part 2.

Herb

---
Herb Sutter (http://blogs.gotdotnet.com/hsutter)     (www.gotw.ca)

Convener, ISO WG21 (C++ standards committee)     (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal         (www.gotw.ca/cuj)
Visual C++ architect, Microsoft            (www.gotw.ca/microsoft)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Usenet@aristeia.com (Scott Meyers)
Date: Tue, 15 Jun 2004 23:39:07 +0000 (UTC)
Raw View
On Mon, 14 Jun 2004 22:36:20 +0000 (UTC), Herb Sutter wrote:
> On Sun, 13 Jun 2004 07:42:48 +0000 (UTC), Usenet@aristeia.com (Scott
> Meyers) wrote:
> >Which reminds me.  It's my understanding that standard C++ programs can be run
> >in the .NET environment, and when so executed, code generation takes place at
> >runtime using JIT technology, just like Java.  Which means that the program's
> >runtime and the runtime of the back end of the compiler are the same time.
>
> I don't think you said that quite the right way:
>
> 1. The C++ compiler emits .NET IL instead of x86 instructions, and at that
> point the result is a .NET assembly just like that created by any .NET
> language.

Right.  The front end of the C++ compiler generates MSIL as its IL.

> 2. Then, when you try to load the .NET assembly, the jitter JIT-compiles
> the assembly's IL to the specific processor's instruction set. That jitter
> is a compiler, but it's an IL compiler and not a C++ compiler.

Again, right.  The back end of the C++ compiler is shared with all the
other .NET compilers and translates the IL (which is MSIL) into executable
code.

In principle, this is no different from e.g., the gcc tool chain which, if
I understand correctly, has many front ends that generate a common IL.  For
each target system, there is then one back end that translates IL into
executable instructions for the target.

For JITed implementations, the code generator has the opportunity to have
essentially global program knowledge, and it can do whatever optimizations
it wants to, including the inlining Herb describes in his article, but also
any other backend optimizations.  The only real constraint on it is that
whatever it does, it better do it quickly, because code generation time is
the same as program runtime.

Scott

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: cpdaniel_remove_this_and_nospam@mvps.org.nospam ("Carl Daniel")
Date: Wed, 16 Jun 2004 15:39:44 +0000 (UTC)
Raw View
Scott Meyers wrote:
> On Mon, 14 Jun 2004 22:36:20 +0000 (UTC), Herb Sutter wrote:
>> On Sun, 13 Jun 2004 07:42:48 +0000 (UTC), Usenet@aristeia.com (Scott
>> Meyers) wrote:
>>> Which reminds me.  It's my understanding that standard C++ programs
>>> can be run in the .NET environment, and when so executed, code
>>> generation takes place at runtime using JIT technology, just like
>>> Java.  Which means that the program's runtime and the runtime of
>>> the back end of the compiler are the same time.
>>
>> I don't think you said that quite the right way:
>>
>> 1. The C++ compiler emits .NET IL instead of x86 instructions, and
>> at that point the result is a .NET assembly just like that created
>> by any .NET language.
>
> Right.  The front end of the C++ compiler generates MSIL as its IL.

Actually, IIUC, the front end generates it's own IL which is then run
through all of the architecture-independent optimization passes of the back
end (including some inlining cases) and then emitted as MSIL.  When the MSIL
is then JITed it's subject to additional optimization passes by the JIT
compiler including potentially additional inlining.

-cd

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: mvglen04-cnews@yahoo.com (dayton)
Date: Wed, 16 Jun 2004 15:47:45 +0000 (UTC)
Raw View
Alexander Terekhov wrote:

> How does that translate to bus transaction? Yeah, implementation-
> defined, I know. C++ TR has some new stuff for I/O registers. Look
> also at Java's approach with getters and setters:
>
> http://www.rtj.org/rtsj-V1.0.pdf
>
> "....
>  Raw Memory Access
>
>  An instance of RawMemoryAccess models a range of physical memory as a
>  fixed sequence of bytes. A full complement of accessor methods allow
..

I thought it was long-standing mathematical proof that it is literally
impossible to emulate atomic read-modify-write operations in software.
You must have hardware support.  This is the purpose of the LOCK
instruction prefix in the Intel world, and CAS instructions.  Some RISC
architectures in fact do not guarantee the order of memory writes.  Even
volatiles won't work for exchanging values between processors. You must
use the special instructions.  The C++ standard does not define
guaranteed writes and bus interlocks.  To test and set a semaphore
reliably you must step outside of the C++ language to access the
low-level instructions.

Because you cannot test and set in an atomic action, the double check
pattern as presented is fatally flawed. No amount of C++ cleverness can
save it.

With POSIX calls you would achieve the initialization of a Singleton
with pthread_once_init(). If the construction takes a significant amount
of time, use a mutex and a condition variable to protect the singleton
pointer.

If pthread_once_init() is not availble in your implementation, try
protecting the initialization with a mutex.  Threads attempting to
initialize attempt to lock with a trylock() operation.  The first thread
that succeeds never releases the lock.  Threads that fail to get the
lock wait for the initialization to complete.

Dayton

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: SeeWebsiteForEmail@moderncppdesign.com ("Andrei Alexandrescu (See Website for Email)")
Date: Wed, 16 Jun 2004 18:39:03 +0000 (UTC)
Raw View
"dayton" <mvglen04-cnews@yahoo.com> wrote in message
news:puUzc.86948$gX5.21098@newssvr25.news.prodigy.com...
> I thought it was long-standing mathematical proof that it is literally
> impossible to emulate atomic read-modify-write operations in software.
> You must have hardware support.  This is the purpose of the LOCK
> instruction prefix in the Intel world, and CAS instructions.

Agreed. The interesting thing is that it's been proven that CAS is all you
need to implement any multithreaded algorithm (!)

> Because you cannot test and set in an atomic action, the double check
> pattern as presented is fatally flawed. No amount of C++ cleverness can
> save it.

I hope "as presented" doesn't mean "as presented in Scott and Andrei's DDJ
article" because that article reaches exactly the same conclusion as you
:o).


Andrei


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Wed, 16 Jun 2004 19:49:52 +0000 (UTC)
Raw View
"Andrei Alexandrescu (See Website for Email)" wrote:
>
> "dayton" <mvglen04-cnews@yahoo.com> wrote in message
> news:puUzc.86948$gX5.21098@newssvr25.news.prodigy.com...
> > I thought it was long-standing mathematical proof that it is literally
> > impossible to emulate atomic read-modify-write operations in software.
> > You must have hardware support.  This is the purpose of the LOCK
> > instruction prefix in the Intel world, and CAS instructions.
>
> Agreed. The interesting thing is that it's been proven that CAS is all you
> need to implement any multithreaded algorithm (!)

Not any. For DCSI you don't need "C++" CAS (lock doesn't count). For
lockless DCCI (it's not for "singletons") you do need CAS (or LL-SC),
though.

regards,
alexander.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Wed, 16 Jun 2004 20:13:57 +0000 (UTC)
Raw View
dayton wrote: ...

My reply can be found at c.p.t. ("too far offtopic for comp.std.c++").

regards,
alexander.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: SeeWebsiteForEmail@moderncppdesign.com ("Andrei Alexandrescu (See Website for Email)")
Date: Thu, 17 Jun 2004 00:11:41 +0000 (UTC)
Raw View
"Alexander Terekhov" <terekhov@web.de> wrote in message
news:40D0A348.F356D703@web.de...
>
> "Andrei Alexandrescu (See Website for Email)" wrote:
> >
> > "dayton" <mvglen04-cnews@yahoo.com> wrote in message
> > news:puUzc.86948$gX5.21098@newssvr25.news.prodigy.com...
> > > I thought it was long-standing mathematical proof that it is literally
> > > impossible to emulate atomic read-modify-write operations in software.
> > > You must have hardware support.  This is the purpose of the LOCK
> > > instruction prefix in the Intel world, and CAS instructions.
> >
> > Agreed. The interesting thing is that it's been proven that CAS is all
you
> > need to implement any multithreaded algorithm (!)
>
> Not any. For DCSI you don't need "C++" CAS (lock doesn't count). For
> lockless DCCI (it's not for "singletons") you do need CAS (or LL-SC),
> though.

Sorry, I meant for lockless algos.

Andrei


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: SeeWebsiteForEmail@moderncppdesign.com ("Andrei Alexandrescu (See Website for Email)")
Date: Sat, 12 Jun 2004 18:19:10 +0000 (UTC)
Raw View
"Graeme Prentice" <gprenz@hotmail.com> wrote in message
news:85jkc09m4vt5jauf0o1rme73s7vdc8k39p@4ax.com...
> You can force the order of execution as generated by the compiler by
> calling functions for which the *compiler* cannot see the definition of
> - they don't have to result in a change in observable behaviour.  e.g.

Alas, link-time inlining could trump that attempt. (The article discusses
that and gives extensive references.) But, nice try :o). It reflects a good
understanding of the issues that are going on.


Andrei


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: roxito@yahoo.com ("Roshan")
Date: Sat, 12 Jun 2004 18:19:31 +0000 (UTC)
Raw View
> More precisely, the SPs and side effects are important for understanding
> how objects are modified, and how functions are called, but the "as-if"
> rule permits the compiler to reorder expression evaluation, even mix up
> expressions across SP boundaries, provided the observable behavior is
> unchanged.

Ah ! Thats the piece I was missing. So there is indeed a rule in the
standard
that explicitly allows the compiler to ignore ALL other rules if  the
execution
does not affect observable behavior produced by the absract machine. If
its mentioned in the standard then thats jolly good.

My contention arose because I felt sequence points imposed constraints
_in_addition_to_
 observable behavior.

> That is why adding SPs doesn't help the programmer in this situation.
> The only way to force the order of execution is to force changes in
> observable behavior, which is defined as reading or writing volatile
> memory or calling a library I/O function. (Note that "library I/O
> function" is not defined in the standard.)

I wonder if marking "pinstance" in Singleton class as "volatile" would make
any differnce
to the instruction ordering guarantees ? Lets see whats coming in the

An interesting thought comes to mind.. the compiler can actually execute all
steps 1, 2 and 3 in
any arbitrary order  (in some cases) cuz it may not affect observable
behavior
Also all 3 steps cud possibly be reduced to a NOP. Hours of design/coding
work ...all for a NOP :-)
And seems like most instances of calls to functions like "sleep" can be
nuked out of existence.

I am now very eager to see whats in the next installment of the article.

On a side note:  I wonder about the wisdom behind associating only 2 things
(volatile data & IO )
with observable behavior. I would imagine it might have been more useful (to
programmers) if the compiler
were constrained additionally with sequence points. H/w for sure is
constrained by read/write dependencies
while reordering instruction sequence. It seems as if correct programs would
be easier to write then.
But not sure what affect such a thing would have on legitimate optimizations
( and also correctness).
Optimzations that alter the intended logic clearly expressed by the
programmer should preferrably be
deemed evil.

Often ponder if getting into such details make me a better programmer  or a
better language lawyer.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: rl.news@tempest-sw.com (Ray Lischner)
Date: Sat, 12 Jun 2004 18:20:09 +0000 (UTC)
Raw View
On Friday 11 June 2004 11:17 pm, Graeme Prentice wrote:

> You can force the order of execution as generated by the compiler by
> calling functions for which the compiler cannot see the definition of
> - they don't have to result in a change in observable behaviour.=C2=A0=C2=
=A0e.g.

The article cited by the original poster ("C++ and the perils of
double-checked locking, part I", by Scott Meyers and Andrei
Alexandrescu, Dr. Dobb's Journal, July 2004, pp. 46-49) dismantle that
argument. They cite "Post-pass compaction techniques" by Robert Cohn,
et al., Communications of the ACM, August 2003. I haven't read the Cohn
article, so I cannot comment further.
--=20
Ray Lischner, author of C++ in a Nutshell
http://www.tempest-sw.com/cpp

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Sat, 12 Jun 2004 18:20:36 +0000 (UTC)
Raw View
Graeme Prentice wrote:
[...]
> This program might have no observable behaviour at all, but you can
> still force the order of execution generated by the *compiler* by hiding
> the definition of func1, func2 without changing the observable
> behaviour.
>
> Of course if the observable behaviour is the same then who cares what
> the order of execution is so you wouldn't bother to force the order of
> execution unless it did actually affect the observable behaviour but you
> don't have to alter the observable behaviour to force the order of
> execution  ...   - until you bring the linker into it, but what sane
> linker would reorder the code generated by  the compiler just because it
> did nothing (much) ...

http://groups.google.com/groups?threadm=4085953D.F2A670E5%40web.de

regards,
alexander.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Usenet@aristeia.com (Scott Meyers)
Date: Sun, 13 Jun 2004 07:42:48 +0000 (UTC)
Raw View
> The days where compilation and optimization strictly preceded linking and
> execution are already behind us for some environments, and I expect this trend
> to continue.  I can't tell you when the first nail went into that coffin, but
> I can say that when Java put a compiler into the language runtime system, the
> line between compilation, linking, and runing blurred quite noticably.

Which reminds me.  It's my understanding that standard C++ programs can be run
in the .NET environment, and when so executed, code generation takes place at
runtime using JIT technology, just like Java.  Which means that the program's
runtime and the runtime of the back end of the compiler are the same time.
The tricks we're used to playing to hide things from compilers and linkers are
based on assumptions that, in some environments, no longer hold.

Scott

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: comeau@panix.com (Greg Comeau)
Date: Sun, 13 Jun 2004 20:37:09 +0000 (UTC)
Raw View
In article <85jkc09m4vt5jauf0o1rme73s7vdc8k39p@4ax.com>,
Graeme Prentice <gprenz@hotmail.com> wrote:
>You can force the order of execution as generated by the compiler by
>calling functions for which the *compiler* cannot see the definition of

If I understand what you are saying, the standard does not prohibit
that occuring, but neither does it require it.  IOWs, it's a neutral
issue up to the implementor (and possibly the user).

>- they don't have to result in a change in observable behaviour.  e.g.
>
>void func1();
>void func2();
>int k1,k2;
>static int k3,k4;
>
>int main()
>{
> k1=1; k2=2; k3=3; k4=4;
> func1();
> k1=11; k2=22; k3=33; k4=44;
> func2();
>}
>
>The *compiler* has to call the functions in the order they are written
>if it can't see what they are and it has to do the assignments k1=1,
>k2=2 before calling func1 and k1=11 k2=22 after calling func1 but before
>calling func2, but can discard the assignments to k3,k4 completely.

This seems to take a too traditional and too strict view.
Your emphasis above is on compiler, but that operative word
is a slippery one.

>This program might have no observable behaviour at all, but you can
>still force the order of execution generated by the *compiler* by hiding
>the definition of func1, func2 without changing the observable
>behaviour.
>
>Of course if the observable behaviour is the same then who cares what
>the order of execution is so you wouldn't bother to force the order of
>execution unless it did actually affect the observable behaviour but you
>don't have to alter the observable behaviour to force the order of
>execution  ...   - until you bring the linker into it, but what sane
>linker would reorder the code generated by  the compiler just because it
>did nothing (much) ...

In that case, the sane linker, or whatever the heck the beast is
(it's probably not worth labelling what it is) might then just
toss the whole thing.
--
Greg Comeau / Comeau C++ 4.3.3, for C++03 core language support
Comeau C/C++ ONLINE ==>     http://www.comeaucomputing.com/tryitout
World Class Compilers:  Breathtaking C++, Amazing C99, Fabulous C90.
Comeau C/C++ with Dinkumware's Libraries... Have you tried it?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: do-not-spam-benh@bwsint.com (Ben Hutchings)
Date: Sun, 13 Jun 2004 20:37:37 +0000 (UTC)
Raw View
"Roshan" wrote:
<snip>
> On a side note:  I wonder about the wisdom behind associating only 2
> things (volatile data & IO ) with observable behavior.

By the way, the main purpose for "volatile" is to declare
memory-mapped I/O registers, so observable behaviour boils down to
just I/O.  If we treat the compiled program as a black box, as its
normal users will, then its I/O is all that matters, so this is a
sensible rule.

> I would imagine it might have been more useful (to programmers) if
> the compiler were constrained additionally with sequence points. H/w
> for sure is constrained by read/write dependencies while reordering
> instruction sequence. It seems as if correct programs would be
> easier to write then.

I don't understand.  The point of the as-if rule is that only non-
observable changes can be made.  Reordering only causes problems when
your program performs I/O in a way the implementation doesn't
recognise - though shared memory.

When using shared memory (either in a multi-threaded program or for
inter-process communication) you don't just need control over ordering
of reads and writes, but also over atomicity of changes.  Exactly
which types of object can be updated atomically varies between
implementations and even between the different processors that the
generated code may run on.  Typically you need locks to make large
(multi-word) changes effectively atomic.  So forbidding reordering
across sequence points would be neither sufficient nor necessary to
allow writing correct programs that use shared memory.  Instead we
need a mutex facility.

> But not sure what affect such a thing would have on legitimate
> optimizations ( and also correctness).
> Optimzations that alter the intended logic clearly expressed by the
> programmer should preferrably be deemed evil.
<snip>

If by "clearly expressed" you mean "expressed in well-defined C++",
the as-if rule doesn't allow that.  The standard obviously cannot
require implementations to guess the intended meaning of code where
it is not explicitly defined by the standard or the implementation.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Mon, 14 Jun 2004 17:52:26 +0000 (UTC)
Raw View
Ben Hutchings wrote:
[...]
> By the way, the main purpose for "volatile" is to declare
> memory-mapped I/O registers, so observable behaviour boils down to

But the other two "main purposes" of C/C++ volatiles (locals for
setjmp/longjmp and sig_atomic_t statics for signals) have really
nothing to do with "observable behaviour".

> just I/O.  If we treat the compiled program as a black box, as its
> normal users will, then its I/O is all that matters, so this is a
> sensible rule.

Volatiles are insane.

*((volatile long *) ptr) |= 1;

How does that translate to bus transaction? Yeah, implementation-
defined, I know. C++ TR has some new stuff for I/O registers. Look
also at Java's approach with getters and setters:

http://www.rtj.org/rtsj-V1.0.pdf

"....
 Raw Memory Access

 An instance of RawMemoryAccess models a range of physical memory as a
 fixed sequence of bytes. A full complement of accessor methods allow
 the contents of the physical area to be accessed through offsets from
 the base, interpreted as byte, short, int, or long data values or as
 arrays of these types. Whether the offset addresses the high-order or
 low-order byte is based on the value of the BYTE_ORDER static boolean
 variable in class RealtimeSystem. The RawMemoryAccess class allows a
 real-time program to implement device drivers, memory-mapped I/O,
 flash memory, battery-backed RAM, and similar lowlevel software.

 A raw memory area cannot contain references to Java objects. Such a
 capability would be unsafe (since it could be used to defeat Java's
 type checking) and errorprone (since it is sensitive to the specific
 representational choices made by the Java compiler).
 ...."

regards,
alexander.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Fri, 11 Jun 2004 04:32:11 +0000 (UTC)
Raw View
dodo@yahoo.com (Perfect One) writes:

>  I dont think this is the case. here is a trivial solution..
>
> void Singleton::assign( Singleton * obj)
> {
>    pinstance = obj;
> }
>
>    Singleton* instance() {
>       ..
>       assign ( new Singelton( ) ) ;     // we introduce a SP before
> assignment can occur
>      ..
>    }
>
> What I have done here is simply introduce a SP that will ensure the
> correct ordering. i.e introduce a SP just after step 2 and right before
> step 3.

Nice.  I think, IIUC, this trivial solution also works:

 template <class T>
 inline T identity(T x) { return x; }

    Singelton* instance()
    {
       pinstance = identity(new Singelton);
    }

Also,

    shared_ptr<Singleton> pinstance;

    Singleton* instance()
    {
       pinstance.reset(new Singleton);
    }

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: SeeWebsiteForEmail@moderncppdesign.com ("Andrei Alexandrescu (See Website for Email)")
Date: Fri, 11 Jun 2004 16:05:06 +0000 (UTC)
Raw View
"Perfect One" <dodo@yahoo.com> wrote in message
news:40C8DABC.2350547@yahoo.com...
> This is regarding the article titled "C++ & Double-Checked Locking" by
> Scott Meyers and Andrei in DDJ July 2004 issue.

I remind that the article comes in two parts, the second of which will come
in the August issue.

Andrei


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: not-impt@nowhere.com ("Roshan")
Date: Fri, 11 Jun 2004 16:05:53 +0000 (UTC)
Raw View
> Nice.  I think, IIUC, this trivial solution also works:


Thanks  Dave for that confirmation. Atleast there is one more individual
besides myself who agrees

Sequence points are indeed not an important and useful tool for ensuring
strict ordering on instructions.

Else there could be pure havoc going on underneath...making trivial programs
impossible to write.

What is most surprising to me is that ....the authors tell me ....the issue
has been discussed with some expersts

including  compiler writers. I just hope they haven't made their compilers
that smart yet... if this is true.

- Roshan

by the way, Dave whats IIUC ? :-)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: hyrosen@mail.com (Hyman Rosen)
Date: Fri, 11 Jun 2004 16:05:58 +0000 (UTC)
Raw View
Perfect One wrote:
> I think the reasoning in this article is fundamentally flawed

As has been discussed here many times, the fundamental flaw of
DCLP is that the program may execute on hardware which reorders
writes to memory regardless of what your program says.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gprenz@hotmail.com (Graeme Prentice)
Date: Fri, 11 Jun 2004 16:06:27 +0000 (UTC)
Raw View
On Thu, 10 Jun 2004 23:32:39 +0000 (UTC), Perfect One wrote:

[ snip ]

>class Singelton {
>   Singleton* pinstance;
>public:
>   static Singleton* instance( ) {
>      ..
>      pinstance =  new Singelton( );    //  whats going on here ?
>      ..
>   }
>};
>
>The assignment statement  involves 3 steps. 1) allocate mem for object,
>2) invoke constructor 3) assign address of object to pointer
>
>The problem that the authors note is that within the rules of the
>standard steps 2 and 3 can be executed in any order after step 1.


Yep, step one has to be done first because its result is needed in step
2 and 3 and possibly also because the compiler may not know what the
call to operator new will do so has to assume that it could do something
that could affect the constructor.


[snip]

>
>Now the authors claim that there is no way for the programmer to
>express, in C or C++, the strict ordering that step 3 should follow step
>
>2.  I dont think this is the case. here is a trivial solution..
>
>void Singleton::assign( Singleton * obj)
>{
>   pinstance = obj;
>}
>
>   Singleton* instance() {
>      ..
>      assign ( new Singelton( ) ) ;     // we introduce a SP before
>assignment can occur
>     ..
>   }
>
>What I have done here is simply introduce a SP that will ensure the
>correct ordering. i.e introduce a SP just after step 2 and right before
>step 3.

No, this does not guarantee the order.


>
>At the heart of the matter is what the Scott writes to me in a pvt email
>
>"As we tried to point out in the article, sequence points don't offer
>very much help with this problem."

They don't help much because they determine the behaviour of the
abstract machine, and don't place any requirement on the generated code
unless they affect the observable behaviour.

>
>This is incorrect (I feel) as demonstrated by the solution above.
>
>Now here is some standardese to back me up.....
>
>1.9.6       // Definition of observable behavior
>The observable behavior of the abstract machine is its sequence of reads
>
>and writes to volatile data and
>calls to library I/O functions.

If the compiler can see all the code involved in calling the Singleton
constructor and the assign function, then it can call assign before the
constructor because it makes no difference to the observable behaviour.

One way to guarantee the order is to write a default constructor for the
Singleton class which makes a call to a function the compiler cannot see
the definition of and pass the address of pinstance to it  - the
compiler then has to assume that the hidden function could modify
pinstance so must be called before pinstance is assigned in step 3.

Sequence points and other rules determine how the abstract machine
executes your code.  The observable behaviour produced by the abstract
machine must be reproduced by the real machine.

Graeme

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Fri, 11 Jun 2004 16:07:22 +0000 (UTC)
Raw View
David Abrahams wrote:
[...]
> > What I have done here is simply introduce a SP that will ensure the
> > correct ordering. i.e introduce a SP just after step 2 and right before
> > step 3.
>
> Nice.  I think, IIUC, this trivial solution also works: ...

C/C++ SPs are useless for threading.

http://groups.google.com/groups?threadm=40867810.2E06D061%40web.de

The standard shall define atomic<> with msync::<hoist and sink barrier
combinations>.

http://lists.boost.org/MailArchives/boost/msg65727.php

regards,
alexander.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: do-not-spam-benh@bwsint.com (Ben Hutchings)
Date: Fri, 11 Jun 2004 16:10:31 +0000 (UTC)
Raw View
Perfect One wrote:
> This is regarding the article titled "C++ & Double-Checked Locking" by
> Scott Meyers and Andrei in DDJ July 2004 issue.
>
> I think the reasoning in this article is fundamentally flawed due the
> authors mixing up a couple of concepts (observable behavior & side
> effects) that are defined  separately in the standard.
<snip>

You are mixing up the ordering of memory access by a thread of
execution with the synchronisation of memory writes across threads.
The C++ standard makes no guarantees about the latter because it does
not acknowledge the possibility of multiple threads of execution, and
most implementations do not make any guarantees in the absence of
explicit synchronisation because it would make multi-threaded code
slow, particularly on multi-processor systems.  This has been
discussed to death elsewhere; try searching Google Groups for
"group:comp.lang.c++.moderated memory observable
(synchronisation|synchronization)".

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Fri, 11 Jun 2004 16:43:08 +0000 (UTC)
Raw View
not-impt@nowhere.com ("Roshan") writes:

>> Nice.  I think, IIUC, this trivial solution also works:
>
>
> Thanks  Dave for that confirmation. Atleast there is one more individual
> besides myself who agrees

Just to be clear, I haven't read the article, I don't have any idea
about it's conclusions, and I haven't drawn any conclusions about how
the ability to sequence a pointer assignment affects the correctness
of DCLP.

> Sequence points are indeed not an important and useful tool for ensuring
> strict ordering on instructions.
>
> Else there could be pure havoc going on underneath...making trivial programs
> impossible to write.
>
> What is most surprising to me is that ....the authors tell me ....the issue
> has been discussed with some expersts

If they have discussed the issue with Dave Butenhof, I'd trust the
conclusions.  There aren't too many others whose conclusions about
threading issues I have a lot of faith in.

> including  compiler writers. I just hope they haven't made their compilers
> that smart yet... if this is true.
>
> - Roshan
>
> by the way, Dave whats IIUC ? :-)

See the handy reference:
http://www.astro.umd.edu/~marshall/abbrev.html#I

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Fri, 11 Jun 2004 16:43:08 +0000 (UTC)
Raw View
terekhov@web.de (Alexander Terekhov) writes:

> David Abrahams wrote:
> [...]
>> > What I have done here is simply introduce a SP that will ensure the
>> > correct ordering. i.e introduce a SP just after step 2 and right before
>> > step 3.
>>
>> Nice.  I think, IIUC, this trivial solution also works: ...
>
> C/C++ SPs are useless for threading.
>
> http://groups.google.com/groups?threadm=40867810.2E06D061%40web.de

Maybe so; I was only commenting on what it takes to introduce a
sequence point.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: adadad@asdadd.com (Roshan)
Date: Fri, 11 Jun 2004 18:07:40 +0000 (UTC)
Raw View
When did I mention anything about multi threading ?

Let me remind you.... my arguments about sequence ordering / seuqnce points
/observalble behavior are not specifically for multithreaded programs. It
applies to single threaded. Before we get to MThding and DCLP we need to
understand instruction ordering guarantees
for Single Threaded programs.

-Roshan

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: rl.news@tempest-sw.com (Ray Lischner)
Date: Fri, 11 Jun 2004 23:22:44 +0000 (UTC)
Raw View
On Thursday 10 June 2004 07:32 pm, Perfect One wrote:

> 2.  I dont think this is the case. here is a trivial solution..
>
> void Singleton::assign( Singleton * obj)
> {
>    pinstance = obj;
> }
>
>    Singleton* instance() {
>       ..
>       assign ( new Singelton( ) ) ;     // we introduce a SP before
> assignment can occur
>      ..
>    }

I side with Meyers and Alexandrescu on this one.

The sequence point (SP) doesn't matter. The execution of the abstract
machine is defined in section 1.9 according to observable behavior, not
sequence points and side effects.

More precisely, the SPs and side effects are important for understanding
how objects are modified, and how functions are called, but the "as-if"
rule permits the compiler to reorder expression evaluation, even mix up
expressions across SP boundaries, provided the observable behavior is
unchanged.

That is why adding SPs doesn't help the programmer in this situation.
The only way to force the order of execution is to force changes in
observable behavior, which is defined as reading or writing volatile
memory or calling a library I/O function. (Note that "library I/O
function" is not defined in the standard.)
--
Ray Lischner, author of C++ in a Nutshell
http://www.tempest-sw.com/cpp

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: adadad@asdadd.com (Roshan)
Date: Fri, 11 Jun 2004 23:22:49 +0000 (UTC)
Raw View
> Sequence points are indeed not an important and useful tool for ensuring
> strict ordering on instructions.

I meant... Sqeuence points ARE an important and useful tool ...

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gprenz@hotmail.com (Graeme Prentice)
Date: Sat, 12 Jun 2004 03:17:01 +0000 (UTC)
Raw View
On Fri, 11 Jun 2004 23:22:44 +0000 (UTC), Ray Lischner wrote:

>The only way to force the order of execution is to force changes in
>observable behavior, which is defined as reading or writing volatile
>memory or calling a library I/O function. (Note that "library I/O
>function" is not defined in the standard.)


You can force the order of execution as generated by the compiler by
calling functions for which the *compiler* cannot see the definition of
- they don't have to result in a change in observable behaviour.  e.g.

void func1();
void func2();
int k1,k2;
static int k3,k4;

int main()
{
 k1=1; k2=2; k3=3; k4=4;
 func1();
 k1=11; k2=22; k3=33; k4=44;
 func2();
}

The *compiler* has to call the functions in the order they are written
if it can't see what they are and it has to do the assignments k1=1,
k2=2 before calling func1 and k1=11 k2=22 after calling func1 but before
calling func2, but can discard the assignments to k3,k4 completely.
This program might have no observable behaviour at all, but you can
still force the order of execution generated by the *compiler* by hiding
the definition of func1, func2 without changing the observable
behaviour.

Of course if the observable behaviour is the same then who cares what
the order of execution is so you wouldn't bother to force the order of
execution unless it did actually affect the observable behaviour but you
don't have to alter the observable behaviour to force the order of
execution  ...   - until you bring the linker into it, but what sane
linker would reorder the code generated by  the compiler just because it
did nothing (much) ...

Graeme

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Sat, 12 Jun 2004 18:18:20 +0000 (UTC)
Raw View
gprenz@hotmail.com (Graeme Prentice) writes:

> You can force the order of execution as generated by the compiler by
> calling functions for which the *compiler* cannot see the definition of
> - they don't have to result in a change in observable behaviour.  e.g.
>
> void func1();
> void func2();
> int k1,k2;
> static int k3,k4;
>
> int main()
> {
>  k1=1; k2=2; k3=3; k4=4;
>  func1();
>  k1=11; k2=22; k3=33; k4=44;
>  func2();
> }
>
> The *compiler* has to call the functions in the order they are written
> if it can't see what they are and it has to do the assignments k1=1,
> k2=2 before calling func1 and k1=11 k2=22 after calling func1 but before
> calling func2, but can discard the assignments to k3,k4 completely.

Optimization can still happen at link time, when the object code for
func1 and func2 is available.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Usenet@aristeia.com (Scott Meyers)
Date: Sat, 12 Jun 2004 18:18:38 +0000 (UTC)
Raw View
On Sat, 12 Jun 2004 03:17:01 +0000 (UTC), Graeme Prentice wrote:
> execution  ...   - until you bring the linker into it, but what sane
> linker would reorder the code generated by  the compiler just because it
> did nothing (much) ...

One where the compiler, the linker, and the optimizer are the same program,
and native code isn't generated until linktime, such as the latest MS compiler
with appropriate switches thrown (as I understand it).  I believe that Intel's
compiler can do this kind of thing, too.  It's not that uncommon a technology
any more.

The days where compilation and optimization strictly preceded linking and
execution are already behind us for some environments, and I expect this trend
to continue.  I can't tell you when the first nail went into that coffin, but
I can say that when Java put a compiler into the language runtime system, the
line between compilation, linking, and runing blurred quite noticably.

Which means that even calling functions defined in external translation units
isn't guaranteed to order instructions, something we point out in the article.
For instruction ordering, the only thing you can count on is observable
behavior.  Trying to trick your compiler/linker into ordering the instructions
it generatess in a particular way is, ultimately, a game between
compiler/linker/runtime authors and application authors, and the deck is
heavily stacked in favor of the compiler/linker/runtime authors.  The only
constraint on what they do is that it must match the observable behavior of
the abstract machine defined by the standard.  How they match that behavior is
completely up to them.

Scott

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dodo@yahoo.com (Perfect One)
Date: Thu, 10 Jun 2004 23:32:39 +0000 (UTC)
Raw View
This is regarding the article titled "C++ & Double-Checked Locking" by
Scott Meyers and Andrei in DDJ July 2004 issue.

I think the reasoning in this article is fundamentally flawed due the
authors mixing up a couple of concepts (observable behavior & side
effects) that are defined  separately in the standard.

Background: In the following statement...

class Singelton {
   Singleton* pinstance;
public:
   static Singleton* instance( ) {
      ..
      pinstance =  new Singelton( );    //  whats going on here ?
      ..
   }
};

The assignment statement  involves 3 steps. 1) allocate mem for object,
2) invoke constructor 3) assign address of object to pointer

The problem that the authors note is that within the rules of the
standard steps 2 and 3 can be executed in any order after step 1. This
is may be true as I cant find a good "sequence point" (SP) [defined in
1.9.7] that will disallow this. Yes there are 3 SPs here. One at start
of object's constructor, one at the end of the object's construtor and
one at the end of the assignment statement. But with just these 3 SPs I
am unable to make a solid case for strict ordering of 1,2 and 3. (the
authors fail to mention the first 2 SPs )

Now the authors claim that there is no way for the programmer to
express, in C or C++, the strict ordering that step 3 should follow step

2.  I dont think this is the case. here is a trivial solution..

void Singleton::assign( Singleton * obj)
{
   pinstance = obj;
}

   Singleton* instance() {
      ..
      assign ( new Singelton( ) ) ;     // we introduce a SP before
assignment can occur
     ..
   }

What I have done here is simply introduce a SP that will ensure the
correct ordering. i.e introduce a SP just after step 2 and right before
step 3.

At the heart of the matter is what the Scott writes to me in a pvt email

"As we tried to point out in the article, sequence points don't offer
very much help with this problem."

This is incorrect (I feel) as demonstrated by the solution above.

Now here is some standardese to back me up.....

1.9.6       // Definition of observable behavior
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       // Definition of side effects and SP
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.7)

1.9.12
A fullexpression is an expression that is not a subexpression of another

expression.

1.9.16
There is a sequence point at the completion of evaluation of each
fullexpression

1.9.17
When calling a function (whether or not the function is inline), there
is a sequence point after the evaluation
of all function arguments (if any) which takes place before execution of

any expressions or statements in
the function body. There is also a sequence point after the copying of a

returned value and before the execution
of any expressions outside the function11).

also 1.9.18  may be of interest but is not relevant for my argument.




Footnote:
All that said.. I am still not fully convinced that steps 2 and 3 can
execute in any order. Reasoning : Order of evaluation of assignment
operator is from right to left. So the "new expression" has to evaluated

completely first. But things get fuzzy as there is no "solid" SP here to

disallow the compiler from reordering. I am not sure what role the SPs
at the start and end of obect's contructor would play (if any) in
defining the strict ordering of the steps 2 and 3.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]