libc++stl中weak_ptr是如何实现的 weak_ptr是如何导致shared_ptr中相关资源延迟释放的
_LIBCPP_STD_VER = 17去掉:_LIBCPP_SHARED_PTR_TRIVIAL_ABI_LIBCPP_HIDE_FROM_ABI_LIBCPP_NODEBUG_LIBCPP_CONSTEXPR_NOEXCEPT
template<class T>class weak_ptr {public:using element_type = std::remove_extent_t<T>;private:element_type* __ptr_;__shared_weak_count* __cntrl_;public:weak_ptr();weak_ptr(shared_ptr<T> const& r);weak_ptr(weak_ptr const& r);weak_ptr(weak_ptr&& r);~weak_ptr();weak_ptr& operator=(...);voidswap(weak_ptr& r);voidreset();longuse_count()const{return __cntrl_ ? __cntrl_->use_count() : 0;}boolexpired()const{return __cntrl_==nullptr|| __cntrl_->use_count()==0;}shared_ptr<T> lock()const;};
weak_ptr+-------------------+| __ptr_ | -----> Object(T)+-------------------+| __cntrl_ | -----> ControlBlock+-------------------+
template <class _Tp>inline weak_ptr<_Tp>::weak_ptr(weak_ptr const& __r) _NOEXCEPT : __ptr_(__r.__ptr_), __cntrl_(__r.__cntrl_) {if (__cntrl_)__cntrl_->__add_weak();}
template <class _Tp>template <class _Yp, __enable_if_t<__compatible_with<_Yp, _Tp>::value, int> >inline weak_ptr<_Tp>::weak_ptr(shared_ptr<_Yp> const& __r) _NOEXCEPT : __ptr_(__r.__ptr_), __cntrl_(__r.__cntrl_) {if (__cntrl_)__cntrl_->__add_weak();}
通过shared ptr直接构造,此时控制块为__shared_ptr_pointer 通过make shared接口创建的shared ptr,此时控制块为 __shared_ptr_emplace
template<class _Tp>weak_ptr<_Tp>::~weak_ptr(){if (__cntrl_)__cntrl_->__release_weak();}
~shared_ptr(){if (__cntrl_)__cntrl_->__release_shared();}
__release_sharedvoid __release_shared() _NOEXCEPT {if (__shared_count::__release_shared())__release_weak();}
bool __release_shared() _NOEXCEPT {if (__libcpp_atomic_refcount_decrement(__shared_owners_) == -1) {__on_zero_shared();return true;}return false;}
__on_zero_shared();__shared_ptr_pointer<_Tp, _Dp, _Alloc>::__on_zero_shared() _NOEXCEPT{__data_.first().second()(__data_.first().first());__data_.first().second().~_Dp();}
__data_.first().second()__data_.first().first()virtual void __on_zero_shared() _NOEXCEPT {#if _LIBCPP_STD_VER > 17using _TpAlloc = typename __allocator_traits_rebind<_Alloc, _Tp>::type;_TpAlloc __tmp(*__get_alloc());allocator_traits<_TpAlloc>::destroy(__tmp, __get_elem());#else__get_elem()->~_Tp();#endif}
void __shared_weak_count::__release_weak() noexcept {// NOTE: The acquire load here is an optimization of the very// common case where a shared pointer is being destructed while// having no other contended references.//// BENEFIT: We avoid expensive atomic stores like XADD and STREX// in a common case. Those instructions are slow and do nasty// things to caches.//// IS THIS SAFE? Yes. During weak destruction, if we see that we// are the last reference, we know that no-one else is accessing// us. If someone were accessing us, then they would be doing so// while the last shared / weak_ptr was being destructed, and// that's undefined anyway.//// If we see anything other than a 0, then we have possible// contention, and need to use an atomicrmw primitive.// The same arguments don't apply for increment, where it is legal// (though inadvisable) to share shared_ptr references between// threads, and have them all get copied at once. The argument// also doesn't apply for __release_shared, because an outstanding// weak_ptr::lock() could read / modify the shared count.if (__libcpp_atomic_load(&__shared_weak_owners_, _AO_Acquire) == 0) {// no need to do this store, because we are about// to destroy everything.//__libcpp_atomic_store(&__shared_weak_owners_, -1, _AO_Release);__on_zero_shared_weak();} else if (__libcpp_atomic_refcount_decrement(__shared_weak_owners_) == -1)__on_zero_shared_weak();}
这段 __release_weak() 是 libc++ 里 weak_ptr 生命周期管理的“最终清理逻辑”之一,它的目标一句话概括:
当 weak_ptr 被释放时,如果它是最后一个 weak 引用,则销毁整个 control block。
但它的实现非常“反直觉优化”,关键在于:先偷看(load),再决定要不要做昂贵的原子减法(CAS/RMW)。
最终上述接口会调用__on_zero_shared_weak这个函数会销毁控制块
夜雨聆风