Android 重學(xué)系列 有趣的工具--智能指針與智能鎖

背景

如果遇到什么問題在這個地址下留言:http://www.lxweimin.com/p/2f0ecf6ca08c

在Android 的底層中,編寫大量的c/c++源碼。但是卻很少看到Android去調(diào)用delete去刪除對象的申請的內(nèi)存。而這其中,必定有一個東西去管理對象的生命周期。而這個擔(dān)起這個責(zé)任就是智能指針。

于此同時,還能看到Android底層調(diào)用了鎖之后,我們也沒看到相應(yīng)的解鎖方法。實(shí)際上這里面起作用的就是智能鎖,將鎖自動解開。

接下來,將會從智能鎖開始,聊聊這兩個相似的設(shè)計(jì)思路。

正文

智能鎖

讓我們先看看源碼,關(guān)于智能鎖其中一個例子。

void SurfaceFlinger::init() {
...
    Mutex::Autolock _l(mStateLock);
    // start the EventThread
    mEventThreadSource =
            std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs,
                                             true, "app");
    mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(),
                                                       [this]() { resyncWithRateLimit(); },
                                                       impl::EventThread::InterceptVSyncsCallback(),
                                                       "appEventThread");
    mSfEventThreadSource =
            std::make_unique<DispSyncSource>(&mPrimaryDispSync,
                                             SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");

    mSFEventThread =
            std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
                                                [this]() { resyncWithRateLimit(); },
                                                [this](nsecs_t timestamp) {
                                                    mInterceptor->saveVSyncEvent(timestamp);
                                                },
                                                "sfEventThread");
    mEventQueue->setEventThread(mSFEventThread.get());
    mVsyncModulator.setEventThread(mSFEventThread.get());

    // Get a RenderEngine for the given display / config (can't fail)
    getBE().mRenderEngine =
            RE::impl::RenderEngine::create(HAL_PIXEL_FORMAT_RGBA_8888,
                                           hasWideColorDisplay
                                                   ? RE::RenderEngine::WIDE_COLOR_SUPPORT
                                                   : 0);
   ...
    getBE().mHwc.reset(
            new HWComposer(std::make_unique<Hwc2::impl::Composer>(getBE().mHwcServiceName)));
    getBE().mHwc->registerCallback(this, getBE().mComposerSequenceId);

    processDisplayHotplugEventsLocked();
...

    getDefaultDisplayDeviceLocked()->makeCurrent();
....
}

我們能看到作為Android顯示系統(tǒng)的核心SurfaceFlinger在自己的進(jìn)程初始化的時候,會初始化我們MessageQueue(設(shè)計(jì)思路和Android應(yīng)用常用的Handler一致,一個消息隊(duì)列)。作為消息隊(duì)列,里面沒有使用線程安全的數(shù)據(jù)結(jié)構(gòu),自然需要上鎖,保護(hù)里面的數(shù)據(jù)結(jié)構(gòu)。

雖然在整個Android系統(tǒng)中,一般只會初始化一次SurfaceFlinger。但是這就Google工程還是做了保護(hù)措施,可見其思想就是自己的模塊要保證自己模塊中數(shù)據(jù)正確性。

我們能看到一個很有趣東西

Mutex::Autolock _l(mStateLock);

看到這個名字我們大致上能夠明白實(shí)際上,這必定是個鎖。但是我們卻沒看到哪里解鎖,哪里上鎖了。

智能鎖的思路

在看源碼之前,我們大致思考一下,如果我們嘗試著簡化,通過一個對象來管理整個上鎖解鎖流程應(yīng)該怎么做。這就能顧讓我們聯(lián)想到,這個過程我們可以讓鎖跟著對象的創(chuàng)建和銷毀的生命周期綁定起來。這樣就能很簡單做到鎖的自動處理。本質(zhì)上源碼也是這么做的。

大致上,我們需要往這個方向努力:

#include <strings.h>
#include <pthread.h>
#include "Define.h"
class Mutex {
private:
    pthread_mutex_t mMutex;

public:
    
    Mutex();


    ~Mutex();

};
#include "Mutex.h"
Mutex::Mutex() {
    pthread_mutex_init(&mMutex,NULL);
    pthread_mutex_lock(&mMutex);

    LOGE("lock");
}

Mutex::~Mutex() {
    pthread_mutex_unlock(&mMutex);
    pthread_mutex_destroy(&mMutex);
    LOGE("unlock");
}

調(diào)用:

    Mutex m;
    LOGE("do something");
image.png

這樣就能讓鎖和對象互相綁定。只要使用這種方式就能夠讓這個Mutex對象跟著方法內(nèi)的作用域跑,當(dāng)這個作用域跑完,就能自動解鎖。也就是這種思路,才會在Android一些底層看到這么調(diào)用。

但是這樣的思路,讓我們看到一種可行性,就是通過作用域來決定對象的釋放實(shí)際,從而決定鎖的釋放范圍。

可以看到源碼BufferQueueProducer(顯示系統(tǒng)中生產(chǎn)GraphBuffer的生產(chǎn)隊(duì)列)中

status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
                                            uint32_t width, uint32_t height, PixelFormat format,
                                            uint64_t usage, uint64_t* outBufferAge,
                                            FrameEventHistoryDelta* outTimestamps) {
    ATRACE_CALL();
    { // Autolock scope
        Mutex::Autolock lock(mCore->mMutex);
        mConsumerName = mCore->mConsumerName;

        if (mCore->mIsAbandoned) {
            BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
            return NO_INIT;
        }

        if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
            BQ_LOGE("dequeueBuffer: BufferQueue has no connected producer");
            return NO_INIT;
        }
    } // Autolock scope
....
}

可以靈活運(yùn)用著在方法創(chuàng)造一個作用域,讓智能鎖自動解鎖。

