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

R

Roshan

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:
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.
 
J

Joe Seigh

Roshan said:
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)

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.

There's no way to discuss this with respect to threading since the C++
standard does not take threading into account. The standard is only
meaningful for a single thread.

For C, POSIX compliance defines additional requirements that make C
work for POSIX functions. For other synchronization not defined by
POSIX, other techniques are used to create the right effect. SP's
are only part of it. For more on DCL see here
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

There's one or two patterns that might help depending on what you
are doing. But you sort of have to be aware of the problem with
DCL to realize that you should resort to an alternate solution.

Joe Seigh
 
R

Roshan

There's no way to discuss this with respect to threading since the C++
standard does not take threading into account. The standard is only
meaningful for a single thread.

I never mention anything about multithreaded programs. Everything that I
have i said above is applicable
to single threaded programs ! And this does not even involve DLCP per say..
Its just about restrictions on
srict ordering of instructions(in a single thread of execution), sequence
points, side effects and observable behavior.
DLCP and the problems associated with comes later on...once you understand
instruction sequencing guarantees correctly.
 
A

Alexander Terekhov

Roshan wrote:

Don't multi-post.
I never mention anything about multithreaded programs. ...

"As if" rule. Whatever the standard says, transformations that
can't break a single-threaded program are legal (and desirable).

More can be found in the reply on c.s.c++.

regards,
alexander.
 
T

tom_usenet

This is regarding the article titled "C++ & Double-Checked Locking" by
Scott Meyers and Andrei in DDJ July 2004 issue.

I haven't read the article, but I have just read this:
http://www.nwcpp.org/Downloads/2004/DCLP_notes.pdf
which I imagine is very similar.
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:
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 )
Ok.

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
..

There's a far more trivial solution:

Singleton* temp = new Singleton(); //sequence point here.
pinstance = temp;

(Note they use this example later).
I really don't think the authors are trying to say there is no way of
introducing a sequence point after the construction of the singleton
and before the assignment to the instance variable.
}

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.

Huh!? The problem in question involves writing correct lazy evaluation
using DCL. How does introducing a sequence point help this?

Tom
 
B

Ben

What you said was absolutely correct regarding a single-threaded
program if you don't drag DCL into this issue.

However, DCL is about multi-threading and Sequence Point is somewhat
irrelevant for it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top