C++0x | Why std::atomic overloads each method with the volatile-qualifier?

1.4k views Asked by At

The following excerpt from the current draft shows what I mean:

namespace std {
    typedef struct atomic_bool {
        bool is_lock_free() const volatile;
        bool is_lock_free() const;
        void store(bool, memory_order = memory_order_seq_cst) volatile;
        void store(bool, memory_order = memory_order_seq_cst);
        bool load(memory_order = memory_order_seq_cst) const volatile;
        bool load(memory_order = memory_order_seq_cst) const;
        operator bool() const volatile;
        operator bool() const;
        bool exchange(bool, memory_order = memory_order_seq_cst) volatile;
        bool exchange(bool, memory_order = memory_order_seq_cst);
        bool compare_exchange_weak(bool&, bool, memory_order, memory_order) volatile;
        bool compare_exchange_weak(bool&, bool, memory_order, memory_order);
        bool compare_exchange_strong(bool&, bool, memory_order, memory_order) volatile;
        bool compare_exchange_strong(bool&, bool, memory_order, memory_order);
        bool compare_exchange_weak(bool&, bool, memory_order = memory_order_seq_cst) volatile;
        bool compare_exchange_weak(bool&, bool, memory_order = memory_order_seq_cst);
        bool compare_exchange_strong(bool&, bool, memory_order = memory_order_seq_cst) volatile;
        bool compare_exchange_strong(bool&, bool, memory_order = memory_order_seq_cst);
        atomic_bool() = default;
        constexpr atomic_bool(bool);
        atomic_bool(const atomic_bool&) = delete;
        atomic_bool& operator=(const atomic_bool&) = delete;
        atomic_bool& operator=(const atomic_bool&) volatile = delete;
        bool operator=(bool) volatile;
    } atomic_bool;
}

Volatile is transitive. Thus, you cannot call a non-volatile member function from a volatile object. On the other hand, calling a volatile member function from a non-volatile object is allowed.

So, is there any implementation difference between the volatile and non-volatile member functions in the atomic classes? In other words, is there any need for the non-volatile overload?

2

There are 2 answers

2
0xbadf00d On

First, it sounds redundant to make a volatile std::atomic. Actually, I can imagine an useful situation. Assuming we have an fixed device (memory-)address we want to operate on. Due to the fact that the std::atomic_xxx classes as well as the std::atomic<> template class sizes should be same size as their corresponding built-in-types, you might want to handle both: Performing atomic operations with control over memory ordering and make sure that the access to our atomic-object is never optimized. Thus, we could declare something like:

std::atomic<long> volatile* vmem_first4 = reinterpret_cast<std::atomic<long> volatile*>(0xxB8000);
4
templatetypedef On

I think that the volatile overloads exist for efficiency reasons. Volatile reads and writes are inherently more expensive than non-volatile reads and writes in C++0x, since the memory model puts some stringent requirements that prevent caching of values of volatile variables. If all the functions were only marked volatile, then the code couldn't necessarily make certain optimizations that would otherwise improve performance. Having the distinction allows the compiler to optimize non-volatile reads and writes when possible while degrading gracefully when volatile reads and writes are required.