caps
caps

Reputation: 1243

std::atomic_store and std::atomic_exchange do not exchange

According to en.cppreference.com, std::atomic_exchange and std::atomic_store are equivalent to a thread-safe std::swap. But that's not the behavior that I'm getting with g++ or clang++.

Problem live on coliru. (see below)

It prints this though:

std::atomic_store

a: 0x1ed2c30    0
b: 0x1ed2c50    1

a: 0x1ed2c50    1
b: 0x1ed2c50    1

std::atomic_exchange

a: 0x1ed2c50    0
b: 0x1ed2c30    1

a: 0x1ed2c30    1
b: 0x1ed2c30    1

Why is this? Am I doing something wrong? Have I misread the documentation?

Code Listing

#include <iostream>
#include <memory>

int main()
{
    {
        std::cout << "std::atomic_store\n\n";
        auto a = std::make_shared<int>(0);
        auto b = std::make_shared<int>(1);

        std::cout
        << "a: " << a.get() << '\t' << *a << '\n'
        << "b: " << b.get() << '\t' << *b << '\n' << std::endl;

        std::atomic_store(&a, b);

        std::cout
        << "a: " << a.get() << '\t' << *a << '\n'
        << "b: " << b.get() << '\t' << *b << '\n' << std::endl;
    }
    {
        std::cout << "std::atomic_exchange\n\n";
        auto a = std::make_shared<int>(0);
        auto b = std::make_shared<int>(1);

        std::cout
        << "a: " << a.get() << '\t' << *a << '\n'
        << "b: " << b.get() << '\t' << *b << '\n' << std::endl;

        std::atomic_exchange(&a, b);

        std::cout
        << "a: " << a.get() << '\t' << *a << '\n'
        << "b: " << b.get() << '\t' << *b << '\n' << std::endl;
    }
}

Upvotes: 3

Views: 3719

Answers (4)

T.C.
T.C.

Reputation: 137315

That description is somewhat misleading.

It says, e.g.,

template<class T>
void atomic_store( std::shared_ptr<T>* p,
                   std::shared_ptr<T> r );

"effectively" does p->swap(r). Which is true as far as it gets (and is actually what the standard says, too).

But, r is a function argument passed by value, and so is destroyed before the function returns. It doesn't affect anything in the caller.

Upvotes: 11

galinette
galinette

Reputation: 9292

std::atomic_exchange does not swap a and b, it sets a to b and returns the previous value of a.

You could do :

b = std::atomic_exchange(&a, b);

This would work as you expect (exchange the pointers of a and b) but this is not thread safe : if multiple threads access the object b at the same time, this is undefined behavior. You cannot do better since a shared pointer is a complex struct containing typically two data members (two pointers). As atomic lock-free exchange requires hardware support, this only works with basic types (integers, and pointers).

Upvotes: 4

caps
caps

Reputation: 1243

It looks like the closest thing to the behavior I want is this:

    std::atomic_store(
        &b,
        std::atomic_exchange(
            &a,
            b
        )
    );

However, this is not strictly atomic. a and b could point to the same address for a fraction of a second.

In my case, that behavior is acceptable as long as they both point to valid, well-defined objects at all times.

Upvotes: 0

SergeyA
SergeyA

Reputation: 62573

It has been puzzling me for ages why 'compare-and-swap' has swap in it. It does not swap anything. It sets the value if check passess, and returns the current value if the check fails. So by no means atomic_exchange is any equivalent to std::swap.

Instead, it's a check-and-set pattern, so I always prefer CAS to mean compare-and-set.

Upvotes: 1

Related Questions