[cpp-threads] Review of N2995 and resulting email threads

Paul E. McKenney paulmck at linux.vnet.ibm.com
Sat Oct 24 19:23:28 BST 2009


On Sat, Oct 24, 2009 at 08:00:25PM +0300, Peter Dimov wrote:
> Paul E. McKenney wrote:
> ...
>>>> [Note: Atomic and locking objects are not trivially
>>>> copyable [3.9], so executions that access atomic
>>>> or locking objects as non-atomic objects, for example
>>>> through an array of character type, will have undefined
>>>> behaviour - end note]
>
> ...
>
>> OK, so your point is that there are the following additional cases?
>
> My main point is that I don't believe that the note is correct, 
> pedantically speaking, as I don't see why reading the storage bytes of an 
> atomic object without a data race would invoke undefined behavior. But 
> there are some practical considerations as well.

Pedantically speaking, undefined behavior does not rule this out, it
simply refuses to mandate it.  ;-)

More seriously, the working draft mandates that even the atomic integral
types are not trivially copyable in 29.5.1p2 when it states that they have
a deleted copy constructor.  The same is true of the other atomic types
(29.5.2p1 and 29.5.3p2).  Furthermore, 3.9p2 and 3.9p3 do not specify
what happens when you apply memcpy to an object of a type that is not
trivially copyable.  So undefined behavior really is the status quo here.

> Note that, for example, 29.5.1/2 says that atomic integral types have 
> standard layout and support aggregate initialization, and 29.5.1/5 
> encourages them to have the same size as the underlying integral type. I 
> can't think of any reason to mandate standard layout _unless_ the intent is 
> for atomic_int to be struct { int }; there is nothing to be gained from the 
> standard layout requirement otherwise.
>
> And if atomic_int is, in fact, struct { int }, standard layout and 9.2/17 
> allow one to read and write the underlying int via a 
> reinterpret_cast<int&>( v ).

Though there might be an associated lock on architectures that require
this, hence this cannot be guaranteed across all implementations.

> All this is fully supported by the memory model. As long as there's no 
> race, everything is fine and well. 

In the past, there have been machines where accessing memory (-not-
MMIO!) could have side effects, such as incrementing the location
accessed.  In this case, a memcpy() copy well have side-effect.  This sort
of thing has fallen out of favor, but given that the Moore's-Law induced
frequency increases have ended, it would not be wise to rule out its
reappearance.

Let's split this out:

1.	Any non-atomic access (memcpy, reinterpreting_cast, ...)
	that is involved in a data race is already undefined behavior.

2.	Any non-data-race non-atomic store to an atomic variable
	should invoke undefined behavior due to the possibility
	of additional data required for non-lock-free implementations
	of atomic objects, as well as the possibility of special
	hardware for atomic objects.

3.	A non-data-race non-atomic load from an atomic variable
	should invoke undefined behavior due to the possibility of
	special hardware for atomic objects.

Of these, #3 is the least worrisome to me.  If you have a strong use
case that does not otherwise rely on unspecified or undefined behavior
(as would a debugger), please do not keep it a secret!

							Thanx, Paul



More information about the cpp-threads mailing list