當(dāng)然這只是雛形,我們看看底層是做了什么。我們隨意抽一個Mutex的實(shí)現(xiàn)看看。

class Mutex {
public:
    Mutex() {
        pthread_mutex_init(&mMutex, NULL);
    }
    int lock() {
        return -pthread_mutex_lock(&mMutex);
    }
    void unlock() {
        pthread_mutex_unlock(&mMutex);
    }
    ~Mutex() {
        pthread_mutex_destroy(&mMutex);
    }

    // A simple class that locks a given mutex on construction
    // and unlocks it when it goes out of scope.
    class Autolock {
    public:
        Autolock(Mutex &mutex) : lock(&mutex) {
            lock->lock();
        }
        ~Autolock() {
            lock->unlock();
        }
    private:
        Mutex *lock;
    };

private:
    pthread_mutex_t mMutex;

    // Disallow copy and assign.
    Mutex(const Mutex&);
    Mutex& operator=(const Mutex&);
};

其思路和和我們的上面的思路很相似。但是能夠看到這里面Mutex是對pthread_mutex_t對象的存在進(jìn)行管理。Autolock則是讓這個對象在作用域能進(jìn)行上鎖解鎖管理。同時禁止了Mutex的拷貝構(gòu)造函數(shù),因?yàn)檫@樣會造成意料之外的鎖存在。

當(dāng)然,在/system/core/libutils/include/utils/下還有寫的更好的智能鎖。

能看到的是,讓某種行為綁定著對象的生命周期這種設(shè)計(jì)也是很常見。比如說Glide的每一次請求都是綁定在當(dāng)前Activity一個隱形的Fragment中。

接下來讓我們看看更加重要的智能智能。

智能指針

智能指針誕生的初衷來源,c++每一次new一個新的對象,因?yàn)閷ο蟮膬?nèi)存放到了堆中,導(dǎo)致我們必須想辦法清除掉堆中的內(nèi)存。當(dāng)我們忘記清除指針指向的內(nèi)存時候,就會產(chǎn)生野指針問題。然而每一次想辦法在合適的地方delete,將會讓整個工程復(fù)雜度提升了一個等級,也給每個程序員素質(zhì)的考驗(yàn)。

就以我之前寫B(tài)inder來說,大部分對象的操作都在內(nèi)核中,對binder_transaction這些事務(wù)來回拷貝,binder_ref在不斷的使用,想要找到一個合適的時機(jī)delete需要判斷的東西就十分多。

此時就誕生了智能指針。本質(zhì)上智能指針的思路,實(shí)際上和我們所說的引用計(jì)數(shù)本質(zhì)上是一個東西。

引用計(jì)數(shù)的思路就是,當(dāng)每一次引用,就對這個對象引用加一,當(dāng)超過作用域的之類調(diào)用一次析構(gòu)函數(shù)函數(shù),引用計(jì)數(shù)減一。知道引用計(jì)數(shù)一直減到1,說明再也沒有任何對象引用這個對象,就能安心的銷毀(初始引用次數(shù)為1)。

但是這種內(nèi)存計(jì)數(shù)方式有一個致命缺點(diǎn),就是當(dāng)兩個引用互相引用時候,會發(fā)生循環(huán)計(jì)數(shù)的問題。而智能指針的為了解決這個,誕生了強(qiáng)引用指針和弱引用指針。

了解這些之后,我們嘗試的思考怎么編寫智能指針才能解決上面那些問題。

智能指針設(shè)計(jì)思路

首先我們能夠確定是這必定是一個模版類,同時能夠?yàn)榱四軌蚬芾碇羔槪囟〞饕粋€指針進(jìn)來。

對于指針的計(jì)數(shù),我們能不能模仿智能鎖那樣,通過某個類包裝起來,讓某個全權(quán)管理這個類的真正的析構(gòu)時機(jī)以及計(jì)數(shù)呢?

就想我上面的說的,加入讓智能指針這個對象去管理計(jì)數(shù),就會出現(xiàn)一個致命的情況。當(dāng)智能指針1和智能指針2都引用同一個對象的時候,當(dāng)智能指針1的計(jì)數(shù)減為0,要去析構(gòu)的時候,智能指針2還持用著計(jì)數(shù),此時就會出現(xiàn)析構(gòu)失敗。

因此,我們不可能只用一個類去完成。這個過程中,至少需要兩個類,一個是用于計(jì)數(shù),另一個是用于管理指針。

接下來讓我們嘗試,編寫一個智能指針。首先創(chuàng)建一個LightRefBase基類,讓之后所有的類都去繼承這個類,讓類自己擁有計(jì)數(shù)的功能。接著再創(chuàng)建一個SmartPointer去管理指針。

LightRefBase

#ifndef SMARTTOOLS_LIGHTREFBASE_H
#define SMARTTOOLS_LIGHTREFBASE_H

#include <stdio.h>
#include <atomic>
#include <Define.h>

using namespace std;

template <class T>
class LightRefBase {
public:
    LightRefBase():mCount(0){

    }

    void incStrong(){
        mCount.fetch_add(1,memory_order_relaxed);
        LOGE("inc");
    }

    void decStrong(){
        LOGE("dec");
        if(mCount.fetch_sub(1,memory_order_release) == 1){
            atomic_thread_fence(memory_order_acquire);
            delete static_cast<const T*>(this);
            LOGE("delete");
        }
    }


private:
    mutable atomic<int> mCount;

};


#endif //SMARTTOOLS_LIGHTREFBASE_H

能看到的是,這里我創(chuàng)建了一個模版類,一個輕量級別的引用計(jì)數(shù)。當(dāng)調(diào)用incStrong,將會增加原子計(jì)數(shù)引用次數(shù)。當(dāng)調(diào)用decStrong,則會減少原子計(jì)數(shù)的引用次數(shù)。

