WebKit 裡物件的生命週期很多都是由引用計數 (reference count) 控制的,今天有空看了一下,發現幾個巧妙之處:
- RefCounted 是個 template class,並且繼承自 RefCountedBase:
template<typename T> class RefCounted : public RefCountedBase {
WTF_MAKE_NONCOPYABLE(RefCounted); WTF_MAKE_FAST_ALLOCATED;
public:
void deref()
{
if (derefBase())
delete static_cast<T*>(this);
}
protected:
RefCounted() { }
~RefCounted()
{
}
};
繼承 RefCountedBase 的技巧 (template hoisting) 是為了避免 template 實例化產生的 code bloating;而 destructor 裡的 delete static_cast<T*>(this); 則是為了避免不必要的 virtual table 產生,觀察 ~RefCountBase() 是個 non-virtual function 可以印證:
~RefCountedBase()
{
ASSERT(m_deletionHasBegun);
ASSERT(!m_adoptionIsRequired);
}
- RefCountedBase 的設計相當輕量化, release 版本裡實際只會佔用一個 integer 的空間,而 debug 版裡則多了幾個輔助除錯的欄位:
#ifndef NDEBUG
bool m_deletionHasBegun;
bool m_adoptionIsRequired;
ThreadRestrictionVerifier m_verifier;
#endif
m_deletionHasBegun 是用來確保當物件當參考計數已經小於 1 之後是不能再做 ref/deref 的動作的;m_adoptionIsRequired 是用來確保 adoptRef() 一定要被呼叫到,也就是 new 出來的物件一定要先用 PassRefPtr 來管理;而 m_verifier 則是用來確認物件不會同時被兩個以上的 thread 引用。
- Reference counting 一般的做法都是把計數初始值設定成 0,而 WebKit 為了避免每次創建都需要做的 0 -> 1 以及銷毀時要做的 1 -> 0 的記憶體存取,則是把計數值初始值設定成 1 的,然後強迫要用 PassRefPtr 來管理新產生的物件。
protected:
RefCountedBase() : m_refCount(1)
- 為了效能,引用計數的增加及減少是預設不保證 thread-safe。
void ref()
{
#ifndef NDEBUG
// Start thread verification as soon as the ref count gets to 2. This
// heuristic reflects the fact that items are often created on one thread
// and then given to another thread to be used.
// FIXME: Make this restriction tigher. Especially as we move to more
// common methods for sharing items across threads like CrossThreadCopier.h
// We should be able to add a "detachFromThread" method to make this explicit.
if (m_refCount == 1)
m_verifier.setShared(true);
#endif
// If this assert fires, it either indicates a thread safety issue or
// that the verification needs to change. See ThreadRestrictionVerifier for
// the different modes.
ASSERT(m_verifier.isSafeToUse());
ASSERT(!m_deletionHasBegun);
ASSERT(!m_adoptionIsRequired);
++m_refCount;
}
所以 RefCounted 的機制要能運作正常,正確地使用 RefPtr 及 PassRefPtr 是很重要的,也就是如果你需要對 WebKit 做一定程度的修改的話,“RefPtr and PassRefPtr Basics” 這篇文章是必備的知識。