shared ptr & weak ptr
1 主要代码
1.1 count
_Mutex_base
using __gnu_cxx::__default_lock_policy;
using __gnu_cxx::_Lock_policy;
using __gnu_cxx::_S_atomic;
using __gnu_cxx::_S_mutex;
using __gnu_cxx::_S_single;
// Empty helper class except when the template argument is _S_mutex.
template <_Lock_policy _Lp>
class _Mutex_base {
protected:
// The atomic policy uses fully-fenced builtins, single doesn't care.
enum {
_S_need_barriers = 0 };
};
template <>
class _Mutex_base<_S_mutex> : public __gnu_cxx::__mutex {
protected:
// This policy is used when atomic builtins are not available.
// The replacement atomic operations might not have the necessary
// memory barriers.
enum {
_S_need_barriers = 1 };
};
_Lock_policy
相关参见备注2.3.
_Sp_counted_base
template <_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base : public _Mutex_base<_Lp> {
public:
_Sp_counted_base() noexcept : _M_use_count(1), _M_weak_count(1) {
}
virtual ~_Sp_counted_base() noexcept {
}
// Called when _M_use_count drops to zero, to release the resources
// managed by *this.
virtual void _M_dispose() noexcept = 0;
// Called when _M_weak_count drops to zero.
virtual void _M_destroy() noexcept {
delete this; }
virtual void* _M_get_deleter(const std::type_info&) noexcept = 0;
void _M_add_ref_copy() {
__gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }
void _M_add_ref_lock() {
if (!_M_add_ref_lock_nothrow()) __throw_bad_weak_ptr();
}
bool _M_add_ref_lock_nothrow() noexcept;
void _M_release() noexcept {
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1) {
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);
_M_dispose();
// There must be a memory barrier between dispose() and destroy()
// to ensure that the effects of dispose() are observed in the
// thread that runs destroy().
// See http://gcc.gnu.org/ml/libstdc++/2005-11/msg00136.html
if (_Mutex_base<_Lp>::_S_need_barriers) {
__atomic_thread_fence(__ATOMIC_ACQ_REL);
}
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1) {
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
_M_destroy();
}
}
}
void _M_weak_add_ref() noexcept {
__gnu_cxx::__atomic_add_dispatch(&_M_weak_count, 1); }
void _M_weak_release() noexcept {
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count, -1) == 1) {
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
if (_Mutex_base<_Lp>::_S_need_barriers) {
// See _M_release(),
// destroy() must observe results of dispose()
__atomic_thread_fence(__ATOMIC_ACQ_REL);
}
_M_destroy();
}
}
long _M_get_use_count() const noexcept {
// No memory barrier is used here so there is no synchronization
// with other threads.
return __atomic_load_n(&_M_use_count, __ATOMIC_RELAXED);
}
private:
_Sp_counted_base(_Sp_counted_base const&) = delete;
_Sp_counted_base& operator=(_Sp_counted_base const&) = delete;
_Atomic_word _M_use_count; // #shared
_Atomic_word _M_weak_count; // #weak + (#shared != 0)
};
template <>
inline bool _Sp_counted_base<_S_single>::_M_add_ref_lock_nothrow() noexcept {
if (_M_use_count == 0) return false;
++_M_use_count;
return true;
}
template <>
inline bool _Sp_counted_base<_S_mutex>::_M_add_ref_lock_nothrow() noexcept {
__gnu_cxx::__scoped_lock sentry(*this);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, 1) == 0) {
_M_use_count = 0;
return false;
}
return true;
}
template <>
inline bool _Sp_counted_base<_S_atomic>::_M_add_ref_lock_nothrow() noexcept {
// Perform lock-free add-if-not-zero operation.
_Atomic_word __count = _M_get_use_count();
do {
if (__count == 0) return false;
// Replace the current counter value with the old value + 1, as
// long as it's not changed meanwhile.
} while (!__atomic_compare_exchange_n(&_M_use_count, &__count, __count + 1, true, __ATOMIC_ACQ_REL,
__ATOMIC_RELAXED));
return true;
}
template <>
inline void _Sp_counted_base<_S_single>::_M_add_ref_copy() {
++_M_use_count;
}
template <>
inline void _Sp_counted_base<_S_single>::_M_release() noexcept {
if (--_M_use_count == 0) {
_M_dispose();
if (--_M_weak_count == 0) _M_destroy();
}
}
template <>
inline void _Sp_counted_base<_S_single>::_M_weak_add_ref() noexcept {
++_M_weak_count;
}
template <>
inline void _Sp_counted_base<_S_single>::_M_weak_release() noexcept {
if (--_M_weak_count == 0) _M_destroy();
}
template <>
inline long _Sp_counted_base<_S_single>::_M_get_use_count() const noexcept {
return _M_use_count;
}
__exchange_and_add_dispatch
函数参见备注2.2.- 构造函数将两个成员变量
_M_use_count
、_M_weak_count
初始化为1.这里的逻辑是,当它创建的时候,自己本身就是一个引用计数。 _M_dispose
函数是纯虚函数,当_M_use_count
为0时,释放this持有的资源。_M_destroy
函数默认删除this,当_M_weak_count
为0时调用。_M_add_ref_copy
函数,对_M_use_count + 1
,是原子操作。_M_add_ref_lock
函数,主要逻辑仍然是_M_use_count + 1
。和_M_add_ref_copy
的区别是对不同_Lock_policy
有不同的实现,包含直接加、原子操作加、加锁。_M_release
函数,当_M_use_count-1=0
时,即_M_use_count
为0时,调用_M_dispose
。并当_M_use_count-1=0
,即_M_weak_count
为0时,调用_M_destroy
。_M_weak_add_ref
:对_M_weak_count + 1
,是原子操作。_M_weak_release
:只对_M_weak_count - 1
。- 以上两个函数都有
_Lock_policy=_M_single
时的重载形式。
_Sp_counted_ptr
// Counted ptr with no deleter or allocator support
template <typename _Ptr, _Lock_policy _Lp>
class _Sp_counted_ptr final : public _Sp_counted_base<_Lp> {
public:
explicit _Sp_counted_ptr(_Ptr __p) noexcept : _M_ptr(__p) {
}
virtual void _M_dispose() noexcept {
delete _M_ptr; }
virtual void _M_destroy() noexcept {
delete this; }
virtual void* _M_get_deleter(const std::type_info&) noexcept {
return nullptr; }
_Sp_counted_ptr(const _Sp_counted_ptr&) = delete;
_Sp_counted_ptr& operator=(const _Sp_counted_ptr&) = delete;
private:
_Ptr _M_ptr;
};
template <>
inline void _Sp_counted_ptr<nullptr_t, _S_single>::_M_dispose() noexcept {
}
template <>
inline void _Sp_counted_ptr<nullptr_t, _S_mutex>::_M_dispose() noexcept {
}
template <>
inline void _Sp_counted_ptr<nullptr_t, _S_atomic>::_M_dispose() noexcept {
}
不支持deleter和allocator的counted ptr。
_M_dispose
默认行为是delete_M_ptr
,_M_destroy
默认行为是delete this。- 对于
_Ptr=nullptr_t
时,不同_Lock_policy
的_M_dispose
的行为都是空的。
_Sp_ebo_helper
template <int _Nm, typename _Tp, bool __use_ebo = !__is_final(_Tp) && __is_empty(_Tp)>
struct _Sp_ebo_helper;
/// Specialization using EBO.
template <int _Nm, typename _Tp>
struct _Sp_ebo_helper<_Nm, _Tp, true> : private _Tp {
explicit _Sp_ebo_helper(const _Tp& __tp) : _Tp(__tp) {
}
explicit _Sp_ebo_helper(_Tp&& __tp) : _Tp(std::move(__tp)) {
}
static _Tp& _S_get(_Sp_ebo_helper& __eboh) {
return static_cast<_Tp&>(__eboh); }
};
/// Specialization not using EBO.
template <int _Nm, typename _Tp>
struct _Sp_ebo_helper<_Nm, _Tp, false> {
explicit _Sp_ebo_helper(const _Tp& __tp) : _M_tp(__tp) {
}
explicit _Sp_ebo_helper(_Tp&& __tp) : _M_tp(std::move(__tp)) {
}
static _Tp& _S_get(_Sp_ebo_helper& __eboh) {
return __eboh._M_tp; }
private:
_Tp _M_tp;
};
在类型_Tp
为final且empty的时候,使用ebo优化的类,即模板参数__use_ebo
为true的时候。
- 对于“空”类型,不存在私有变量,
_S_get
返回强转为_Tp
类型的入参_Sp_ebo_helper
。 - 对于“非空”类型,有一个
_Tp
类型的私有变量:_M_tp
。_S_get
返回这个变量。
_Sp_counted_deleter
// Support for custom deleter and/or allocator
template <typename _Ptr, typename _Deleter, typename _Alloc, _Lock_policy _Lp>
class _Sp_counted_deleter final : public _Sp_counted_base<_Lp> {
class _Impl : _Sp_ebo_helper<0, _Deleter>, _Sp_ebo_helper<1, _Alloc> {
typedef _Sp_ebo_helper<0, _Deleter> _Del_base;
typedef _Sp_ebo_helper<1, _Alloc> _Alloc_base;
public:
_Impl(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept
: _Del_base(std::move(__d)), _Alloc_base(__a), _M_ptr(__p) {
}
_Deleter& _M_del() noexcept {
return _Del_base::_S_get(*this); }
_Alloc& _M_alloc() noexcept {
return _Alloc_base::_S_get(*this); }
_Ptr _M_ptr;
};
public:
using __allocator_type = __alloc_rebind<_Alloc, _Sp_counted_deleter>;
// __d(__p) must not throw.
_Sp_counted_deleter(_Ptr __p, _Deleter __d) noexcept : _M_impl(__p, std::move(__d), _Alloc()) {
}
// __d(__p) must not throw.
_Sp_counted_deleter(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept : _M_impl(__p, std::move(__d), __a) {
}
~_Sp_counted_deleter() noexcept {
}
virtual void _M_dispose() noexcept {
_M_impl._M_del()(_M_impl._M_ptr); }
virtual void _M_destroy() noexcept {
__allocator_type __a(_M_impl._M_alloc());
__allocated_ptr<__allocator_type> __guard_ptr{
__a, this};
this->~_Sp_counted_deleter();
}
virtual void* _M_get_deleter(const type_info& __ti [[__gnu__::__unused__]]) noexcept {
#if __cpp_rtti
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2400. shared_ptr's get_deleter() should use addressof()
return __ti == typeid(_Deleter) ? std::__addressof(_M_impl._M_del()) : nullptr;
#else
return nullptr;
#endif
}
private:
_Impl _M_impl;
};
_Impl
继承自_Sp_ebo_helper<0, _Deleter>
作为_Del_base
、_Sp_ebo_helper<1, _Alloc>
作为_Alloc_base
。
_M_ptr
作为私有变量是被管理的对象的指针。_M_del
从_Del_base
获取一个_Deleter
变量。_M_alloc
从_Alloc_base
获取一个_Alloc
类型变量。
_Sp_counted_deleter
继承自_Sp_counted_base
。
- 唯一的私有变量是
_Impl
类型的。 - 构造函数主要是将传入的数据构造一个
_Impl
。 _M_dispose
函数对_Ptr
调用_M_del()
。_M_destroy
函数对this调用析构函数。_M_get_delter
,当传入的type_info
类型的__ti
和_Deleter
是同样的类型的时候,返回_M_del()
的地址,否则返回nullptr。
_Sp_counted_ptr_inplace
struct _Sp_make_shared_tag {
private:
template <typename _Tp, typename _Alloc, _Lock_policy _Lp>
friend class _Sp_counted_ptr_inplace;
static const type_info& _S_ti() noexcept _GLIBCXX_VISIBILITY(default) {
alignas(type_info) static constexpr char __tag[sizeof(type_info)] = {
};
return reinterpret_cast<const type_info&>(__tag);
}
static bool _S_eq(const type_info&) noexcept;
};
template <typename _Alloc>
struct _Sp_alloc_shared_tag {
const _Alloc& _M_a;
};
template <typename _Tp, typename _Alloc, _Lock_policy _Lp>
class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp> {
class _Impl : _Sp_ebo_helper<0, _Alloc> {
typedef _Sp_ebo_helper<0, _Alloc> _A_base;
public:
explicit _Impl(_Alloc __a) noexcept : _A_base(__a) {
}
_Alloc& _M_alloc() noexcept {
return _A_base::_S_get(*this); }
__gnu_cxx::__aligned_buffer<_Tp> _M_storage;
};
public:
using __allocator_type = __alloc_rebind<_Alloc, _Sp_counted_ptr_inplace>;
// Alloc parameter is not a reference so doesn't alias anything in __args
template <typename... _Args>
_Sp_counted_ptr_inplace(_Alloc __a, _Args&&... __args) : _M_impl(__a) {
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2070. allocate_shared should use allocator_traits<A>::construct
allocator_traits<_Alloc>::construct(__a, _M_ptr