這里稍微記錄一下atomic原子類操作。

    1. fetch_add 是指原子數(shù)字的增加
    1. fetch_sub 是指原子數(shù)字的減少

在這些原子模版類的操作中memory_order_release之類的內(nèi)存順序約束的操作。一共有如下操作:

enum memory_order {

memory_order_relaxed,

memory_order_consume,

memory_order_acquire,

memory_order_release,

memory_order_acq_rel,

memory_order_seq_cst

};

這6種memory_order可以分為3類。第一類,relaxed內(nèi)存松弛。第二類,sequential_consistency內(nèi)存一致序,第三類acquire-release獲取釋放一致序。

  • 對于內(nèi)存松弛(memory_order_relaxed),對于多線程沒有指令順序一致性要求。只是保證了在一個線程內(nèi)的原子操作保證了順序上處理。對于不同線程之間的執(zhí)行順序是隨意的。

  • 對于內(nèi)存一致序(memory_order_seq_cst),這是以犧牲優(yōu)化效率,來保證指令順序的一致性。相當(dāng)于不打開編譯器的優(yōu)化指令。按照正常指令執(zhí)行序執(zhí)行,多線程之間原子操作也會Synchronized-with,比如atomic::load()需要等待atomic::store()寫下元素才能讀取,同步過程。

  • 獲取釋放一致序,相當(dāng)于對relaxed的加強(qiáng)。relax序由于無法限制多線程間的排序,所以引入synchronized-with,但并不一定意味著,統(tǒng)一的操作順序。因?yàn)榭赡艹霈F(xiàn)當(dāng)出現(xiàn)讀寫操作時候,寫入操作完成但是還是在緩存,并沒有對應(yīng)的內(nèi)存,造成的異常。因此設(shè)計(jì)上誕生memory_order_release釋放鎖,memory_order_acquire上自旋鎖。memory_order_consume的多線程消費(fèi)者生產(chǎn)這些設(shè)計(jì)。

SmartPointer

在編寫SmartPointer的時候,記住要重寫幾個操作符號,因?yàn)橘x值,創(chuàng)造構(gòu)造SmartPointer的構(gòu)造函數(shù),我們都需要為對象的引用計(jì)數(shù)加一。當(dāng)超出了作用域,則把對象的引用減一。

#ifndef SMARTTOOLS_SMARTPOINTER_H
#define SMARTTOOLS_SMARTPOINTER_H

#include <stdio.h>

template <class  T>
class SmartPointer {
private:
    T* m_ptr;

public:
    SmartPointer():m_ptr(0){

    }


    SmartPointer(T *ptr){
        if(m_ptr){
            m_ptr = ptr;
            m_ptr->incStrong();
        }


    }


    ~SmartPointer(){
        if(m_ptr){
            m_ptr->decStrong();
        }
    }

    SmartPointer& operator = (T* other){
        if(other){
            m_ptr = other;
            other->incStrong();
        }

        return *this;
    }
};

#endif //SMARTTOOLS_SMARTPOINTER_H

接下來我們測試一下,在隨意一個方法,測試一下。

class TestLight :public LightRefBase<TestLight>{
public:
    TestLight(){

    }
};

SmartPointer<TestLight> sp(new TestLight());

看到了嗎,這個形式就和我們之前在Binder源碼分析時候,出現(xiàn)的引用計(jì)數(shù)時候聲明一個sp的方式一模一樣。
測試一下:


image.png

在一個方法內(nèi),確實(shí)完成了自動增加計(jì)數(shù)和銷毀了。這樣的設(shè)計(jì)和智能(自動鎖)鎖十分相似。都是靈活運(yùn)用了作用域和析構(gòu)函數(shù)之間的關(guān)系,對對象的引用計(jì)數(shù)做了內(nèi)存管理,來判斷是否繼續(xù)需要這個對象。

LightRefBase的缺點(diǎn)分析

正如上面所說的一樣,這么做雖然能做到簡單的計(jì)數(shù)統(tǒng)計(jì),似乎沒有什么問題。為什么Java虛擬機(jī)不采用引用計(jì)數(shù),而去使用GC引用鏈對對象進(jìn)行內(nèi)存掛歷。

我們來考慮這種情況。
當(dāng)A和B互相引用的時候。就造成這么一個問題


image.png

互相引用的時候,就造成一個特殊的情況。A中的B字段指向了B內(nèi)存會讓B本身無法析構(gòu),而B中的A字段指向了A的內(nèi)存也會讓A本身無法析構(gòu)。

這樣就出現(xiàn),我們常說的循環(huán)引用。為了處理這種問題,誕生了強(qiáng)弱指針的概念。

先來聊聊強(qiáng)指針sp(StrongPointer)。強(qiáng)指針和我上面寫的SmartPointer原理幾乎一致。目的是為了操作繼承了RefBase類中引用計(jì)數(shù)。

那么弱指針誕生就是為了處理循環(huán)引用的問題。如果換做是我們的話,我們該怎么處理這種異常呢。

我們回歸問題的本質(zhì),這種情況類似于死鎖,因?yàn)橄到y(tǒng)檢查到雙方都需要對方的資源,導(dǎo)致無法回收。那么就按照處理死鎖的辦法,打斷死鎖的資源的引用鏈就ok。這就是弱引用誕生的初衷。

強(qiáng)弱指針兩種指針,將會分別為自己計(jì)數(shù)。那么我們一定需要一個刪除引用的計(jì)數(shù)標(biāo)準(zhǔn),當(dāng)強(qiáng)引用了一個對象,當(dāng)強(qiáng)引用的計(jì)數(shù)減到了1,將會刪除里面的引用。

這樣就打斷了,引用計(jì)數(shù)的循環(huán)。但是,你們一定會想到,當(dāng)我們刪除A對象的引用,從B訪問A,不就會出現(xiàn)了訪問野指針/空指針的問題嗎?

