7

I was trying to find the answer for some time but I failed.

Lets assume that we have a shared_ptr created from one thread. Then we pass this shared_ptr to another 2 threads (using some queue for example). So from this moment there are 2 copies of the original shared_ptr, pointing to the same raw pointer. Both owner threads will take their copies of this shared_ptr from the queue. Then they will pass it to another thread or will destroy it.

Question is - is it safe? Will the raw pointer destroyed correctly (there will be no race to reference counter?) enter image description here

user1332475
  • 115
  • 1
  • 6
  • Slight vagueness in your formulation: by “destroy”, do you mean calling `reset`, assigning a new pointee or letting the `shared_ptr` go out of scope? If so, those operations are fine. Other destructive operations probably aren’t. – Konrad Rudolph Jul 15 '12 at 13:26
  • I do not plan any reset() call. shared_ptr goes out of scope. – user1332475 Jul 15 '12 at 13:27

3 Answers3

9

The C++ standard has almost no guarantees regarding thread safety. The reference count of std::shared_ptr is the only exception: it’s guaranteed to behave as an atomically accessed variable. I believe this is codified in this phrase in §20.7.2.2/4:

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

boost::shared_ptr offers the same guarantees:

shared_ptr objects offer the same level of thread safety as built-in types. A shared_ptr instance can be "read" … simultaneously by multiple threads. Different shared_ptr instances can be "written to"… simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.)

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • OP is asking about `boost::shared_ptr` though. I expect the behaviour would be the same, but better be clear about it. – juanchopanza Jul 15 '12 at 12:31
  • @juan Good call, but the link you provided explicitly says that the operation that OP wants to use is safe. – Konrad Rudolph Jul 15 '12 at 12:34
  • 20.7.2.5 is also of interest here: "Concurrent access to a shared_ptr object from multiple threads does not introduce a data race if the access is done exclusively via the functions in this section and the instance is passed as their first argument." – marcinj Jul 15 '12 at 12:53
  • @luskan That’s what I referenced first, but then I noticed that the section only applies to very specific functions, and isn’t relevant here. – Konrad Rudolph Jul 15 '12 at 13:20
  • Why are you mentioning `use_count()`? – curiousguy Jul 26 '12 at 06:30
  • @curiousguy This was the only section that I could find which actually talks about concurrent access to `shared_ptr`, and it *does* (by implication) guarantee that OP’s actions are thread-safe. (I had hoped for a clearer, more direct formulation but no dice.) – Konrad Rudolph Jul 26 '12 at 06:56
  • 2
    No specific guarantee exists for `shared_ptr`. The guarantee applies to every standard class. – curiousguy Jul 26 '12 at 07:06
  • @curiousguy Nah. `shared_ptr` is guaranteed to have atomic reference counting, so that different threads can concurrently copy `shared_ptr` instances which point to the same underlying object. No other standard class has comparable guarantees – but then, no other class has comparable semantics anyway. Nevertheless, no other standard class description contains wording comparable to §20.7.2.2/4, explicitly mentioning safe cross-thread access. – Konrad Rudolph Jul 26 '12 at 08:33
  • 1
    "_shared_ptr is guaranteed to have atomic reference counting_" Nah. There is explicitly no guarantee that `shared_ptr` even uses a counter. "_No other standard class has comparable guarantees_" Nah. Every other C++ construct, both core and library, offers exactly the same guarantee: **the implementation shall not introduce a data race** (if there is none in the source code, of course). "_no other standard class description contains wording comparable to §20.7.2.2/4,_" not even `try_lock`? – curiousguy Jul 26 '12 at 08:43
5

The boost docs state:

Different shared_ptr instances can be "written to" (accessed using mutable operations such as operator= or reset) simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.)

(emphasis mine)

So the crux here is whether you copy the boost::shared_ptrs between threads or not. If you create copies (the "safe" way to use shared_ptrs) you don't have any worries about thread-safety. If however you pass the shared_ptr by reference or pointer, and hence are using the actual same shared_ptr in different threads, you would have to worry about thread-safety, as described in the docs.

Fraser
  • 74,704
  • 20
  • 238
  • 215
0

I would like to post my comment for the reference counting in the boost shared pointer in the multiple threads use cases. The comment is to answer the question that “is there any race condition in the boost shared pointer reference counting?”

My simple answer is “No” at least after boost 1.35 for most mainstream compiler. The boost implementation called “add_ref_copy” defined in the boost/detail/shared_count.hpp. This function will invoke the corresponding atomic function defined for individual compiler. For example, the windows version will call “BOOST_INTERLOCKED_INCREMENT” to increment the count in the atomic way (see details in detail\sp_counted_base_w32.hpp). And the Linux gcc for X86 will call atomic_increment(… ) (see details in detail\sp_counted_base_gcc_x86.hpp). Each individual compiler implemented the thread-safe mechanism to make sure the reference counting update in an efficient way. Some piece of code are even written in assembly.

Now there is a caveats in my simple answer. You really need to make sure your compiler is included in the boost’s blessed list for multiple thread-safe reference counting. If you are not sure you can define “BOOST_SP_USE_PTHREADS” which drives boost to use the pthread library to make reference count update atomically (by including boost/detail/sp_counted_base_pt.hpp for pthread solution).