1

It is well known that std::shared_ptr is not thread safe. So it is easy to find a lot of crashing code samples in the web with simple programs to illustrate disadvantages of std::shared_ptr. But I can't find any for the boost::atomic_shared_ptr Does this mean that I can use boost::atomic_shared_ptr without any fear it can crash my app in the multithreaded environment? (Of course the boost::atomic_shared_ptr can contain bugs. So I want to know - it is safe "by design" or not.)

Evg
  • 25,259
  • 5
  • 41
  • 83
  • 1
    You mean the [`boost::atomic_shared_pointer`](https://www.boost.org/doc/libs/1_75_0/libs/smart_ptr/doc/html/smart_ptr.html#atomic_shared_ptr) that was standardized as [`std::atomic`](https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic2)? What did you find when you read the documentation? – Useless Feb 15 '21 at 09:48
  • 1
    Yes, the `atomic_shred_ptr` provides strong thread safety for reading and modifying the pointer itself. Related information [here](https://stackoverflow.com/a/65615682/13782669) – alex_noname Feb 16 '21 at 07:42

2 Answers2

2

You can use shared_pointer with atomic_load/atomic_store anyways ever since C++11, even though it is superseded in c++20:

https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic

I can't find a lot of examples myself, though I used it here: How to use boost::atomic_store with shared_ptr<T> and shared_ptr<const T>?, and here https://github.com/sehe/msghub/blob/std-over-boost/src/msghub.cpp#L25

Regardless of all this, none of this is going to make your code safe, because you'll still be sharing the objects pointed to by ths shared pointer, which makes your code every bit as prone to Data Races as before.

You still need to add proper synchronization to your code.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Does this mean that the memory management layer is absolutely safe in boost::atomic_shared_ptr and its c++20 analog (by design) ? Actually I am looking for a memory management system for a code fragments that are allowed to be slow but must be safe. – Mikhail Krasnorutsky Feb 15 '21 at 17:18
  • I specifically said: "none of this is going to make your code safe" - and explained why. There is no silver bullet. [Though programming in a pur functional language could get you some length, which suits certain domains and algorithms, but C++ is popular for a reason] – sehe Feb 15 '21 at 18:34
  • 1
    Depending on what exactly you mean with "the memory management layer" you could argue it is "absolutely safe" when used correctly. But that's not a statement that adds much value IMO. – sehe Feb 15 '21 at 18:35
0

std::atomicstd::shared_ptr<> is thread-safe but ONLY for reading and writing the value of the pointer itself. It's use does not make all uses of shared_ptr thread-safe.

Consider this:

// using a single writer of data pointed to by foo for simplicity.

// contract: only set values from main thread (for example)
//           set values, then call apply_changes when all your parameters
//           are correct.
struct Foo
{
    void set_valueA(int x);
    //...  
    void set_valueZ(int x);

    void apply_changes();   
};   

// using global data for silplicity, tis could be a member of some class,
// or in any stable memory location in an application.       

// More than one thread may reallocate or reset this object, hence the std::atomic. 
std::atomic<std::shared_ptr<Foo>> global_ptr;   

void some_function()
{
    // Set or reset  global_ptr attributes

    if (!global_ptr)
        return;

    global_ptr->set_valueA(42);    // a bad_alloc exception could 
                                   // happen here

    // what happens if global_ptr is set or reset
    // by another thread in between these two statements?

    global_ptr->set_valueB(17);    // a bad_alloc exception may occur here.
    global_ptr->apply_changes();   // undefined behavior as per contract of Foo 
                                   // may happen here.
}

// for this reason, proper usage would be...

void some_function()
{
    // Set or reset  global_ptr attributes

    // since global_ptr is atomic, it is guaranteed that this line will not crash
    // and will always return a well-formed shared_ptr. If fact, that's the
    // one and only guarantee given by std::atomic<std::shared_ptr<>>.
    if (std::shared_ptr<Foo> local_copy = global_ptr)
    {
        // Note that local_copy is guaranteed to point to the same object until 
        // end of scope. 

        local_copy->set_valueA(42);
        local_copy->set_valueB(17);
        local_copy->apply_changes();

        // if global_ptr has changed, then memory will be released when 
        //  exiting scope. 
    }
}

So, imho, some basic precautions must still be taken when using atomic shared pointers, either for read or write operations on the pointed to data.

Michaël Roy
  • 6,338
  • 1
  • 15
  • 19