Boost documentation describes shared pointer's behavior when accessing it from multiple threads simultaneously. Particularly they give some examples:
shared_ptr<int> p(new int(42));
//--- Example 1 ---
// thread A
shared_ptr<int> p2(p); // reads p
// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
//--- Example 2 ---
// thread A
p.reset(new int(1912)); // writes p
// thread B
p2.reset(); // OK, writes p2
//--- Example 3 ---
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
...
But they do not say (or I cannot see) what will happen if the same shared_ptr
object is written and read at the same time. Say:
shared_ptr<int> p(new int(42));
//--- My Example ---
// thread A
p.reset(new int(1912));
// thread B
shared_ptr<int> p1 = p;
So my question is whether the last example is OK or not?
NOTE: Boost's third example explains that it is not safe to read and write the same object in parallel. But in their example they assign p3
to p
, which is a copy of p3
. So it is not clear to me whether the safety of that example depends on the fact that p3
gets assigned to its copy or whether something else is unsafe.
std::shared_ptr is not thread safe. A shared pointer is a pair of two pointers, one to the object and one to a control block (holding the ref counter, links to weak pointers ...).
The ownership of an object can only be shared with another shared_ptr by copy constructing or copy assigning its value to another shared_ptr . Constructing a new shared_ptr using the raw underlying pointer owned by another shared_ptr leads to undefined behavior.
A std::shared_ptr consists of a control block and its resource. Yes, the control block is thread-safe; but no, the access to the resource is not thread-safe. That means, modifying the reference counter is an atomic operation and you have the guarantee that the resource will be deleted exactly once.
All the instances point to the same object, and share access to one "control block" that increments and decrements the reference count whenever a new shared_ptr is added, goes out of scope, or is reset. When the reference count reaches zero, the control block deletes the memory resource and itself.
But they does not say (or I cannot see) what will happen if the same shared_ptr object will be written and read at the same time.
Yes they do:
A
shared_ptr
instance can be "read" (accessed using only const operations) simultaneously by multiple threads. Differentshared_ptr
instances can be "written to" (accessed using mutable operations such asoperator=
orreset
) simultaneously by multiple threads (even when these instances are copies, and share the same reference count underneath.)Any other simultaneous accesses result in undefined behavior.
(emphasis mine)
Seems pretty clear to me.
So my question is whether the last example OK or not?
No, because what you are doing is not reading simultaneously from a single instance, nor writing to separate instances. You are simultaneously reading and writing a single instance. That's a data race, and undefined behaviour.
Now that we have C++11 (and 14/17), you may use the atomic shared pointer classes to atomically handle shared pointers in your threads.
http://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
Simply put, if you may end up modifying a shared pointer, you should use the atomic functions everywhere (all reads and all writes.) Then your threads should not crash because of the shared pointer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With