Atomic Shared Pointer (std::atomic<std::shared_ptr<>)

Safe: Control Block Modification

#include <iostream>
#include <memory>
#include <thread>

int main()
{
    auto p = std::make_shared<int>(42);
    auto threadfunc = [p](){                           // <-- copy
        while (true)
            auto local_p = p;                          // <-- copy
    };
    std::thread t1(threadfunc);
    std::thread t2(threadfunc);
    t1.join();
    t2.join();
    return 0;
}

Sideways: Synchronizing With Thread Startup (1)

#include <iostream>
#include <memory>
#include <thread>

int main()
{
    auto p = std::make_shared<int>(42);
    auto threadfunc = [p](){
        while (true)
            auto local_p = p;
    };
    std::thread t1(threadfunc);
    std::thread t2(threadfunc);

    std::cout << p.use_count() << std::endl;           // <-- moving target but safe

    t1.join();
    t2.join();
    return 0;
}

Sideways: Synchronizing With Thread Startup (2)

#include <iostream>
#include <memory>
#include <thread>
#include <future>

int main()
{
    std::promise<void> started_promise;
    auto started_future = started_promise.get_future();

    auto p = std::make_shared<int>(42);
    auto threadfunc = [p, &started_promise](){
        started_promise.set_value();                   // <-- kick waiter loose
        while (true)
            auto local_p = p;
    };
    std::thread t(threadfunc);

    started_future.get();                              // <-- wait until kicked
    std::cout << p.use_count() << std::endl;

    t.join();
    return 0;
}

Unsafe: Assignment To Shared Pointer

#include <memory>
#include <thread>

int main()
{
    auto p = std::make_shared<int>(42);
    auto threadfunc = [&p](){
        while (true)
            p = std::make_shared<int>(666);            // <-- **NOT** threadsafe!
    };
    std::thread t1(threadfunc);
    std::thread t2(threadfunc);
    t1.join();
    t2.join();
    return 0;
}

Solution: std::atomic_store<> Specialization

  • Deprecated in C++20

#include <memory>
#include <thread>

int main()
{
    auto p = std::make_shared<int>(42);
    auto threadfunc = [&p](){
        while (true)
            std::atomic_store(&p, std::make_shared<int>(666));  // <-- ugly
    };
    std::thread t1(threadfunc);
    std::thread t2(threadfunc);
    t1.join();
    t2.join();
    return 0;
}

Solution: std::atomic<std::shared_ptr>

  • Don’t want manually enable thread safety by using global function

  • Better: want the object itself to be thread safe

  • Drawback: no std::make_atomic_shared()

#include <memory>
#include <thread>

int main()
{
    std::atomic<std::shared_ptr<int>> p = std::make_shared<int>(42);
    auto threadfunc = [&p](){
        while (true)
            p = std::make_shared<int>(666);
    };
    std::thread t1(threadfunc);
    std::thread t2(threadfunc);
    t1.join();
    t2.join();
    return 0;
}