[cpp-threads] Some concerns with N2324

Lawrence Crowl lawrence.crowl at gmail.com
Mon Jul 16 16:35:37 BST 2007


On 7/15/07, Roger Orr <rogero at howzatt.demon.co.uk> wrote:
> Our main concern with the paper is with the structs, and in
> particular the C++ specific parts.
> We feel there is a danger that, by attempting to make them
> easy to use, it is too easy to abuse the atomic types.

In drafting this paper, I chose to put in functions rather than
leave them out under the philosophy that a committee is much
better at reducing a proposal than expanding it.

That said, the primary abuse will be in choosing to use the
atomic when it is not necessary.  Relatively few programmers
will be using them, and why should they not have convenient
access?

> (1) Implicit construction from, and conversion to, native type.
>
> We fear that having conversion in both directions implicit is
> dangerous.
>
>   atomic_int a1;
>   a1 = a1; // won't compile, as copy assignment is marked 'delete'
>   [assuming N2326]
>   a1 = a1 + 1; // will compile (I believe) but is not atomic
>   // rhs a1 converts to int, 1 is added and then operator=( int ) is used

That behavior is the intent of the proposal.

>
> We weren't sure whether we wanted a conversion operator to
> native type. If so we'd like this conversion operator explicit
> [subject to N2333 being adopted]

Note that the load function serves in the absence of a conversion
operator.

> We also prefer the ctor to be explicit.

As atomic only makes sense for variables, adding explicit
seems like a good idea.

> (2) We were unsure whether we liked operator++/--
>
> (a) the unconventional return value
> Prefix operator++/operator-- traditionally returns a reference to self.
> As mentioned in the paper this is not appropriate for atomics.
>
> However, we wondered if this might lead to problems - for example
>
> void v( std::atomic_int const & a );
>
> void func( std::atomic & shared_atomic )
> {
>   std::atomic_int shared_atomic;
>   v( ++shared_atomic );
> }
>
> v() is now using a temporary, created from the value returned from ++,
> rather than 'shared_atomic' itself.  This might be a hard problem to
> resolve when some of the functions are templates.

I don't think one can reasonably write a template that applies to both
general integral and atomic integral types; the semantic implications
of multiple updates will prevent that.

> (b) missing optimisation opportunity (perhaps)
>
> Some platforms have special instructions for inc/dec, but the paper has
> no inc/dec methods - the operators are simply wrappers for calls to
> fetch_add/fetch_sub with an argument of 1.  Would there be benefit in
> having some explicit operations for these cases?

Compilers can propagate constant expressions into code generation.
We expect them to be doing so for the memory_order parameter
anyway.

>
> (3) We were worried by swap()
>
> int main()
> {
>   atomic_int a1(1);
>   atomic_int a2(2);
>
>   a1.swap( a2 );
>   std::cout << a1 << '/' << a2 << std::endl;
> }
>
> What do you *think* this should output? We get 2/2 and think
> this is a problem; we would like this not to compile.

The swap operation is essentially an assignment, but returning the
old value rather than the new value.  I am amenable to a change
in name, but the "swap" name follows from "compare and swap"
and practice in the concurrency community.

Admittedly, it is a poor match to std::swap.

> (4) Should we prefer a type to an enumeration?
>
> We wondered whether the 'memory_order' could to be a set of
> typed objects, like with new(nothrow), rather than an enumeration.
> However this might cause problems with the C API.
> Or would this be a possible application of strong enum (N2213)?

I would like to ensure that use of the base functions is compilable
from C.

>
> (5) We were unhappy today with public derivation of atomic<>
> specialisations from the underlying type.
> For instance:  template<> struct atomic< int > : atomic_int
>
> We'd prefer containment to inheritance, with something like
> std::string's c_str() member function to access the member data.

The intent was to permit code like the following:

   extern int function( atomic_int * );
   atomic<int> variable;
   .... function( &variable ) ....

In essence, this ability enables passing C++-named objects into
C code; there is not "value-copy" of atomic types.

> (6) We remain a little worried about the design whereby the
> same named struct has different semantics in C and C++.
>
> Eg in C pretty well all you can do with an atomic_int is to pass
> it by address to free functions.
> In C++ you can call member functions, assign to it, etc.

First, there is some additional expressiveness one gets with C++,
and we should be able to exploit that.  Second, the C committee
may well add appropriate operators to the atomic types.

> This sort of 'dual-personality' type seems to be without a
> precedent in the standard and we fear this may make the
> atomics harder to teach and use since code valid in C is invalid
> in C++.

No, the C code is either syntactically not possible or semantically
incorrect, and so there is no valid C code which does not have
exactly the same interpretation in C++.  The primary difference
is that in C, there is no mechanism to prevent use of the bad
assignment operator.

-- 
Lawrence Crowl



More information about the cpp-threads mailing list