因此弱指針又有一個規(guī)定,弱指針不能直接訪問對象,必須升級為強(qiáng)指針才能訪問對象。

有這幾個標(biāo)準(zhǔn)之后,我們嘗試編寫一下弱指針的源碼。我們拋棄原來的LightRefBase,創(chuàng)建一個更加泛用的RefBase基類。

#ifndef SMARTTOOLS_REFBASE_H
#define SMARTTOOLS_REFBASE_H

#include <stdio.h>
#include <StrongPointer.h>

#define COMPARE_WEAK(_op_)                                      \
inline bool operator _op_ (const sp<T>& o) const {              \
    return m_ptr _op_ o.m_ptr;                                  \
}                                                               \
inline bool operator _op_ (const T* o) const {                  \
    return m_ptr _op_ o;                                        \
}                                                               \
template<typename U>                                            \
inline bool operator _op_ (const sp<U>& o) const {              \
    return m_ptr _op_ o.m_ptr;                                  \
}                                                               \
template<typename U>                                            \
inline bool operator _op_ (const U* o) const {                  \
    return m_ptr _op_ o;                                        \
}

class RefBase {
public:
    void incStrong(const void* id) const;

    void decStrong(const void* id) const;

    int getStrongCount(const void* id) const;

    void forceStrong(const void* id) const;


    class weakref_type{
    public:
        RefBase* refBase() const;

        void incWeak(const void* id);

        void decWeak(const void* id);

        // acquires a strong reference if there is already one.
        bool attemptIncStrong(const void* id);

        bool attemptIncWeak(const void* id);

        int getWeakCount() const;

    };

    weakref_type* createWeak(const void* id) const;

    weakref_type* getWeakRef()const;

protected:
    RefBase();

    virtual ~RefBase();

    enum {
        OBJECT_LIFETIME_STRONG = 0x0000,
        OBJECT_LIFETIME_WEAK = 0x0001,
        OBJECT_LIFETIME_MASK = 0x0001
    };

    void extendObjectLifetime(int mode);

    enum {
        FIRST_INC_STRONG = 0x0001
    };

    virtual void onFirstRef();

    virtual void onLastStrongRef(const void* id);

    virtual bool onIncStrongAttempted(int flag, const void* id);

    virtual void onLastWeakRef(const void* id);

private:
    //為了讓weakref_type去訪問到refbase中的私有數(shù)據(jù)
    friend class weakref_type;
    //一個實(shí)現(xiàn)類
    class weakref_impl;
    RefBase(const RefBase& o);

    RefBase& operator =(const RefBase& o);

    weakref_impl *const mRefs;
};

//---------------------

template <typename T>
class wp{
public:
    typedef typename RefBase::weakref_type weakref_type;

    inline wp():m_ptr(0){}

    wp(T* other);
    //拷貝構(gòu)造函數(shù)
    wp(const wp<T>& other);

    explicit wp(const sp<T>& other);


    template <typename U> wp(U* other);

    template <typename U> wp(const sp<U>& other);

    template <typename U> wp(const wp<U>& other);


    ~wp();


    wp& operator = (T* other);
    wp& operator = (const wp<T>& other);
    wp& operator = (const sp<T>& other);

    template<typename U> wp& operator = (U* other);
    template<typename U> wp& operator = (const wp<U>& other);
    template<typename U> wp& operator = (const sp<U>& other);

    void set_object_and_refs(T* other, weakref_type* refs);

    // promotion to sp

    sp<T> promote() const;

    
    // Reset

    void clear();

    // Accessors

    inline  weakref_type* get_refs() const { return m_refs; }

    inline  T* unsafe_get() const { return m_ptr; }

    // Operators
//
    COMPARE_WEAK(==)
    COMPARE_WEAK(!=)
    COMPARE_WEAK(>)
    COMPARE_WEAK(<)
    COMPARE_WEAK(<=)
    COMPARE_WEAK(>=)

    inline bool operator == (const wp<T>& o) const {
        return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
    }
    template<typename U>
    inline bool operator == (const wp<U>& o) const {
        return m_ptr == o.m_ptr;
    }

    inline bool operator > (const wp<T>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
    }
    template<typename U>
    inline bool operator > (const wp<U>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
    }

    inline bool operator < (const wp<T>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
    }
    template<typename U>
    inline bool operator < (const wp<U>& o) const {
        return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr);
    }
    inline bool operator != (const wp<T>& o) const { return m_refs != o.m_refs; }
    template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }
    inline bool operator <= (const wp<T>& o) const { return !operator > (o); }
    template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }
    inline bool operator >= (const wp<T>& o) const { return !operator < (o); }
    template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }


private:
    template <typename Y> friend class wp;
    template <typename Y> friend class sp;
    T* m_ptr;
    weakref_type* m_refs;
};

#undef COMPARE_WEAK

#endif //SMARTTOOLS_REFBASE_H

我們能看到,所有的要使用智能指針的對象,都要繼承RefBase對象。里面包含了關(guān)鍵的增加強(qiáng)引用計(jì)數(shù)以及減少強(qiáng)引用計(jì)數(shù)的方法,以及創(chuàng)建弱引用和獲取強(qiáng)弱引用計(jì)數(shù)的方法。

并且為了方便弱引用能夠訪問到Refbase中的私有屬性,作為一個友元類存在里面。

同時創(chuàng)建一個wp(WeakPointer)弱引用的類。里面包含了必要的構(gòu)造函數(shù),以及比對方法。為了避免用戶使用操作符號,對弱引用中的東西進(jìn)行操作,必須重寫所有的操作符號。

更重要的是,聲明一個promote方法,這個方法的作用就是把wp弱引用指針升級為強(qiáng)引用指針。

