[cpp-threads] memory model

Boehm, Hans hans.boehm at hp.com
Thu May 5 21:36:48 BST 2005


> -----Original Message-----
> From: Peter Dimov
> 
> - the remark about f((a, b), c) is redundant and probably needs to be 
> removed. There is a sequence point between a and b, and there are no 
> sequence points between (a, b) and c.
I'd love to do that.  I really don't want to deal with this stuff.
But I'm not convinced I can.  I just did a search through the current
standard from the committee web site, and all the text that mentions
sequence points is written as though they were particular points
in a sequential program execution, with a total order between all
operations in the execution.

Look particularly at paragraph 7, section 1.9.  It talks about
previous and subsequent evaluations, with no indication that some
evaluations might fall in neither category, and no indication
that some sequence points might be unordered, i.e. neither might
come before the other.

(I don't think this has changed in ages.  Thus for those of us with
old copies of the standard, I think that's fine.  Unless there's a newer
version that I don't know about either.)

My impression is that it is possible to read this the way we want,
but that's not the way I would have read it.  If indeed it is common
to read this in the right way, i.e. such that there are only partial
orders between sequence points and sequence points and other operations,
it should be easy to make that explicit, I hope. 

I'll update the comments in the proposal to reflect this.
> 
> - the term "field" is alien to C++. C++ is specified in terms 
> of "objects"; 
> when it's necessary to distinguish between the various types 
> of objects the 
> standard uses "subobject", "array element", "nonstatic data 
> member of a 
> class" and so on. But I think that the memory model does not need to 
> distinguish between the different kinds of objects.

I'll change it to "data member", though we still have "bit-fields", as
we do in the standard.
> 
> - "mutable volatile" already has a meaning.
Yes.  I think that was more of a placeholder.

I changed it to __async for now.  I'm OK with leaving it that way, or
with using "protected", which we are tentatively abusing for similar
purposes
with function-scope statics.
> 
> - I don't believe that constructors need any special treatment. In:
> 
> struct X { int m; X( int m ): m( m ) {} };
> struct Y { int m; };
> 
> X x( 1 );
> Y y = { 1 };
> int a[1] = { 1 };
> int b = 1;
> 
> I see no difference between x and the other variables from 
> memory model 
> point of view.

This may indeed not be an issue for us.

The question here is if I have

X *p = 0;

and then later I execute

	{
		X x(1);
		p = &X;
	}

and another threads reads a non-null p.  Is the
x field guaranteed to be initialized?

But I think the answer is that's illegal unless p
is declared __async volatile, in which case we get
the necessary ordering constraint from that.

> 
> - I think that silently making "mutable volatile uint64_t" atomic on 
> platform A and nonatomic on platform B is very much not in 
> the spirit of 
> making atomic operations accessible to the average 
> programmer. I'd prefer 
> "mutable volatile" to be a compile-time error ("require a 
> diagnostic") when 
> the access is not atomic.
> 
> - I believe that we must specify the interaction of the 
> __thread storage 
> duration with user-defined types, in particular constructors and 
> destructors.
> 
> Existing practice simply disallows such types. This is 
> unacceptable; it 
> makes __thread (even) less useful than pthread_setspecific.
> 
> I suggest the following tentative formulation:
> 
> - In a given thread, the constructors of all variables with 
> __thread storage 
> duration are run before the first access to a variable with 
> __thread storage 
> duration (but the implementations are strongly encouraged to 
> execute the 
> constructors before the thread procedure is entered);
> 
> - The constructors are run in the same order that could have 
> been used had 
> the variables been declared with static storage duration;
> 
> - The destructors of the variables with __thread storage 
> duration are run 
> immediately after the thread procedure returns (normally or via a 
> cancellation exception or pthread_exit) in the reverse order of 
> construction;
> 
> - The constructor or the destructor of a variable with 
> __thread storage 
> duration shall not throw exceptions.
> 
> The above constructor rule does not cover function-scope 
> __thread statics, 
> which obey the usual "constructed when the function is first 
> entered by a 
> given thread" rule. 
Sounds reasonable to me.  (I actually think __thread is quite
useful without this, since you at least get __thread pointers,
which is what I tend to use.  And unlike pthread_..._specific,
I think they're generally faster than what I can implement myself.)

I'll put it in for now.  I expect the only disagreement we
might get is from implementors.

Hans




More information about the cpp-threads mailing list