chromium的base源码阅读之scoped_refptr

简介

我们之前对C++标准库的智能指针有了一定了解,今天我们来聊一聊Chromium中base的智能指针-scoped_refptr,该智能指针同样是采用引用计数的方式来控制指针的创建和析构。接下来我们看下源码来解读下,源码在base下的memory/ref_counted.h下

base类


namespace subtle {

class BASE_EXPORT RefCountedBase {
 public:
  bool HasOneRef() const { return ref_count_ == 1; }

 protected:
  RefCountedBase();
  ~RefCountedBase();

  void AddRef() const;

  // Returns true if the object should self-delete.
  bool Release() const;

 private:
  mutable int ref_count_;
#ifndef NDEBUG
  mutable bool in_dtor_;
#endif

  DFAKE_MUTEX(add_release_);

  DISALLOW_COPY_AND_ASSIGN(RefCountedBase);
};

class BASE_EXPORT RefCountedThreadSafeBase {
 public:
  bool HasOneRef() const;

 protected:
  RefCountedThreadSafeBase();
  ~RefCountedThreadSafeBase();

  void AddRef() const;

  // Returns true if the object should self-delete.
  bool Release() const;

 private:
  mutable AtomicRefCount ref_count_;
#ifndef NDEBUG
  mutable bool in_dtor_;
#endif

  DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase);
};

}  // namespace subtle

看代码中有两个base类(RefCountedBase,RefCountedThreadSafeBase)看名字其实可以知道一个是线程安全的,另一个就是普通的。

RefCountedBase

两个类长得很像,我们先来看这个普通的base类(RefCountedBase),它的主要成员就是一个ref_count_,这个就是整个智能指针实现的关键,引用计数的值,在构造函数里初始化为0,在AddRef自增,Release里自减,如下代码:

void RefCountedBase::AddRef() const {
  // TODO(maruel): Add back once it doesn't assert 500 times/sec.
  // Current thread books the critical section "AddRelease" without release it.
  // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
#ifndef NDEBUG
  DCHECK(!in_dtor_);
#endif
  ++ref_count_;
}

bool RefCountedBase::Release() const {
  // TODO(maruel): Add back once it doesn't assert 500 times/sec.
  // Current thread books the critical section "AddRelease" without release it.
  // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
#ifndef NDEBUG
  DCHECK(!in_dtor_);
#endif
  if (--ref_count_ == 0) {
#ifndef NDEBUG
    in_dtor_ = true;
#endif
    return true;
  }
  return false;
}

其中Release在引用计数变为0时返回true,这个我们后边讲。

RefCountedThreadSafeBase

我们再来看这个线程安全的base类和上边那个有什么不同之处,首先既然是线程安全的类,那么他肯定可以保证他引用计数的线程安全。首先声明时使用:

AtomicRefCount ref_count_

我们看下AtomicRefCount是个啥,转到声明处:

typedef subtle::Atomic32 AtomicRefCount;
typedef int32 Atomic32;

最后发现也只是int32,这是在32位编译器上,如果编译64位那就是int64_t了,所以只是平台相关的整型。那是怎么来保证线程安全的呢,我们继续往下看,看下递增和递减时使用:

void RefCountedThreadSafeBase::AddRef() const {
#ifndef NDEBUG
  DCHECK(!in_dtor_);
#endif
  AtomicRefCountInc(&ref_count_);
}

bool RefCountedThreadSafeBase::Release() const {
#ifndef NDEBUG
  DCHECK(!in_dtor_);
  DCHECK(!AtomicRefCountIsZero(&ref_count_));
#endif
  if (!AtomicRefCountDec(&ref_count_)) {
#ifndef NDEBUG
    in_dtor_ = true;
#endif
    return true;
  }
  return false;
}

主要是AtomicRefCountInc和AtomicRefCountDec函数来控制,先来看下AtomicRefCountInc函数的定义:

// Increment a reference count by 1.
inline void AtomicRefCountInc(volatile AtomicRefCount *ptr) {
  base::AtomicRefCountIncN(ptr, 1);
}