等一下,讀者肯定會好奇了,為什么已經(jīng)存在了wp的類,還要創(chuàng)造一個weakref_type的類呢?

從我們上面的設(shè)計(jì)上看來,我們需要統(tǒng)計(jì)sp和wp的引用計(jì)數(shù),并且以sp的引用計(jì)數(shù)為標(biāo)準(zhǔn)進(jìn)行刪除。那么我們勢必需要計(jì)算兩者計(jì)數(shù)。那么我們?yōu)槭裁床怀殡x這一塊計(jì)數(shù)邏輯出來呢?weakref_type的實(shí)現(xiàn)是weak_impl,因此其存在意義就是方便計(jì)算兩種指針的引用次數(shù)。

那么我們繼續(xù)實(shí)現(xiàn)sp,強(qiáng)引用的頭文件StrongPointer。

#ifndef SMARTTOOLS_STRONGPOINTER_H
#define SMARTTOOLS_STRONGPOINTER_H

template <typename T> class wp;

#define COMPARE(_op_)                                           \
inline bool operator _op_ (const sp<T>& o) const {              \
    return m_ptr _op_ o.m_ptr;                                  \
}                                                               \
inline bool operator _op_ (const T* o) const {                  \
    return m_ptr _op_ o;                                        \
}                                                               \
template<typename U>                                            \
inline bool operator _op_ (const sp<U>& o) const {              \
    return m_ptr _op_ o.m_ptr;                                  \
}                                                               \
template<typename U>                                            \
inline bool operator _op_ (const U* o) const {                  \
    return m_ptr _op_ o;                                        \
}                                                               \
inline bool operator _op_ (const wp<T>& o) const {              \
    return m_ptr _op_ o.m_ptr;                                  \
}                                                               \
template<typename U>                                            \
inline bool operator _op_ (const wp<U>& o) const {              \
    return m_ptr _op_ o.m_ptr;                                  \
}

template <typename T>
class sp{
    inline sp():m_ptr(0){}

    sp(T* other);

    sp(const sp<T>& other);

    sp(sp<T>&& other);

    template <typename U> sp(U *other);

    template <typename U> sp(const sp<U>& other);

    template <typename U> sp(sp<U>&& other);


    ~sp();


    sp& operator = (T* other);

    sp& operator = (const sp<T>& other);

    sp&operator = (sp<T>&& other);


    template <typename U> sp&operator = (const sp<U>& other);

    template <typename U> sp&operator = (sp<U>&& other);

    template <typename U> sp&operator = (U* other);

    void force_set(T* other);

    void clear();

    inline T& operator*() const {
        return *m_ptr;
    };

    inline T* operator -> ()const{
        return m_ptr;
    }

    inline T* get() const {
        return m_ptr;
    }

    inline explicit operator bool () const {
        return m_ptr != nullptr;
    }


    // Operators

    COMPARE(==)
    COMPARE(!=)
    COMPARE(>)
    COMPARE(<)
    COMPARE(<=)
    COMPARE(>=)


private:
    template <typename Y> friend class wp;
    template <typename Y> friend class sp;
    void set_pointer(T* ptr);
    T* m_ptr;
};

#endif //SMARTTOOLS_STRONGPOINTER_H

sp的設(shè)計(jì)上相對簡單點(diǎn),和上面的SmartPointer十分相似。只是復(fù)寫很多操作符號。

實(shí)現(xiàn)sp,wp,weakref_type

wp的實(shí)現(xiàn)

template <typename T>
wp<T>::wp(T *other):m_ptr(other){
    if(other){
        m_refs = other->createWeak(this);
    }
}


template <typename T>
wp<T>::wp(const wp<T>& other)
:m_ptr(other.m_ptr),m_refs(other.m_refs){
    //other的指針不為空,再增加弱引用計(jì)數(shù)
    if(m_ptr){
        m_refs->incWeak(this);
    }
}

template <typename T>
wp<T>::wp(const sp<T>& other):m_ptr(other.m_ptr){
    if(m_ptr){
        m_refs = m_ptr->createWeak(this);
    }
}

template <typename T> template <typename U>
wp<T>::wp(U *other)
:m_ptr(other){
    if(other){
        m_refs = other->createWeak(this);
    }
}


template <typename T> template <typename U>
wp<T>::wp(const wp<U>& other)
:m_ptr(other.m_ptr){
    if(m_ptr){
        m_refs = other.m_refs;
        m_refs->incWeak(this);
    }
}

template <typename T> template <typename U>
wp<T>::wp(const sp<U>& other)
:m_ptr(other.m_ptr){
    if(m_ptr){
        m_refs = m_ptr->createWeak(this);
    }
}


template <typename T>
wp<T>::~wp() {
    if(m_ptr){
        m_refs->decWeak(this);
    }
}


template <typename T>
wp<T>& wp<T>::operator=(T *other) {
    //賦值操作,把帶著RefBase的對象復(fù)制給弱引用
    //為新的對象創(chuàng)建引用計(jì)數(shù)器
    weakref_type* newRefs = other ? other->createWeak(this) : 0;
    //如果原來的指針有數(shù)據(jù),則需要把原來的弱引用減一。
    //因?yàn)榇藭r相當(dāng)于把當(dāng)前已有的弱引用被新來的替換掉
    //那么,原來引用的弱引用計(jì)數(shù)要減一
    if(m_ptr){
        m_refs->decWeak(this);
    }


    m_ptr = other;
    m_refs = newRefs;
    return *this;
}


template <typename T>
wp& wp<T>::operator=(const wp<T> &other) {
    //弱引用賦值
    weakref_type* otherRef(other.m_refs);
    T* otherPtr(other.m_ptr);
    if(otherPtr){
        otherPtr->incWeak(this);
    }

    if(m_ptr){
        m_refs->decWeak(this);
    }

    m_ptr = otherPtr;
    m_refs = otherRef;
    return *this;
}

