I have a block of code where std::atomic<std::weak_ptr<T>>
doesn’t behave the way I would have expected if the underlying weak pointer is expired:
std::atomic<std::weak_ptr<Widget>> ptrAtomicWidget = ...;
std::shared_ptr<Widget> ptrWidget = ptrAtomicWidget.load().lock();
while (ptrWidget == nullptr)
{
ptrWidget = std::make_shared<Widget>();
std::weak_ptr<Widget> ptrExpected; // <--- nullptr
std::weak_ptr<Widget> ptrDesired = ptrWidget;
// Problem Version: Causes an infinite loop when ptrExpected is expired
if (!ptrAtomicWidget.compare_exchange_weak(ptrExpected, ptrDesired))
{
ptrWidget = ptrExpected().lock();
}
// Potential Repair Version: *seems* to work (could alternately move declaration of ptrExpected above while loop)
if (!ptrAtomicWidget.compare_exchange_weak(ptrExpected, ptrDesired)
&& ptrExpected.expired()
&& !ptrAtomicWidget.compare_exchange_weak(ptrExpected, ptrDesired))
{
ptrWidget = ptrExpected().lock();
}
}
The problem I’m having involves the “seems to work” part of the “potential repair version” of the loop body. The repair requires two different expired weak_ptrs to reliably compare equal to each other during the compare_exchange. std::weak_ptr
doesn’t have an equality operator so consequently its documentation is mute on this. None of the documentation I can find for the specialization of std::atomic<>
, for example at CPPReference, describes the behavior of compare-exchange when the pointer is expired. I don’t know whether it just happens to work with my particular compiler or whether the C++ standard guarantees it. Does someone know if it is guaranteed to work by the standard?