// Increment a reference count by "increment", which must exceed 0.
inline void AtomicRefCountIncN(volatile AtomicRefCount *ptr,
                               AtomicRefCount increment) {
  subtle::NoBarrier_AtomicIncrement(ptr, increment);
}

inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
                                          Atomic32 increment) {
  return Barrier_AtomicIncrement(ptr, increment);
}

inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
                                        Atomic32 increment) {
  return InterlockedExchangeAdd(
      reinterpret_cast<volatile LONG*>(ptr),
      static_cast<LONG>(increment)) + increment;
}

我们看到AtomicRefCountInc->AtomicRefCountIncN->NoBarrier_AtomicIncrement->Barrier_AtomicIncrement这样的调用链,使用volatile类型的指针,可以避免指向的值被编译器优化,关于volatile的用法可以参照:volatile的解读
最终是由InterlockedExchangeAdd函数来维持递增时的线程安全,InterlockedExchangeAdd时windows的api(因为我这边用windows来看的源码,只是说下windows的),这个函数用于对一个32位数值执行加法的原子操作,具体的底层原理我们这里就不说了。这里还有一个地方需要注意的NoBarrier_AtomicIncrement调用了Barrier_AtomicIncrement函数,是非内存屏障的函数调用了内存屏障的函数(关于内存屏障我在volatile的解读也有说),因为对于递增和递减这样的函数其实我们不需要要求他是内存屏障的,所以我们会调用NoBarrier_AtomicIncrement,然后在windows下要保证递增时原子性的,即使用InterlockedExchangeAdd函数,这个函数又是内存屏障形式的,所以有了这样的调用。递减函数同理,只是判断下值是否是为0。
继续看下HasOneRef函数,用来判断是不是引用计数的值为1:

bool RefCountedThreadSafeBase::HasOneRef() const {
  return AtomicRefCountIsOne(
      &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_);
}

使用AtomicRefCountIsOne函数来判断,我们看下实现:

inline bool AtomicRefCountIsOne(volatile AtomicRefCount *ptr) {
  bool res = (subtle::Acquire_Load(ptr) == 1);
  if (res) {
    ANNOTATE_HAPPENS_AFTER(ptr);
  }
  return res;
}

inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
  Atomic32 value = *ptr;
  return value;
}

同样使用volatile指针来取ref_count_的值,简单解释下这个流程,由于我们上边讲的递增和递减时首先更新ref_count_的地址的指向的值,这里可能有点绕,可能在想ref_count_的地址的指向的值不就是ref_count_,其实不是的,因为ref_count_的地址的指向的值是内存中的值,而ref_count_这个可能缓存中的值,或者寄存器中的值,然后我们再来理解这个函数实现,是不是就简单多了,其实就是从内存中取指来判断是不是为1。

我们会用到的类

上边我们讲了base类,base类是源码里边需要用到的类,即为了做一些实现用到的基础类,接下来我们来看下我们如果想要使用scoped_refptr这个智能指针,我们的类需要继承的一些类。

RefCounted

首先是简单RefCounted类,我们看下他的实现:

template <class T>
class RefCounted : public subtle::RefCountedBase {
 public:
  RefCounted() {}

  void AddRef() const {
    subtle::RefCountedBase::AddRef();
  }

  void Release() const {
    if (subtle::RefCountedBase::Release()) {
      delete static_cast<const T*>(this);
    }
  }

 protected:
  ~RefCounted() {}

 private:
  DISALLOW_COPY_AND_ASSIGN(RefCounted<T>);
};

这个函数比较简单,继承了RefCountedBase类,重新实现AddRef和Release函数,在Release里如果引用计数为0时,就析构掉自己。
然后看下这个函数的使用方式,看下注释里说的:

// A base class for reference counted classes.  Otherwise, known as a cheap
// knock-off of WebKit's RefCounted<T> class.  To use this guy just extend your
// class from it like so:
//
//   class MyFoo : public base::RefCounted<MyFoo> {
//    ...
//    private:
//     friend class base::RefCounted<MyFoo>;
//     ~MyFoo();
//   };
//
// You should always make your destructor private, to avoid any code deleting
// the object accidently while there are references to it.