template <typename T>
wp& wp<T>::operator=(const sp<T> &other) {
    //強(qiáng)引用賦值給弱引用
    //和上面對象賦值同理
    weakref_type* newRefs = other ? other->createWeak(this) : 0;
    T* otherPtr(other.m_ptr);
    if(m_ptr){
        m_refs->decWeak(this);
    }

    m_ptr = otherPtr;
    m_refs = newRefs;
    return *this;
}

template <typename T> template <typename U>
wp& wp<T>::operator=(U *other) {
    //不是同類型賦值給弱引用
    weakref_type* newRefs = other ? other->createWeak(this) : 0;
    if(m_ptr){
        m_refs->decWeak(this);
    }

    m_ptr = other;
    m_refs = newRefs;
    return *this;
}

template<typename T> template<typename U>
wp<T>& wp<T>::operator = (const wp<U>& other)
{
    //不同類型的弱引用賦值
    weakref_type* otherRefs(other.m_refs);
    U* otherPtr(other.m_ptr);
    if (otherPtr){
        otherRefs->incWeak(this);
    }
    if (m_ptr){
        m_refs->decWeak(this);
    }
    m_ptr = otherPtr;
    m_refs = otherRefs;
    return *this;
}

template<typename T> template<typename U>
wp<T>& wp<T>::operator = (const sp<U>& other)
{
    //不同對象的強(qiáng)引用賦值給弱引用
    weakref_type* newRefs =
            other != NULL ? other->createWeak(this) : 0;
    U* otherPtr(other.m_ptr);
    if (m_ptr){
        m_refs->decWeak(this);
    }
    m_ptr = otherPtr;
    m_refs = newRefs;
    return *this;
}

template<typename T>
void wp<T>::set_object_and_refs(T* other, weakref_type* refs)
{
    //直接賦值對象和引用
    if (other){
        refs->incWeak(this);
    }
    if (m_ptr){
        m_refs->decWeak(this);
    }
    m_ptr = other;
    m_refs = refs;
}

template <typename T>
sp<T> wp<T>::promote() const {
    //核心
    sp<T> result;
    if(m_ptr && m_refs->attemptIncStrong(&result)){
        result.set_pointer(m_ptr);
    }

    return result;
}

template<typename T>
void wp<T>::clear()
{
    if (m_ptr) {
        m_refs->decWeak(this);
        m_ptr = 0;
    }
}

能看到這里面大部分的工作都是處理操作符和構(gòu)造函數(shù)。當(dāng)調(diào)用構(gòu)造函數(shù)的時候,會為wp弱引用指針創(chuàng)建一個計(jì)數(shù)器。當(dāng)調(diào)用賦值操作符時候,會判斷原來是否包含引用對象,有則因?yàn)槲覀冃枰鎿Q,相當(dāng)于不需要這個對象,需要減少一次引用計(jì)數(shù)。

在這里面核心還是promote方法。還記得wp不能直接操作,需要promote升級,沒錯這里是約定俗稱的。因此promote的時候會創(chuàng)建一個sp,并且會調(diào)用attemptIncStrong增加一次引用計(jì)數(shù)。attemptIncStrong為了避免多線程干擾而創(chuàng)建的方法,稍后會繼續(xù)聊聊。

那么sp的思路實(shí)際上和wp思路幾乎一致

template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}

template<typename T>
sp<T>::sp(const sp<T>& other)
        : m_ptr(other.m_ptr) {
    if (m_ptr)
        m_ptr->incStrong(this);
}

template<typename T>
sp<T>::sp(sp<T>&& other)
        : m_ptr(other.m_ptr) {
    other.m_ptr = nullptr;
}

template<typename T> template<typename U>
sp<T>::sp(U* other)
        : m_ptr(other) {
    if (other)
        (static_cast<T*>(other))->incStrong(this);
}

template<typename T> template<typename U>
sp<T>::sp(const sp<U>& other)
        : m_ptr(other.m_ptr) {
    if (m_ptr)
        m_ptr->incStrong(this);
}

template<typename T> template<typename U>
sp<T>::sp(sp<U>&& other)
        : m_ptr(other.m_ptr) {
    other.m_ptr = nullptr;
}

template<typename T>
sp<T>::~sp() {
    if (m_ptr)
        m_ptr->decStrong(this);
}

template<typename T>
sp<T>& sp<T>::operator =(const sp<T>& other) {
    // Force m_ptr to be read twice, to heuristically check for data races.
    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
    T* otherPtr(other.m_ptr);
    if (otherPtr) otherPtr->incStrong(this);
    if (oldPtr) oldPtr->decStrong(this);
    m_ptr = otherPtr;
    return *this;
}

template<typename T>
sp<T>& sp<T>::operator =(sp<T>&& other) {
    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
    if (oldPtr) oldPtr->decStrong(this);
    m_ptr = other.m_ptr;
    other.m_ptr = nullptr;
    return *this;
}

template<typename T>
sp<T>& sp<T>::operator =(T* other) {
    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
    if (other) other->incStrong(this);
    if (oldPtr) oldPtr->decStrong(this);
    m_ptr = other;
    return *this;
}

template<typename T> template<typename U>
sp<T>& sp<T>::operator =(const sp<U>& other) {
    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
    T* otherPtr(other.m_ptr);
    if (otherPtr) otherPtr->incStrong(this);
    if (oldPtr) oldPtr->decStrong(this);
    m_ptr = otherPtr;
    return *this;
}

template<typename T> template<typename U>
sp<T>& sp<T>::operator =(sp<U>&& other) {
    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
    if (m_ptr) m_ptr->decStrong(this);
    m_ptr = other.m_ptr;
    other.m_ptr = nullptr;
    return *this;
}

