Search code examples
c++multithreadingc++11atomic

Nonlocking Way to Copy Atomics in Copy Constructor


I am writing a copy constructor for a data structure which needs to copy two std::atomic<T> members into a new object. While the process doesn't necessarily have to be atomic in my use-case, I would prefer to have the most correct solution possible.

I am aware that the copy constructor is explicitly deleted with std::atomic<T> so as to force users to use the atomic interface.

atomic(const atomic&) = delete;

What I am currently I am doing something like this:

SomeObject(const SomeObject& other): 
   _atomic1(other._atomic1.load()),            
   _atomic2(other._atomic2.load()) {
...
}

I do not believe this operation is atomic, nor do I know a way to make is so (without locks).

Is there a way to copy these values atomically (without locks)?


Solution

  • The only way is to make a trivially copyable struct S containing two Ts and use std::atomic<S>.

    Note that this only works if you've been using this S from the start - there is no way to atomically load two separate atomics without locks.

    So instead of:

    struct SomeObject {
        SomeObject(const SomeObject& other) : i(other.i.load()), j(other.j.load()) { }
        std::atomic<int> i, j;
    };
    

    Do this:

    struct SomeObject {
        SomeObject(const SomeObject& other) : data(other.data.load()) { }
        struct Data { int i, j; };
        std::atomic<Data> data;
    };
    

    Note that this might (probably will) still use locks internally. Use is_lock_free to check if it does.