Are C++ smart pointers lockfree?
Asked Answered
A

2

5

Are the following operations lockfree for std::unique_ptr and/or std::shared_ptr?

  1. Dereferencing, i.e. read(*myPtr) or myPtr->getSomething()
  2. Removing a reference, i.e. with std::move(myUniquePtr) or when a std::shared_ptr goes out of scope.

In my case, I am not concurrently accessing these pointers from multiple threads. I'm just curious if I can use them exclusively on a high-priority, lockfree thread. The objects managed by the pointers were allocated by the main thread prior to the high-priority callbacks and will not be deallocated until the callbacks cease.

Thanks!

Archenemy answered 8/4, 2014 at 3:36 Comment(5)
use these en.cppreference.com/w/cpp/memory/shared_ptr/atomicAblution
Note that move and going out of scooe are very different. move never changes the reference count.Shopper
@bryanchen those are for multiple threads one variable, which is not what the OP is talking about methinks.Shopper
@Yakk doesn't move remove the reference from a source unique_ptr and add it to a destination unique_ptr? I've seen std::remove_reference in errors related to moving a unique_ptr.Archenemy
@Archenemy sure, bit unique_ptr has no need to ever lock: it has no significant contention. For shared, you can move the reference to the dest without doing a +1 -1 mess... without messimg with the control block comtents of the source at all! The dest admittedly needs a .reset() of non-empty, so there is that.Shopper
N
3

All that the standard says is that for shared_ptr<> (20.7.2.2/4 "Class template shared_ptr"):

Changes in use_count() do not reflect modifications that can introduce data races

It doesn't say that those changes in use_count() have to be lock free. The standard permits a mutex to be used to prevent the data race.

unique_ptr<> has no promises to prevent data races (it's not intended to be thread safe on it's own).

Northington answered 8/4, 2014 at 3:56 Comment(3)
In practice, however... about the only one with any reason to lock is 'going out of scope'.Shopper
@mxdubois: I don't think there's any reason for the unique_ptr implementation to introduce a lock for dereferencing, but I'm not sure the standard forbids it. Similarly for the move operation.Northington
It isn't guaranteed to be lockfree, but there's no reason to use an implementation that synchronises at all.Resect
W
5

With a reasonable implementation, you can assume:

std::unique_ptr:

  • All operations on a std::unique_ptr are as lock-free as the corresponding operations on a raw pointer, because there is nothing special regarding concurrency.

std::shared_ptr:

  • All operations, that do not change the reference count, are as lock-free as the corresponding operations on a raw pointer. That includes the operations dereferencing and move construction.
  • std::move is lock-free, because it is only a cast to an rvalue-reference.
  • The destructor of a std::shared_ptr is at least as lock-free as std::atomic<std::size_t> (can be checked with the member function is_lock_free).
  • Move assignment depends on whether the std::shared_ptr on the left side has an associated managed object or not. If there is an associated managed object, it is as lock-free as the destructor. Otherwise it is as lock-free as a move constructor, because the reference count is not changed.
Wei answered 8/4, 2014 at 7:5 Comment(2)
I'm going to accept Burr's answer because I was mostly looking for what was guaranteed, but thanks for the extra info!Archenemy
std::unique_ptr isn't lock-free since the pointer itself isn't managed thread-safe. The operations on the reference-counter are lock-free since this is only a single atomic. The shared_ptr itself is never lock-free, but only its managed control block. An atomic<shared_ptr<>> could be lock-free, but today's implementations are all based on futexes.Repatriate
N
3

All that the standard says is that for shared_ptr<> (20.7.2.2/4 "Class template shared_ptr"):

Changes in use_count() do not reflect modifications that can introduce data races

It doesn't say that those changes in use_count() have to be lock free. The standard permits a mutex to be used to prevent the data race.

unique_ptr<> has no promises to prevent data races (it's not intended to be thread safe on it's own).

Northington answered 8/4, 2014 at 3:56 Comment(3)
In practice, however... about the only one with any reason to lock is 'going out of scope'.Shopper
@mxdubois: I don't think there's any reason for the unique_ptr implementation to introduce a lock for dereferencing, but I'm not sure the standard forbids it. Similarly for the move operation.Northington
It isn't guaranteed to be lockfree, but there's no reason to use an implementation that synchronises at all.Resect

© 2022 - 2024 — McMap. All rights reserved.