WebKit 裡物件的生命週期很多都是由引用計數 (reference count) 控制的,今天有空看了一下,發現幾個巧妙之處:

1) 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(this); 則是為了避免不必要的 virtual table 產生,觀察 ~RefCountBase() 是個 non-virtual function 可以印證:

~RefCountedBase()
{
    ASSERT(m_deletionHasBegun);
    ASSERT(!m_adoptionIsRequired);
}

2) 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 引用。

3) Reference counting 一般的做法都是把計數初始值設定成 0,而 WebKit 為了避免每次創建都需要做的 0 -> 1 以及銷毀時要做的 1 -> 0 的記憶體存取,則是把計數值初始值設定成 1 的,然後強迫要用 PassRefPtr 來管理新產生的物件。

protected:
    RefCountedBase() : m_refCount(1)

4) 為了效能,引用計數的增加及減少是預設不保證 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” 這篇文章是必備的知識。