注释里有说明我们如何使用这个类,因为这个类其实是个模板类,我们继承他的同时,模板参数指定为我们自己的类。然后需要把我们自己的类的析构函数设置为私有的,因为我们使用了智能指针,析构应该是交由智能指针,不能在外部析构。然后因为析构函数是私有的了,这里的做法是将父类设置成我们类的友元,这样父类便能够析构我们类的对象了。

RefCountedThreadSafe

接下来我们看我们需要继承使用线程安全的父类RefCountedThreadSafe,再这之前我们先看下DefaultRefCountedThreadSafeTraits这个类的源码:

// Default traits for RefCountedThreadSafe<T>.  Deletes the object when its ref
// count reaches 0.  Overload to delete it on a different thread etc.
template<typename T>
struct DefaultRefCountedThreadSafeTraits {
  static void Destruct(const T* x) {
    // Delete through RefCountedThreadSafe to make child classes only need to be
    // friend with RefCountedThreadSafe instead of this struct, which is an
    // implementation detail.
    RefCountedThreadSafe<T,
                         DefaultRefCountedThreadSafeTraits>::DeleteInternal(x);
  }
};

首先这个类是个模板类,这个类的主要作用便是Destruct函数,析构传入模板参数的类的对象,通过调用RefCountedThreadSafe的DeleteInternal函数来实现。我们先看下RefCountedThreadSafe实现再细讲下:

template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T> >
class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase {
 public:
  RefCountedThreadSafe() {}

  void AddRef() const {
    subtle::RefCountedThreadSafeBase::AddRef();
  }

  void Release() const {
    if (subtle::RefCountedThreadSafeBase::Release()) {
      Traits::Destruct(static_cast<const T*>(this));
    }
  }

 protected:
  ~RefCountedThreadSafe() {}

 private:
  friend struct DefaultRefCountedThreadSafeTraits<T>;
  static void DeleteInternal(const T* x) { delete x; }

  DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe);
};

这个和RefCounted很像,同样我们使用的时候需要设置这个类是我们自己类的父类及友元类,自己的析构函数是私有的。但是这个类特殊的地方在于使用Traits类来做析构,同时我们看默认的Traits类析构是还是使用RefCountedThreadSafe类的函数,这样看是不是多此一举了,其实不是我们仔细看下DefaultRefCountedThreadSafeTraits的注释有说在另外的线程析构时会用到你自己定义的Traits类,这就说明这个用法,即有些时候我们需要析构对象在别的线程,然后这就给我们提供很好的方式来解决。

RefCountedData

我们首先看下源码:

//
// A thread-safe wrapper for some piece of data so we can place other
// things in scoped_refptrs<>.
//
template<typename T>
class RefCountedData
    : public base::RefCountedThreadSafe< base::RefCountedData<T> > {
 public:
  RefCountedData() : data() {}
  RefCountedData(const T& in_value) : data(in_value) {}

  T data;

 private:
  friend class base::RefCountedThreadSafe<base::RefCountedData<T> >;
  ~RefCountedData() {}
};

RefCountedData继承了RefCountedThreadSafe类,看起来和我们自定义类有几分相似,然定义了一个data用来存放数据,其实个人认为这个类是可以直接给我们用的,而不是需要去继承的,我们使用自定义类的对象封装在scoped_refptr时,可以使用RefCountedThreadSafe或者RefCounted,如果我们类似想要封装原始类型(int,bool等)的智能指针,就可以使用RefCountedData< int >或者RefCountedData< bool >,当然也不是必须如此,可以看下base里边怎么使用的:

class DeletionHelper : public base::RefCountedThreadSafe<DeletionHelper> {
 public:
  explicit DeletionHelper(
      const scoped_refptr<base::RefCountedData<bool> >& deleted_flag)
      : deleted_flag_(deleted_flag) {
  }

 private:
  friend class base::RefCountedThreadSafe<DeletionHelper>;
  virtual ~DeletionHelper() { deleted_flag_->data = true; }

  const scoped_refptr<base::RefCountedData<bool> > deleted_flag_;
  DISALLOW_COPY_AND_ASSIGN(DeletionHelper);
};

这个其实使用RefCountedData这个类,就是类似封装了bool的智能指针。

scoped_refptr