template<typename T> template<typename U>
sp<T>& sp<T>::operator =(U* other) {
    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
    if (other) (static_cast<T*>(other))->incStrong(this);
    if (oldPtr) oldPtr->decStrong(this);
    m_ptr = other;
    return *this;
}

template<typename T>
void sp<T>::force_set(T* other) {
    other->forceIncStrong(this);
    m_ptr = other;
}

template<typename T>
void sp<T>::clear() {
    if (m_ptr) {
        m_ptr->decStrong(this);
        m_ptr = 0;
    }
}

template<typename T>
void sp<T>::set_pointer(T* ptr) {
    m_ptr = ptr;
}

小節(jié)

可以看見,在wp和sp的體系中,這兩者只做兩件事情,持有對象引用,并且調(diào)用計(jì)數(shù)方法進(jìn)行計(jì)數(shù)。而核心方法還是在weakref_type以及RefBase中。

接下來,我們要實(shí)現(xiàn)核心的計(jì)數(shù)方法。

weakref_type的實(shí)現(xiàn)

首先肯定有強(qiáng)弱引用的計(jì)數(shù)

#define INITIAL_STRONG_VALUE (1<<28)

class RefBase::weakref_impl : public RefBase::weakref_type{
public:
    //強(qiáng)引用計(jì)數(shù)
    std::atomic<int32_t> mStrong;
    //弱引用計(jì)數(shù)
    std::atomic<int32_t> mWeak;

    //持有計(jì)數(shù)基礎(chǔ)
    RefBase* const mBase;

    //聲明周期的標(biāo)志位
    std::atomic<int32_t> mFlags;


public:
    explicit weakref_impl(RefBase* base)
    :mStrong(INITIAL_STRONG_VALUE),mWeak(0),mBase(base){

    }

};

RefBase的實(shí)現(xiàn)

構(gòu)造函數(shù)

RefBase::RefBase():mRefs(new weakref_impl(this)) {

}

首先看看,增加引用指針計(jì)數(shù)。

增加強(qiáng)引用計(jì)數(shù)
void RefBase::incStrong(const void *id) const {
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    const int32_t c = refs->mStrong.fetch_add(1,std::memory_order_relaxed);
    //說明不是第一次聲明
    if(c != INITIAL_STRONG_VALUE){
        return;
    }

    int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,std::memory_order_relaxed);

    refs->mBase->onFirstRef();
}

能看到的是,為了同步強(qiáng)引用和弱引用的次數(shù),只要每一次增加一次強(qiáng)引用計(jì)數(shù),就會增加弱引用次數(shù)。但是弱引用就不是如此,因此強(qiáng)引用的次數(shù)一定小于等于弱引用。

在這里面,強(qiáng)引用的計(jì)數(shù)次數(shù)會初始化為(1<<28)就是1向左移動28位。在32位的int中屬于十分大的數(shù)字。

這么做的好處就是能夠通過簡單的加減就能知道是否是第一次。

考慮到指針為因?yàn)橹羔樖?2位,所以這個大數(shù)字沒有可能被引用這么多次可能。因此只要判斷加一前發(fā)現(xiàn)不是這個數(shù)字INITIAL_STRONG_VALUE,就能確定是不是第一次。從而判斷是否調(diào)用onFirstRef。這個只有第一次初始化sp才會調(diào)用的方法,相當(dāng)于sp中綁定的生命周期。

從這里就能知道Google工程師的功力深厚。

增加弱引用計(jì)數(shù)
void RefBase::weakref_type::incWeak(const void *id) {
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    const int32_t c __unused = impl->mWeak.fetch_add(1,
            std::memory_order_relaxed);
}

很簡單沒什么好聊的。

減少引用計(jì)數(shù)

減少引用計(jì)數(shù),我們就必須要小心。因?yàn)檫@個控制著對象什么時候刪除。以及存在的邏輯。

由于定義中sp能夠使用對象,那么意味著,sp的強(qiáng)引用指針計(jì)數(shù)將會控制對象引用的聲明周期。

注意到?jīng)]有,在這個過程中,我們除了有對象的引用對象之外,還存在著一個用來統(tǒng)計(jì)強(qiáng)弱引用計(jì)數(shù)的weakref_type。這個對象也必須銷毀。既然sp管理了愿對象,那么wp的引用計(jì)數(shù)就管理控制統(tǒng)計(jì)強(qiáng)弱引用計(jì)數(shù)的weakref_type聲明周期。

因此,我們在減少的強(qiáng)引用計(jì)數(shù)的時候,要注意順序。必須先減少強(qiáng)引用計(jì)數(shù),再減少弱引用順序。

減少強(qiáng)引用指針的計(jì)數(shù)
void RefBase::decStrong(const void *id) const {
    weakref_impl* const  refs = mRefs;
    const int32_t c = refs->mStrong.fetch_sub(1,std::memory_order_release);

    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_WEAK) == OBJECT_LIFETIME_STRONG){
            delete this;
        }
    }

    refs->decWeak(id);
}

能看到的是,此時減少一次強(qiáng)引用次數(shù),當(dāng)達(dá)到1了之后,說明不會再使用,就delete掉。當(dāng)然源碼里面還有一個flags字段,這個字段使用擴(kuò)展sp和wp的生命周期的行為。默認(rèn)就是OBJECT_LIFETIME_STRONG。

