This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Resolved status.

1147. Non-volatile atomic functions

Section: 32.5.8.2 [atomics.types.operations] Status: Resolved Submitter: Jeffrey Yasskin Opened: 2009-06-16 Last modified: 2016-01-28

Priority: Not Prioritized

View other active issues in [atomics.types.operations].

View all other issues in [atomics.types.operations].

View all issues with Resolved status.

Discussion:

Addresses US 90

The C++0X draft declares all of the functions dealing with atomics (section 32.5.8.2 [atomics.types.operations]) to take volatile arguments. Yet it also says (29.4-3),

[ Note: Many operations are volatile-qualified. The "volatile as device register" semantics have not changed in the standard. This qualification means that volatility is preserved when applying these operations to volatile objects. It does not mean that operations on non-volatile objects become volatile. Thus, volatile qualified operations on non-volatile objects may be merged under some conditions. — end note ]

I was thinking about how to implement this in gcc, and I believe that we'll want to overload most of the functions on volatile and non-volatile. Here's why:

To let the compiler take advantage of the permission to merge non-volatile atomic operations and reorder atomics in certain, we'll need to tell the compiler backend about exactly which atomic operation was used. So I expect most of the functions of the form atomic_<op>_explicit() (e.g. atomic_load_explicit, atomic_exchange_explicit, atomic_fetch_add_explicit, etc.) to become compiler builtins. A builtin can tell whether its argument was volatile or not, so those functions don't really need extra explicit overloads. However, I don't expect that we'll want to add builtins for every function in chapter 29, since most can be implemented in terms of the _explicit free functions:

class atomic_int {
  __atomic_int_storage value;
 public:
  int fetch_add(int increment, memory_order order = memory_order_seq_cst) volatile {
    // &value has type "volatile __atomic_int_storage*".
    atomic_fetch_add_explicit(&value, increment, order);
  }
  ...
};

But now this always calls the volatile builtin version of atomic_fetch_add_explicit(), even if the atomic_int wasn't declared volatile. To preserve volatility and the compiler's permission to optimize, I'd need to write:

class atomic_int {
  __atomic_int_storage value;
 public:
  int fetch_add(int increment, memory_order order = memory_order_seq_cst) volatile {
    atomic_fetch_add_explicit(&value, increment, order);
  }
  int fetch_add(int increment, memory_order order = memory_order_seq_cst) {
    atomic_fetch_add_explicit(&value, increment, order);
  }
  ...
};

But this is visibly different from the declarations in the standard because it's now overloaded. (Consider passing &atomic_int::fetch_add as a template parameter.)

The implementation may already have permission to add overloads to the member functions:

16.4.6.5 [member.functions] An implementation may declare additional non-virtual member function signatures within a class:
...

but I don't see an equivalent permission to add overloads to the free functions.

[ 2009-06-16 Lawrence adds: ]

I recommend allowing non-volatile overloads.

[ 2009-10 Santa Cruz: ]

NAD EditorialResolved. Addressed by N2992.

Proposed resolution: