So I have a simple cow_ptr
. It looks something like this:
template<class T, class Base=std::shared_ptr<T const>>
struct cow_ptr:private Base{
using Base::operator*;
using Base::operator->;
using Base::operator bool;
// etc
cow_ptr(std::shared_ptr<T> ptr):Base(ptr){}
// defaulted special member functions
template<class F>
decltype(auto) write(F&& f){
if (!unique()) self_clone();
Assert(unique());
return std::forward<F>(f)(const_cast<T&>(**this));
}
private:
void self_clone(){
if (!*this) return;
*this = std::make_shared<T>(**this);
Assert(unique());
}
};
this guarantees that it holds a non-const T
and ensures it is unique
when it .write([&](T&){})
s to it.
The c++17 deprecation of .unique()
seems to indicate this design is flawed.
I am guessing that if we start with a cow_ptr<int> ptr
with 1
in thread A, pass it to thread B, make it unique, modify it to 2
, pass ptr
it back and read it in thread A
we have generated a race condition.
How do I fix this? Can I simply add a memory barrier in write
? Which one? Or is the problem more fundamental?
Are symptoms less likely on x86 due to the x86 memory consistency going above and beyond what C++ demands?
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.
To implement copy-on-write, a smart pointer to the real content is used to encapsulate the object's value, and on each modification an object reference count is checked; if the object is referenced more than once, a copy of the content is created before modification.
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 ...).
Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.
The problem is that use_count()
may grow, even if you don't change shared_ptr
in this thread by other threads activity.
You may not touch the current shared_ptr
by other thread, but other instances of shared_ptr
or weak_ptr
may be copied/deleted. The use_count() == 1
seem to exclude concurrent shared_ptr
, but concurrent weak_ptr
are still possible, and may be .lock()
ed.
Ok, for your case, you hidden part of shared_ptr
interface, so you may have hidden weak_ptr
creation ability, so your case may be fine. Too bad that while this question waited for answer, .unique()
is removed in C++20. You still have .use_count()
though.
Or maybe just implement your own reference counting or take a more suitable base. Remember extra overhead for shared_ptr
, most notably, two atomic counters (use and weak) instead of one.
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