減少弱引用計(jì)數(shù)
void RefBase::weakref_type::decWeak(const void *id) {
    weakref_impl* const impl = static_cast<weakref_impl*>(this);

    const int32_t c = impl->mWeak.fetch_sub(1,std::memory_order_release);


    if(c != 1){
        return;
    }
    std::atomic_thread_fence(std::memory_order_acquire);
    int32_t flags = impl->mFlags.load(std::memory_order_release);

    if((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG){
        if(impl->mStrong.load(std::memory_order_release)
        == INITIAL_STRONG_VALUE){
            //說明強(qiáng)引用指針只是初始化
        } else{
            //刪除引用計(jì)數(shù)對象
            delete impl;
        }
    } else{
        impl->mBase->onLastWeakRef(id);
        delete impl->mBase;
    }

}

智能指針其他細(xì)節(jié)

當(dāng)我們第一次升級sp的時候調(diào)用了一個特殊的引用次數(shù)增加的方法。

bool RefBase::weakref_type::attemptIncStrong(const void *id) {
    incWeak(id);

    weakref_impl*const impl = static_cast<weakref_impl*>(this);

    int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);

    //這種情況是有本已經(jīng)有數(shù)據(jù)引用
    while(curCount >0 &&curCount != INITIAL_STRONG_VALUE){
        //發(fā)現(xiàn)和原來相比大于1則退出循環(huán)
        if(impl->mStrong.compare_exchange_weak(curCount,curCount+1,
                std::memory_order_relaxed)){
            break;
        }
    }

    //這種情況是初始化,或者已經(jīng)被釋放了
    if(curCount<=0 || curCount == INITIAL_STRONG_VALUE){
        int32_t flags = impl->mFlags.
                load(std::memory_order_relaxed);

        if((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG){
            //原來的強(qiáng)引用被釋放
            if(curCount <= 0){
                decWeak(id);
                return false;
            }

            //初始化
            while (curCount > 0){
                if(impl->mStrong.compare_exchange_weak(curCount,
                        curCount+1,std::memory_order_relaxed)){
                    break;
                }
            }


            //promote 升級失敗
            //避免某些線程,又把當(dāng)前的sp釋放掉
            if(curCount <= 0){
                decWeak(id);
                return false;
            }

        } else{
            //會判斷當(dāng)前是否是需要FIRST_INC_STRONG
            if(!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG,id)){
                decWeak(id);
                return false;
            }

            curCount = impl->mStrong.load(std::memory_order_relaxed);

            //如果已經(jīng)初始化過了引用計(jì)數(shù),則調(diào)用onLastStrongRef
            if(curCount != 0&&curCount!=INITIAL_STRONG_VALUE){
                impl->mBase->onLastStrongRef(id);
            }
        }
    }

    //如果在添加之前是INITIAL_STRONG_VALUE,說明是初始化,
    // 需要減掉INITIAL_STRONG_VALUE,才是真正的計(jì)數(shù)
    if(curCount == INITIAL_STRONG_VALUE){
        impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,std::memory_order_relaxed);
    }

    return true;
}

而每一次調(diào)用createWeak的方法只會增加一次計(jì)數(shù)

RefBase::weakref_type *RefBase::createWeak(const void *id) const {
    mRefs->incWeak(id);
    return mRefs;
}

測試一下:

class Test:public RefBase{
private:
    void onFirstRef(){
        LOGE("first");
    }
public:
    void print(){
        LOGE("PRINT");
    }

    void incStrongPointer(){
        incStrong(this);
    }

    int printSCount(){
        return getStrongCount(this);
    }
};



void testPointer(){
    sp<Test> s(new Test());
  
}
image.png

確實(shí)是正確的流程。

那么,我們試試,更加復(fù)雜的作用域操作。

void testPointer(){
    sp<Test> s;
    {
        s = new Test();
        s->print();
        LOGE("1 times:%d",s->printSCount());
        s->incStrongPointer();
        LOGE("2 times:%d",s->printSCount());
    }


    LOGE("3 times:%d",s->printSCount());
}

當(dāng)我們在一個作用域內(nèi)聲明了一個Test的對象。按照道理會在這個作用域結(jié)束的時候析構(gòu)。我們看看其能不能通過增加引用計(jì)數(shù),來延長生命周期。

void testPointer(){
    sp<Test> s1;
    {
        sp<Test> s;
        s = new Test();
        s->print();
        LOGE("1 times:%d",s->printSCount());
        //s->incStrongPointer();
        s1 = s;
        if(s){
            LOGE("2 times:%d",s1->printSCount());
        }
    }

    if(s1){
        LOGE("3 times:%d",s1->printSCount());
    }

}

對于s1來說作用域是整個方法,而對于s來說作用域就是在方法的打括號內(nèi)。理論上,=的操作符會增加一次新的強(qiáng)引用指針,減少一次舊的引用指針,也就如下圖。


image.png

總結(jié)

繪制一個UML圖。


智能指針.png

我們能夠從UML中清晰的看到各自的職責(zé)。
weakref_type控制弱引用的計(jì)數(shù)方法,同時通過弱引用計(jì)數(shù)控制weakref_type的生命周期。

所以這就是為什么在上述的代碼中,并沒有直接在weakref聲明一個方法,而是通過參數(shù)來設(shè)置。

其次在繼承了RefBase的Object本身具備了增加減少強(qiáng)引用的方法。因?yàn)榇藭r想要操作Object的時候已經(jīng)默認(rèn)是強(qiáng)引用指針引用狀態(tài)。同時持有這weakref_impl去訪問引用計(jì)數(shù)。

wp和sp都是持有一個引用原有Object的引用。管理操作符,構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),操作符,來對傳進(jìn)來的Object控制其引用計(jì)數(shù)。

實(shí)際上,看到這個UML圖,就感覺很簡單了。

特別提一句,看到上面的用法之后,sp和wp是怎么限制其他人使用內(nèi)部的指針的。

可以關(guān)注到sp,重寫下面這個操作符。

inline T& operator*() const {
        return *m_ptr;
    };

    inline T* operator -> ()const{
        return m_ptr;
    }

而wp沒有重寫這個操作符。因此sp才能操作得了sp持有的對象。

附上完整的代碼的地址:
https://github.com/yjy239/SmartTool

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。