0

The following snippet runs fine:

#include <memory>
#include <cassert>
int main()
{
    auto ptr1 = std::make_shared<int>(10);
    assert(ptr1.use_count() == 1);

    auto ptr2 = std::move(ptr1);
    assert(ptr1 == nullptr);

    auto ptr3 = static_cast<std::shared_ptr<int>&&>(ptr2);
    assert(ptr2 == nullptr);
    assert(ptr3.use_count() == 1);
}

It looks std::move does several operations, one of them is reseting the moved shared pointer somehow, so is this thread safe? For instance if i have something like:

void ThreadLogic()
{
    if (sharedPtr != nullptr)
    {
        DoSomething(std::move(sharedPtr));
    }
    else
    {
        DoSomethingElse();
    }
}

Is that std::move(sharedPtr) atomic or should I protect the check (the critical section) there by some other means?

user17732522
  • 53,019
  • 2
  • 56
  • 105
muaz
  • 550
  • 9
  • 15
  • Are you assuming that `sharedPtr` can be accessed from multiple threads? That is not the case. You should not share `std::shared_ptr` instances, just have `std::shared_ptr` instances owning the same object in multiple threads. Then the reference counting and destruction of the managed object is thread-safe. – user17732522 Jun 23 '22 at 22:54
  • @user17732522 yes that is the assumption, i just want to know how safe `move` implementation is. – muaz Jun 23 '22 at 22:59
  • `std::move` doesn't move anything, it's just a syntactic sugar hiding a `static_cast` to rvalue reference. It doesn't by itself do anything at runtime, so it's meaningless to ask whether it's thread-safe. `auto ptr2 = std::move(ptr1);` calls the move constructor of `shared_ptr` - that's the one that "does several operations"; not `std::move`. If you just write `std::move(ptr1);` on its own, you can confirm that `ptr1` is intact afterwards. – Igor Tandetnik Jun 23 '22 at 23:13
  • @IgorTandetnik so implicitly the question asks whether that move ctor a thread safe or not. It is a matter of yes/no question and why. – muaz Jun 23 '22 at 23:18
  • 3
    No, member functions of `std::shared_ptr` are generally not thread-safe. This includes the move constructor. If you move from `ptr1` while another thread accesses `ptr1`, you have a data race. – Igor Tandetnik Jun 23 '22 at 23:19
  • *"It looks std::move does several operations, one of them is reseting the moved shared pointer"*. Refer to [std::move() doesn't actually move anything](https://stackoverflow.com/a/27026280/12002570). – Jason Jun 24 '22 at 09:16
  • Please stop telling people that 'move' does not move anything, like it does not do anything, the question is: is it atomic, and it looks the answer is no. – muaz Jun 24 '22 at 10:09

1 Answers1

1

You have a misconception about what std::move does. In fact std::move does nothing. It's just a compile time mechanism with the meaning: I no longer need this named value.

This then causes the compiler to use the value in different ways, like call a move constructor/assignment instead of copy constructor/assignment. But the std::move itself generates no code so nothing to be affected by threads.

The real question you should be asking is whether the move constructor of shared_ptr is thread safe and the answer is: No.

But the point of the shared_ptr is not to share the shared_ptr but to share what it points too. Do not pass a shared_ptr by reference to threads, instead pass it by value so each thread gets it own shared_ptr. Then it is free to move that around at will without problems.

The shared_ptr protects the lifetime of the thing it points to. The reference count the shared_ptr uses is thread safe but nothing else. It only manages the lifetime of the pointed to objects in a thread safe way so it doesn't get destroyed as long as any shared_ptr has hold of the object.

Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42
  • Nice answer, move does nothing but it cause the compiler to call move ctor, this answer is applicable for each wrapper, and no I am not misconcepting it, i am asking whether the compiler will add measures to guarantee thread safety, it looks the answer is No. – muaz Jun 24 '22 at 10:17
  • As said, `std::move` generates 0 code. It's just forces the compiler to consider move semantic as an option where it couldn't when something has a name. The thread safety has to come from whatever the compiler calls with or without the `std::move`. – Goswin von Brederlow Jun 24 '22 at 11:21