android智能指針sp和wp

普通指針的使用隱患和智能指針的解決辦法

普通指針在使用過程中的三個隱患:

  1. 指針沒有初始化。
  2. 指針沒有釋放。
  3. 釋放後沒有置空。

智能指針的解決辦法:

  1. 構造函數中初始化。
  2. 使用計數器,計數器爲0時釋放。
  3. 將指針封裝在智能指針中,析構時釋放。

標準庫中的智能指針爲shared_ptr,而android Binder相關代碼使用wp,sp作爲智能指針。

android中的智能指針

計數器由object自身持有

計數器並非由智能指針擁有(這是我之前一直弄錯的地方)。如果計數器由智能指針擁有,那麼當有多個智能指針都由該object初始化的時候,它們的計數值不能共享,也就無法確定該object究竟被引用了多少次。

解決辦法就是讓計數器由object自身持有,這樣只要在初始化智能指針的時候更新object持有的計數器,計數值就自然而然地被所有智能指針共享了。每個智能指針都可以從object的計數器中知道該object還剩多少個引用,從而決定是否釋放。

android中定義了統一的父類“RefBase”和“LightRefBase”,所有有計數要求的類型都繼承於這兩個類。

這是sp和wp的實現方法,暫不清楚stl中是怎麼實現智能指針的。

另外,sp中重載了->操作符,sp指針->的效果等價於原指針->的效果

弱指針wp的構造

弱指針是爲了避免智能指針引用中的“死鎖”而產生的,“死鎖”現象即兩個類的成員變量中均有指向對方的智能指針,這樣就會導致這兩個類的智能指針引用計數永遠都不爲零。

弱引用有兩個特點:

  1. 當強引用爲0時,無論弱引用是多少,對象都會被delete(android中有例外?)。
  2. 弱指針必須先升級成強指針,才能訪問它所指的對象。

wp的參數爲指針的構造函數如下:

//frameworks\rs\cpp\util\RefBase.h
template<typename T>
wp<T>::wp(T* other)
    : m_ptr(other)
{
    if (other) m_refs = other->createWeak(this);
}

構造函數把指針賦給自己的成員m_ptr,然後以自身作爲參數調用了createWeak函數,createWeak函數屬於前面提到的父類RefBase:

//android-7.1.1_r1\system\core\libutils\RefBase.cpp
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

createWeak將wp指針作爲id參數調用了mRefs的incWeak函數(從之後的代碼來看,id僅是用來調試用的,這裏可以忽略),並將mRef返回。mRef將被賦值給wp的m_refs。
mRef的類型是weakref_impl,而wp的m_refs成員的類型是weakref_type,weakref_impl繼承weakref_type,從名字就可以看到前者是後者的實現類,可以把這兩者當作相同類型看待。
weakref_impl非常重要,截取weakref_impl的定義如下:

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    std::atomic<int32_t>    mStrong;
    std::atomic<int32_t>    mWeak;
    RefBase* const          mBase;
    std::atomic<int32_t>    mFlags;

#if !DEBUG_REFS

    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
    {
    }
...
}

其中的mStrong和mWeak就是強弱引用計數,mBase是weakref_impl的從屬類RefBase的指針,之所以這樣設計是爲了便於釋放RefBase。從這裏可以看出weakref_impl即是統一父類RefBase的計數器。接下來看incWeak函數:

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);
    const int32_t c __unused = impl->mWeak.fetch_add(1,
            std::memory_order_relaxed);
    ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

第一行代碼用於強制類型轉換,將weakref_type強制轉化成weakref_impl,第二行是debug代碼,release版本中addWeakRef並沒有實現。第三行即是將weakref_impl的mWeak+1,這裏實現了計數器的自增
這裏的寫法非常奇特,是我以前沒見過的寫法,incWeak屬於weakref_type,其內部實現卻調用了子類weakref_Impl的函數,不知道爲什麼要這樣設計。(有點好奇這樣的寫法是怎麼通過編譯的)

