[cpp-threads] Prism 0.9.1, and further on the effects of races

Peter Dimov pdimov at mmltd.net
Fri Sep 22 12:32:26 BST 2006


Herb Sutter wrote:
> Peter wrote:
>>> Here's a different and maybe more useful way to put this question:
>>> Should line c be allowed any additional behaviors than if global_ptr
>>> pointed to a type having no virtual functions?
>>
>> I don't see why there should be any difference. The vptr is just a
>> hidden member of the object. Consider:
> [an example with an explicit program-declared member of pointer type]
>
> The key word is "hidden." The vptr is not a member of the object as
> the object is declared in the program source code, and I do think
> that's a material difference. In the other email I sent in response
> to Hans, I compared it to a COW string; if the string implementer
> generates additional variables and machinery, it's his job to 'hold
> the caller harmless' -- make the caller no worse off than he would be
> if the implementer hadn't generated the fancy machinery. Put another
> way, implementation details shouldn't affect semantics.
>
> Consider: If we go down the path of treating vptrs as UD-bait, then
> it's perilous even to dereference atomic<T*> in the current proposal.
> In particular, any virtual function call could cause a wild branch,
> even though there's not a function pointer in sight in the source
> code (and note that at test time there may not even be a vptr there,
> but it could get mixed in later, e.g., via inheritance from a
> template parameter).
>
> If that's what's being proposed, does anyone really feel good about
> saying that calling any virtual function through an atomic<T*> is
> undefined behavior?

I'm really not sure what you mean. An atomic<T*> cannot be dereferenced as 
it doesn't have operator*. It can be read and written using an ordering 
constraint. If:

- thread 1 writes "new T" into an atomic<T*> without a release constraint,
- thread 2 reads the same atomic<T*> without an acquire constraint,

then

- thread 2 is not guaranteed to observe the effects that preceded the thread 
1 write in program order, even when thread 2 does observe the written value 
of that atomic<T*>. In particular, thread 2 is not guaranteed to observe the 
effects of "new T".

This makes the above effectively undefined behavior regardless of what T is 
because thread 2 is not guaranteed to observe a fully constructed T object. 
It doesn't matter whether T is polymorphic. Accessing an object before its 
constructor has completed is, and always has been, undefined behavior.




More information about the cpp-threads mailing list