接下来开始我们的重头戏scoped_refptr,首先我们把代码粘出来,然后在仔细分析下他的内容及用法

template <class T>
class scoped_refptr {
 public:
  typedef T element_type;

  scoped_refptr() : ptr_(NULL) {
  }

  scoped_refptr(T* p) : ptr_(p) {
    if (ptr_)
      ptr_->AddRef();
  }

  scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
    if (ptr_)
      ptr_->AddRef();
  }

  template <typename U>
  scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {
    if (ptr_)
      ptr_->AddRef();
  }

  ~scoped_refptr() {
    if (ptr_)
      ptr_->Release();
  }

  T* get() const { return ptr_; }
  operator T*() const { return ptr_; }
  T* operator->() const {
    assert(ptr_ != NULL);
    return ptr_;
  }

  scoped_refptr<T>& operator=(T* p) {
    // AddRef first so that self assignment should work
    if (p)
      p->AddRef();
    T* old_ptr = ptr_;
    ptr_ = p;
    if (old_ptr)
      old_ptr->Release();
    return *this;
  }

  scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
    return *this = r.ptr_;
  }

  template <typename U>
  scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
    return *this = r.get();
  }

  void swap(T** pp) {
    T* p = ptr_;
    ptr_ = *pp;
    *pp = p;
  }

  void swap(scoped_refptr<T>& r) {
    swap(&r.ptr_);
  }

 protected:
  T* ptr_;
};

代码很少,应该也比较好理解,scoped_refptr是一个模板类,自然模板参数就是我们自定义的类,或者我们使用的RefCountedData类,成员ptr_就是指向我们自定义类的对象。

  • 默认构造函数初始化ptr_为空。
  • T*参数的构造函数,参数赋值给ptr_,同时调用ptr_的AddRef函数,上边也说了我们的类会继承RefCountedThreadSafe和RefCounted,这些类里边已经帮我们管理了引用计数,定义了AddRef和Release函数。
  • 拷贝构造函数中获得ptr_的值,然后增加引用计数
  • 在析构函数中递减引用计数
  • 重载箭头函数
  • 重载赋值运算符,将原来的ptr_递减引用计数,将要赋值的ptr_递增引用计数,即ptr1 = ptr2时,因为ptr1是要将之前封装的对象的引用计数减1,因为他不再拥有这个对象了,然后因为ptr1和ptr2同时拥有一个对象,这个引用计数加1。

实例

基本上所有的类我们都讲了一遍,这里我们总结下,如果我们想要使用scoped_refptr这个智能指针,要么直接使用RefCountedData来封装我们的数据,要么就是我们自定义类,继承RefCounted或者RefCountedThreadSafe(需要线程安全),然后scoped_refptr的模板 参数是我们的自定义类或者RefCountedData,然后就使用scoped_refptr来进行我们的目的。我们看下注释里的例子:

//   class MyFoo : public RefCounted<MyFoo> {
//    ...
//   };
//
//   void some_function() {
//     scoped_refptr<MyFoo> foo = new MyFoo();
//     foo->Method(param);
//     // |foo| is released when this function returns
//   }
//
//   void some_other_function() {
//     scoped_refptr<MyFoo> foo = new MyFoo();
//     ...
//     foo = NULL;  // explicitly releases |foo|
//     ...
//     if (foo)
//       foo->Method(param);
//   }

MyFoo 是我们的自定义类,scoped_refptr对象调用MyFoo类的函数。

//     scoped_refptr<MyFoo> a = new MyFoo();
//     scoped_refptr<MyFoo> b;
//
//     b.swap(a);

置换两个指针的值

//     scoped_refptr<MyFoo> a = new MyFoo();
//     scoped_refptr<MyFoo> b;
//
//     b = a;

两个智能指针指向相同的资源

本篇文章到这里就讲完了,其实这个源码是较老版本的base了,其实原理其实都是一样,一方面为了自己学习,一方面记录下来,和大家交流下。scoped_refptr其实是侵入式的std::shared_ptr。如果看较旧一点的代码我们发现base库里有scoped_ptr,这其实就是std::unique_ptr,新版中已经废弃了,我说这个是避免大家搞混了,欢迎交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章