各個類的關係如下:
在這裏插入圖片描述

強指針sp的構造

強指針的代碼比弱指針的代碼簡單,它的構造函數如下

//\frameworks\rs\cpp\util\StrongPointer.h
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
  {
    if (other) other->incStrong(this);
  }

直接調用了”統一父類“RefBase的incStrong函數。這裏注意和弱指針的不同,弱指針的incWeak在weakref_type類中,而強指針的incStrong在RefBase類中。繼續看incStrong:

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    
    refs->addStrongRef(id);
    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
    ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }

    int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
            std::memory_order_relaxed);
    // A decStrong() must still happen after us.
    ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
    refs->mBase->onFirstRef();
}

就是強弱引用計數自增。後面的代碼是處理第一次引用的情況的,可以忽略。

強弱指針的刪除

引用的自減由decStrong和decWeak完成,和前面一樣,decStrong屬於RefBase,而decWeak屬於weakref_type。
強指針的析構會調用decStrong先使強引用減一,再調用decWeak使弱引用減一(注意先後順序)。弱指針的析構會調用decWeak使弱引用減一。當強弱引用爲0時,就要判斷是否要釋放指針和計數器,即RefBase和weakref_impl。考慮以下幾種情況:

  1. 強引用爲0,弱引用不爲0。強指針的析構可能會導致該情況的發生。此時decStrong會釋放RefBase,但decWeak不會釋放weakref_impl,即wp的m_ref成員依然有效。
  2. 弱引用爲0,強引用爲0,且指針曾被強引用過。這種情況只可能發生在情況1之後,此時RefBase已被釋放,decWeak只需釋放weakref_impl即可。
  3. 弱引用爲0,強引用爲0,且指針未被強引用過。這種情況下,由於沒有強引用,也就沒有強指針的析構,decStrong不會被調用,故RefBase釋放的任務由decWeak完成。而RefBase的析構函數中,當弱引用爲0時,會釋放weakref_impl,這就完成了指針的計數器的釋放。

雖然weakref_impl是由RefBase創建的,但在情況1和情況2中,它卻是由自己完成自己的釋放(在weakref_type的incWeak中完成釋放,難道這就是分weakref_impl和weakref_type的理由?),只有在情況3中它才被RefBase釋放。這是由於強引用爲0時,弱引用不一定爲0,所以此時計數器不能隨着RefBase釋放。只有當強弱引用都爲0時才能釋放計數器。
理解了以上概念後,再看代碼就不難了:

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
#if PRINT_REFS
    ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) {
        std::atomic_thread_fence(std::memory_order_acquire);
        refs->mBase->onLastStrongRef(id);
        int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
            // Since mStrong had been incremented, the destructor did not
            // delete refs.
        }
    }
    // Note that even with only strong reference operations, the thread
    // deallocating this may not be the same as the thread deallocating refs.
    // That's OK: all accesses to this happen before its deletion here,
    // and all accesses to refs happen before its deletion in the final decWeak.
    // The destructor can safely access mRefs because either it's deleting
    // mRefs itself, or it's running entirely before the final mWeak decrement.
    refs->decWeak(id);
}

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);
    const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    if (c != 1) return;
    atomic_thread_fence(std::memory_order_acquire);

    int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
    if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
        // This is the regular lifetime case. The object is destroyed
        // when the last strong reference goes away. Since weakref_impl
        // outlive the object, it is not destroyed in the dtor, and
        // we'll have to do it here.
        if (impl->mStrong.load(std::memory_order_relaxed)
                == INITIAL_STRONG_VALUE) {
            // Special case: we never had a strong reference, so we need to
            // destroy the object now.
            delete impl->mBase;
        } else {
            // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
            delete impl;
        }
    } else {
        // This is the OBJECT_LIFETIME_WEAK case. The last weak-reference
        // is gone, we can destroy the object.
        impl->mBase->onLastWeakRef(id);
        delete impl->mBase;
    }
}

參考資料:深入理解Android內核設計思想

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