// 引用计数器的同步方式, 单线程情形无需同步, 多线程下如支持原子操作则使用atomic, 否则退化为锁
enum _Lock_policy { _S_single, _S_mutex, _S_atomic };
static const _Lock_policy __default_lock_policy =
#ifndef __GTHREADS
_S_single;
#elif defined _GLIBCXX_HAVE_ATOMIC_LOCK_POLICY
_S_atomic;
#else
_S_mutex;
#endif
// _Lock_policy枚举将用来模板特化具体的_Mutex_base
// TODO 为什么atomic不需要使用barrier?
template <_Lock_policy _Lp>
class _Mutex_base {
protected:
// The atomic policy uses fully-fenced builtins, single doesn't care.
// TODO
enum { _S_need_barriers = 0 };
};
// TODO
// template <>
// class _Mutex_base<_S_mutex> : public __gnu_cxx::__mutex {
// protected:
// enum { _S_need_barriers = 1 };
// };
// 引用计数器的父类
// 这个基类的设计非常巧妙, 其目的是区分两种实现子类, 主要是区分由指针构造shared_ptr
// 和直接make_shared的实现, 直接make_shared将对象和control block一次性分配在同一块内存中,
// 而由构造器产生的shared_ptr只需要维护一个指针即可
// 同时这种封装将子类异构的模板封装了起来, 阻止了模板化扩散, 简化和解耦了程序结构, 对于__shared_count是隐式的
// 代价是虚函数的成本
template <_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base : public _Mutex_base<_Lp> {
public:
// TODO 为什么_M_weak_count要设置为1?
_Sp_counted_base() noexcept : _M_use_count(1), _M_weak_count(1) {}
virtual ~_Sp_counted_base() noexcept {}
// 这里居然是直接返回void* (TODO 怎么保证类型安全?)
virtual void* _M_get_deleter(const std::type_info&) noexcept = 0;
// 每次拷贝shared_ptr, _M_use_count+1
// 注意这里形成了acq-rel顺序 (TODO 为什么不是relax?)
void _M_add_ref_copy() { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }
// 注意这两个操作区分开来了, 其目的应该是所有的shared_ptr释放后,
// 应当立即释放维护的对象, 但不释放计数器本身, 当所有的weak_ptr释放后,
// 再释放计数器本身
virtual void _M_dispose() noexcept = 0;
virtual void _M_destroy() noexcept { delete this; }
// TODO
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();
}
}
}
// 返回引用计数, 原子计数
// 注意这里提到的`no synchronization with other threads`
// 首先relaxed会产生synchronization吗, 不会, 不仅不构成happens-before关系, 也不构成synchronization-with关系
// 这里不使用barrier是有意为之, TODO
// decrementing the shared_ptr counters requires acquire -
// release synchronization with the destructor
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:
// 引用计数
_Atomic_word _M_use_count;
};
// 引用计数器
template <_Lock_policy _Lp>
class __shared_count {
private:
_Sp_counted_base<_Lp>* _M_pi;
};
// shared_ptr会直接public继承__shared_ptr
// 单独提出一个__shared_ptr的目的是封装LP这个模板参数, 其中LP会传递给__shared_count
// __shared_ptr持有一个元素指针和引用计数器
template <typename _Tp, _Lock_policy _Lp = __default_lock_policy>
class __shared_ptr {
public:
// (1) T=>T (2) T[]=>T
using element_type = typename remove_extent<_Tp>::type;
private:
// 元素指针
// 注意类型不是T, 是element_type, 为了支持数组
element_type* ptr;
// 引用计数器, 也就是所谓的control block
__shared_count<_Lp> _M_refcount;
};
// 摘自标准提案对于shared_ptr的定义和api标准, 对于理解shared_ptr非常重要
// A shared_ptr can share ownership of an object while storing a pointer to another object. This feature can be used to
// point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), the
// dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count reaches
// zero.
// A shared_ptr may also own no objects, in which case it is called empty (an empty shared_ptr may have a non-null
// stored pointer if the aliasing constructor was used to create it).
// All specializations of shared_ptr meet the requirements of CopyConstructible, CopyAssignable, and LessThanComparable
// and are contextually convertible to bool.
// All member functions (including copy constructor and copy assignment) can be called by multiple threads on different
// instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of
// the same object. If multiple threads of execution access the same shared_ptr without synchronization and any of those
// accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of
// atomic functions can be used to prevent the data race.
template <typename T>
class shared_ptr {
// Constructs a shared_ptr with no managed object, i.e. empty shared_ptr
// constexpr shared_ptr() noexcept;
// constexpr shared_ptr( std::nullptr_t ) noexcept;
// Constructs a shared_ptr with ptr as the pointer to the managed object.
// Y* must be convertible to T*.
// template< class Y >
// explicit shared_ptr( Y* ptr );
// Constructs a shared_ptr with ptr as the pointer to the managed object.
// Uses the specified deleter d as the deleter. The expression d(ptr) must be well formed, have well-defined behavior
// and not throw any exceptions. The construction of d and of the stored deleter from d must not throw exceptions.
// These constructors additionally do not participate in overload resolution if the expression d(ptr) is not
// well-formed, or if std::is_move_constructible<D>::value is false.
// template< class Y, class Deleter >
// shared_ptr( Y* ptr, Deleter d );
// Constructs a shared_ptr which shares ownership of the object managed by r. If r manages no object, *this manages no
// object too. The template overload doesn't participate in overload resolution if Y* is not compatible with T*
// shared_ptr( const shared_ptr& r ) noexcept;
// template< class Y >
// shared_ptr( const shared_ptr<Y>& r ) noexcept;
// Move-constructs a shared_ptr from r. After the construction, *this contains a copy of the previous state of r, r is
// empty and its stored pointer is null. The template overload doesn't participate in overload resolution if Y* is not
// compatible with (since C++17) T*.
// shared_ptr( shared_ptr&& r ) noexcept;
// template< class Y >
// shared_ptr(shared_ptr<Y>&& r ) noexcept;
// operator= assigns the shared_ptr
// reset replaces the managed object
// swap swaps the managed objects
// get returns the stored pointer
// operator*
// operator-> dereferences the stored pointer
// use_count returns the number of shared_ptr objects referring to the same managed object
// operator bool checks if the stored pointer is not null
private:
};
#include <memory>
int main() {
std::shared_ptr<int> p();
//
}
shared_ptr源码分析
于 2020-11-08 14:56:01 首次发布