背景
如果遇到什么問題在這個地址下留言:http://www.lxweimin.com/p/2f0ecf6ca08c
在Android 的底層中,編寫大量的c/c++源碼。但是卻很少看到Android去調用delete去刪除對象的申請的內存。而這其中,必定有一個東西去管理對象的生命周期。而這個擔起這個責任就是智能指針。
于此同時,還能看到Android底層調用了鎖之后,我們也沒看到相應的解鎖方法。實際上這里面起作用的就是智能鎖,將鎖自動解開。
接下來,將會從智能鎖開始,聊聊這兩個相似的設計思路。
正文
智能鎖
讓我們先看看源碼,關于智能鎖其中一個例子。
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顯示系統的核心SurfaceFlinger在自己的進程初始化的時候,會初始化我們MessageQueue(設計思路和Android應用常用的Handler一致,一個消息隊列)。作為消息隊列,里面沒有使用線程安全的數據結構,自然需要上鎖,保護里面的數據結構。
雖然在整個Android系統中,一般只會初始化一次SurfaceFlinger。但是這就Google工程還是做了保護措施,可見其思想就是自己的模塊要保證自己模塊中數據正確性。
我們能看到一個很有趣東西
Mutex::Autolock _l(mStateLock);
看到這個名字我們大致上能夠明白實際上,這必定是個鎖。但是我們卻沒看到哪里解鎖,哪里上鎖了。
智能鎖的思路
在看源碼之前,我們大致思考一下,如果我們嘗試著簡化,通過一個對象來管理整個上鎖解鎖流程應該怎么做。這就能顧讓我們聯想到,這個過程我們可以讓鎖跟著對象的創建和銷毀的生命周期綁定起來。這樣就能很簡單做到鎖的自動處理。本質上源碼也是這么做的。
大致上,我們需要往這個方向努力:
#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");
}
調用:
Mutex m;
LOGE("do something");
這樣就能讓鎖和對象互相綁定。只要使用這種方式就能夠讓這個Mutex對象跟著方法內的作用域跑,當這個作用域跑完,就能自動解鎖。也就是這種思路,才會在Android一些底層看到這么調用。
但是這樣的思路,讓我們看到一種可行性,就是通過作用域來決定對象的釋放實際,從而決定鎖的釋放范圍。
可以看到源碼BufferQueueProducer(顯示系統中生產GraphBuffer的生產隊列)中
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
....
}
可以靈活運用著在方法創造一個作用域,讓智能鎖自動解鎖。
當然這只是雛形,我們看看底層是做了什么。我們隨意抽一個Mutex的實現看看。
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對象的存在進行管理。Autolock則是讓這個對象在作用域能進行上鎖解鎖管理。同時禁止了Mutex的拷貝構造函數,因為這樣會造成意料之外的鎖存在。
當然,在/system/core/libutils/include/utils/下還有寫的更好的智能鎖。
能看到的是,讓某種行為綁定著對象的生命周期這種設計也是很常見。比如說Glide的每一次請求都是綁定在當前Activity一個隱形的Fragment中。
接下來讓我們看看更加重要的智能智能。
智能指針
智能指針誕生的初衷來源,c++每一次new一個新的對象,因為對象的內存放到了堆中,導致我們必須想辦法清除掉堆中的內存。當我們忘記清除指針指向的內存時候,就會產生野指針問題。然而每一次想辦法在合適的地方delete,將會讓整個工程復雜度提升了一個等級,也給每個程序員素質的考驗。
就以我之前寫Binder來說,大部分對象的操作都在內核中,對binder_transaction這些事務來回拷貝,binder_ref在不斷的使用,想要找到一個合適的時機delete需要判斷的東西就十分多。
此時就誕生了智能指針。本質上智能指針的思路,實際上和我們所說的引用計數本質上是一個東西。
引用計數的思路就是,當每一次引用,就對這個對象引用加一,當超過作用域的之類調用一次析構函數函數,引用計數減一。知道引用計數一直減到1,說明再也沒有任何對象引用這個對象,就能安心的銷毀(初始引用次數為1)。
但是這種內存計數方式有一個致命缺點,就是當兩個引用互相引用時候,會發生循環計數的問題。而智能指針的為了解決這個,誕生了強引用指針和弱引用指針。
了解這些之后,我們嘗試的思考怎么編寫智能指針才能解決上面那些問題。
智能指針設計思路
首先我們能夠確定是這必定是一個模版類,同時能夠為了能夠管理指針,必定會傳一個指針進來。
對于指針的計數,我們能不能模仿智能鎖那樣,通過某個類包裝起來,讓某個全權管理這個類的真正的析構時機以及計數呢?
就想我上面的說的,加入讓智能指針這個對象去管理計數,就會出現一個致命的情況。當智能指針1和智能指針2都引用同一個對象的時候,當智能指針1的計數減為0,要去析構的時候,智能指針2還持用著計數,此時就會出現析構失敗。
因此,我們不可能只用一個類去完成。這個過程中,至少需要兩個類,一個是用于計數,另一個是用于管理指針。
接下來讓我們嘗試,編寫一個智能指針。首先創建一個LightRefBase基類,讓之后所有的類都去繼承這個類,讓類自己擁有計數的功能。接著再創建一個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
能看到的是,這里我創建了一個模版類,一個輕量級別的引用計數。當調用incStrong,將會增加原子計數引用次數。當調用decStrong,則會減少原子計數的引用次數。
這里稍微記錄一下atomic原子類操作。
- fetch_add 是指原子數字的增加
- fetch_sub 是指原子數字的減少
在這些原子模版類的操作中memory_order_release之類的內存順序約束的操作。一共有如下操作:
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內存松弛。第二類,sequential_consistency內存一致序,第三類acquire-release獲取釋放一致序。
對于內存松弛(memory_order_relaxed),對于多線程沒有指令順序一致性要求。只是保證了在一個線程內的原子操作保證了順序上處理。對于不同線程之間的執行順序是隨意的。
對于內存一致序(memory_order_seq_cst),這是以犧牲優化效率,來保證指令順序的一致性。相當于不打開編譯器的優化指令。按照正常指令執行序執行,多線程之間原子操作也會Synchronized-with,比如atomic::load()需要等待atomic::store()寫下元素才能讀取,同步過程。
獲取釋放一致序,相當于對relaxed的加強。relax序由于無法限制多線程間的排序,所以引入synchronized-with,但并不一定意味著,統一的操作順序。因為可能出現當出現讀寫操作時候,寫入操作完成但是還是在緩存,并沒有對應的內存,造成的異常。因此設計上誕生memory_order_release釋放鎖,memory_order_acquire上自旋鎖。memory_order_consume的多線程消費者生產這些設計。
SmartPointer
在編寫SmartPointer的時候,記住要重寫幾個操作符號,因為賦值,創造構造SmartPointer的構造函數,我們都需要為對象的引用計數加一。當超出了作用域,則把對象的引用減一。
#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源碼分析時候,出現的引用計數時候聲明一個sp的方式一模一樣。
測試一下:
在一個方法內,確實完成了自動增加計數和銷毀了。這樣的設計和智能(自動鎖)鎖十分相似。都是靈活運用了作用域和析構函數之間的關系,對對象的引用計數做了內存管理,來判斷是否繼續需要這個對象。
LightRefBase的缺點分析
正如上面所說的一樣,這么做雖然能做到簡單的計數統計,似乎沒有什么問題。為什么Java虛擬機不采用引用計數,而去使用GC引用鏈對對象進行內存掛歷。
我們來考慮這種情況。
當A和B互相引用的時候。就造成這么一個問題
互相引用的時候,就造成一個特殊的情況。A中的B字段指向了B內存會讓B本身無法析構,而B中的A字段指向了A的內存也會讓A本身無法析構。
這樣就出現,我們常說的循環引用。為了處理這種問題,誕生了強弱指針的概念。
先來聊聊強指針sp(StrongPointer)。強指針和我上面寫的SmartPointer原理幾乎一致。目的是為了操作繼承了RefBase類中引用計數。
那么弱指針誕生就是為了處理循環引用的問題。如果換做是我們的話,我們該怎么處理這種異常呢。
我們回歸問題的本質,這種情況類似于死鎖,因為系統檢查到雙方都需要對方的資源,導致無法回收。那么就按照處理死鎖的辦法,打斷死鎖的資源的引用鏈就ok。這就是弱引用誕生的初衷。
強弱指針兩種指針,將會分別為自己計數。那么我們一定需要一個刪除引用的計數標準,當強引用了一個對象,當強引用的計數減到了1,將會刪除里面的引用。
這樣就打斷了,引用計數的循環。但是,你們一定會想到,當我們刪除A對象的引用,從B訪問A,不就會出現了訪問野指針/空指針的問題嗎?
因此弱指針又有一個規定,弱指針不能直接訪問對象,必須升級為強指針才能訪問對象。
有這幾個標準之后,我們嘗試編寫一下弱指針的源碼。我們拋棄原來的LightRefBase,創建一個更加泛用的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中的私有數據
friend class weakref_type;
//一個實現類
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);
//拷貝構造函數
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對象。里面包含了關鍵的增加強引用計數以及減少強引用計數的方法,以及創建弱引用和獲取強弱引用計數的方法。
并且為了方便弱引用能夠訪問到Refbase中的私有屬性,作為一個友元類存在里面。
同時創建一個wp(WeakPointer)弱引用的類。里面包含了必要的構造函數,以及比對方法。為了避免用戶使用操作符號,對弱引用中的東西進行操作,必須重寫所有的操作符號。
更重要的是,聲明一個promote方法,這個方法的作用就是把wp弱引用指針升級為強引用指針。
等一下,讀者肯定會好奇了,為什么已經存在了wp的類,還要創造一個weakref_type的類呢?
從我們上面的設計上看來,我們需要統計sp和wp的引用計數,并且以sp的引用計數為標準進行刪除。那么我們勢必需要計算兩者計數。那么我們為什么不抽離這一塊計數邏輯出來呢?weakref_type的實現是weak_impl,因此其存在意義就是方便計算兩種指針的引用次數。
那么我們繼續實現sp,強引用的頭文件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的設計上相對簡單點,和上面的SmartPointer十分相似。只是復寫很多操作符號。
實現sp,wp,weakref_type
wp的實現
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的指針不為空,再增加弱引用計數
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的對象復制給弱引用
//為新的對象創建引用計數器
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>
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) {
//強引用賦值給弱引用
//和上面對象賦值同理
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)
{
//不同對象的強引用賦值給弱引用
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;
}
}
能看到這里面大部分的工作都是處理操作符和構造函數。當調用構造函數的時候,會為wp弱引用指針創建一個計數器。當調用賦值操作符時候,會判斷原來是否包含引用對象,有則因為我們需要替換,相當于不需要這個對象,需要減少一次引用計數。
在這里面核心還是promote方法。還記得wp不能直接操作,需要promote升級,沒錯這里是約定俗稱的。因此promote的時候會創建一個sp,并且會調用attemptIncStrong增加一次引用計數。attemptIncStrong為了避免多線程干擾而創建的方法,稍后會繼續聊聊。
那么sp的思路實際上和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;
}
小節
可以看見,在wp和sp的體系中,這兩者只做兩件事情,持有對象引用,并且調用計數方法進行計數。而核心方法還是在weakref_type以及RefBase中。
接下來,我們要實現核心的計數方法。
weakref_type的實現
首先肯定有強弱引用的計數
#define INITIAL_STRONG_VALUE (1<<28)
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;
public:
explicit weakref_impl(RefBase* base)
:mStrong(INITIAL_STRONG_VALUE),mWeak(0),mBase(base){
}
};
RefBase的實現
構造函數
RefBase::RefBase():mRefs(new weakref_impl(this)) {
}
首先看看,增加引用指針計數。
增加強引用計數
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();
}
能看到的是,為了同步強引用和弱引用的次數,只要每一次增加一次強引用計數,就會增加弱引用次數。但是弱引用就不是如此,因此強引用的次數一定小于等于弱引用。
在這里面,強引用的計數次數會初始化為(1<<28)就是1向左移動28位。在32位的int中屬于十分大的數字。
這么做的好處就是能夠通過簡單的加減就能知道是否是第一次。
考慮到指針為因為指針是32位,所以這個大數字沒有可能被引用這么多次可能。因此只要判斷加一前發現不是這個數字INITIAL_STRONG_VALUE,就能確定是不是第一次。從而判斷是否調用onFirstRef。這個只有第一次初始化sp才會調用的方法,相當于sp中綁定的生命周期。
從這里就能知道Google工程師的功力深厚。
增加弱引用計數
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);
}
很簡單沒什么好聊的。
減少引用計數
減少引用計數,我們就必須要小心。因為這個控制著對象什么時候刪除。以及存在的邏輯。
由于定義中sp能夠使用對象,那么意味著,sp的強引用指針計數將會控制對象引用的聲明周期。
注意到沒有,在這個過程中,我們除了有對象的引用對象之外,還存在著一個用來統計強弱引用計數的weakref_type。這個對象也必須銷毀。既然sp管理了愿對象,那么wp的引用計數就管理控制統計強弱引用計數的weakref_type聲明周期。
因此,我們在減少的強引用計數的時候,要注意順序。必須先減少強引用計數,再減少弱引用順序。
減少強引用指針的計數
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);
}
能看到的是,此時減少一次強引用次數,當達到1了之后,說明不會再使用,就delete掉。當然源碼里面還有一個flags字段,這個字段使用擴展sp和wp的生命周期的行為。默認就是OBJECT_LIFETIME_STRONG。
減少弱引用計數
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){
//說明強引用指針只是初始化
} else{
//刪除引用計數對象
delete impl;
}
} else{
impl->mBase->onLastWeakRef(id);
delete impl->mBase;
}
}
智能指針其他細節
當我們第一次升級sp的時候調用了一個特殊的引用次數增加的方法。
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);
//這種情況是有本已經有數據引用
while(curCount >0 &&curCount != INITIAL_STRONG_VALUE){
//發現和原來相比大于1則退出循環
if(impl->mStrong.compare_exchange_weak(curCount,curCount+1,
std::memory_order_relaxed)){
break;
}
}
//這種情況是初始化,或者已經被釋放了
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){
//原來的強引用被釋放
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 升級失敗
//避免某些線程,又把當前的sp釋放掉
if(curCount <= 0){
decWeak(id);
return false;
}
} else{
//會判斷當前是否是需要FIRST_INC_STRONG
if(!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG,id)){
decWeak(id);
return false;
}
curCount = impl->mStrong.load(std::memory_order_relaxed);
//如果已經初始化過了引用計數,則調用onLastStrongRef
if(curCount != 0&&curCount!=INITIAL_STRONG_VALUE){
impl->mBase->onLastStrongRef(id);
}
}
}
//如果在添加之前是INITIAL_STRONG_VALUE,說明是初始化,
// 需要減掉INITIAL_STRONG_VALUE,才是真正的計數
if(curCount == INITIAL_STRONG_VALUE){
impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,std::memory_order_relaxed);
}
return true;
}
而每一次調用createWeak的方法只會增加一次計數
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());
}
確實是正確的流程。
那么,我們試試,更加復雜的作用域操作。
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());
}
當我們在一個作用域內聲明了一個Test的對象。按照道理會在這個作用域結束的時候析構。我們看看其能不能通過增加引用計數,來延長生命周期。
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來說作用域就是在方法的打括號內。理論上,=的操作符會增加一次新的強引用指針,減少一次舊的引用指針,也就如下圖。
總結
繪制一個UML圖。
我們能夠從UML中清晰的看到各自的職責。
weakref_type控制弱引用的計數方法,同時通過弱引用計數控制weakref_type的生命周期。
所以這就是為什么在上述的代碼中,并沒有直接在weakref聲明一個方法,而是通過參數來設置。
其次在繼承了RefBase的Object本身具備了增加減少強引用的方法。因為此時想要操作Object的時候已經默認是強引用指針引用狀態。同時持有這weakref_impl去訪問引用計數。
wp和sp都是持有一個引用原有Object的引用。管理操作符,構造函數,拷貝構造函數,操作符,來對傳進來的Object控制其引用計數。
實際上,看到這個UML圖,就感覺很簡單了。
特別提一句,看到上面的用法之后,sp和wp是怎么限制其他人使用內部的指針的。
可以關注到sp,重寫下面這個操作符。
inline T& operator*() const {
return *m_ptr;
};
inline T* operator -> ()const{
return m_ptr;
}
而wp沒有重寫這個操作符。因此sp才能操作得了sp持有的對象。
附上完整的代碼的地址:
https://github.com/yjy239/SmartTool