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?
use_count()
based on document open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0521r0.html. "Note: When multiple threads can affect the return value of use_count(), the result should be treated as approximate. In particular, use_count() == 1 does not imply that accesses through a previously destroyed shared_ptr have in any sense completed. — end note" – Icsunique()
does not do its job as telling ashared_ptr
is unique or not. And also imply that there is flaw and race onuse_count()
in multi-threading context. For example, if you haveT a
and initialise 2shared_ptr
to manageda
in 2 different threads; you can get the the result in both thread asuse_count() == 1
. I do not know how this happen, but it happen to occur while I'm working on several projects. As the result, I have to manageweak_ptr
before creatingshared_ptr
as an workaround – Icsuse_count()
can be unreliable due to a few reasons; weak ptr, and on some systems relaxed memory ordering (not all systems support that). An answer that covers that, and any possible read/modify/write problems on the data the shared ptr itself points to, would be useful. None of the answers there seem to cover it. Hence the bounty. – Bluhmcow_ptr
but this design has a bunch of inherent flaws. Like any kind of use of the originalshared_ptr
and possible derivedweak_ptr
. So I really don't understand what exactly you want to achieve. – Press