Androidシステムのスマートポインタ(軽量ポインタ、強ポインタ、弱ポインタ)の実現原理分析(4)
外層のif文に戻り、ターゲットオブジェクトのライフサイクルが弱い参照カウントによって制御されている場合は、次の文を実行します.
理論的には、ターゲットオブジェクトのライフサイクルが弱い参照カウントによって制御されている場合、強い参照カウントと弱い参照カウントが両方とも0の場合、deleteターゲットオブジェクトが必要になりますが、ここではもう1つの制御があり、ターゲット対象のフラグ値をOBJECT_に設定することができます.LIFETIME_FOREVER、すなわちターゲットオブジェクトのライフサイクルが強参照カウントと弱参照カウントに全く制御されていない場合、ターゲットオブジェクトの強参照カウントと弱参照カウントが同時に0であっても、ここではdeleteというターゲットオブジェクトはできません.では、誰がdeleteを落とすのでしょうか.もちろん誰がnewから出てきたのか、誰がdeleteに来たのか、この時スマートポインタは完全に普通のポインタに退化し、ここのスマートポインタは非常に強く設計されています.
ここまで分析すると、小結する必要があります.
A.オブジェクトのフラグビットが0に設定されている場合、オブジェクトの強い参照カウント値が0であることを発見すると、自動的にdeleteがこのオブジェクトを削除します.
B.オブジェクトのフラグビットがOBJECT_に設定されている場合LIFETIME_WEAKでは、オブジェクトの強い参照カウントと弱い参照カウントが0の場合にのみ、自動的にdeleteがこのオブジェクトを削除します.
C.オブジェクトのフラグビットがOBJECT_に設定されている場合LIFETIME_FOREVERでは、オブジェクトは自動的にdeleteされず、誰がnewから出てきたオブジェクトがdeleteに落ちます.
ここまで来ると、強いポインタの分析が完了し、最後に弱いポインタを分析します.
4. 弱ポインタ
弱いポインタで使用する参照カウントクラスは、強いポインタと同様にRefBaseクラスであるため、ここでは重複して説明する、frameworks/base/include/utils/RefBaseで定義された弱いポインタの実装に直接来る.hファイル内:
強いポインタクラスに比べて、メンバー変数m_が1つあります.ptrはターゲットオブジェクトを指しますが、弱いポインタには追加のメンバー変数m_があります.refs、そのタイプはweakref_typeポインタ、次に弱いポインタの構造関数を分析したとき、初期化された場合を見てみましょう.ここで注目すべきは,依然として弱いポインタの構造関数と構造関数である.
まず、コンストラクション関数を見てみましょう.
ここでのパラメータotherは必ずRefBaseクラスを継承するので、ここではframeworks/base/libs/utils/RefBaseで定義RefBaseクラスのcreateWeak関数を呼び出す.cppファイル:
ここでのメンバー変数mRefsのタイプはweakref_implポインタ、weakref_implクラスのincWeak関数は、オブジェクトの弱い参照数を増やす役割を果たしています.関数は最後にmRefsを返し、弱いポインタオブジェクトのメンバー変数m_refsはターゲットオブジェクトを指すweakref_implオブジェクトです.
構造関数を見てみましょう.
ここで、弱いポインタは、解析時に強いポインタとは異なり、ターゲットオブジェクトのweakref_を直接呼び出すimplオブジェクトのdecWeak関数により弱参照カウントが減少し、弱参照カウントが0の場合、ターゲットオブジェクトでのフラグビット(0、OBJECT_LIFETIME_WEAKまたはOBJECT_LIFETIME_FOREVER)に基づいてdeleteターゲットオブジェクトを決定しますが、前述したように、ここでは説明しません.
ここまで分析して、弱いポインタはまだ紹介していないで、その最も重要な特性は私達はまだ分析していません.前述したように、弱いポインタの最大の特徴は、ターゲットオブジェクトを直接操作できないことです.これはどのようにして行われていますか.秘密は、弱いポインタクラスに*と->操作記号が再ロードされず、強いポインタにはこの2つの操作記号が再ロードされていることです.しかし、ターゲットオブジェクトを操作するには、どうすればいいのでしょうか.弱いポインタを強いポインタにアップグレードします.
メンバー変数m_を使用してアップグレードptrとm_refsは強いポインタspを構築し,ここでm_ptrはターゲットオブジェクトを指すポインタであり、m_refsはターゲットオブジェクトの中を指すweakref_implオブジェクト.
この強いポインタの構造過程を見てみましょう.
主に、ターゲットオブジェクトを指すメンバー変数m_を初期化します.ptr、ターゲットオブジェクトがまだ存在する場合、このm_ptrはターゲットオブジェクトを指し、ターゲットオブジェクトが存在しない場合、m_ptrはNULLです.アップグレードが成功するかどうかは、refs->attemptIncStrong関数の戻り結果によって決まります.
この関数の役割は、ターゲットオブジェクトの強い参照カウントを増やそうとすることですが、失敗する可能性があります.失敗した原因は、ターゲットオブジェクトがdeleteによって削除されたか、または他の原因である可能性があります.以下で分析します.先に強いポインタについて説明したように、ターゲットオブジェクトの強い参照カウントを増やすと同時に、ターゲットオブジェクトの弱い参照カウントも増加するので、関数は最初にincWeak関数を呼び出してターゲットオブジェクトの参照カウントを先に増加させ、後でターゲットオブジェクトの強い参照カウントを増加しようとすると失敗した場合、decWeak関数が呼び出され、前のincWeak操作がロールバックされます.
ここで、ターゲットオブジェクトの強い参照カウントを増やそうとすると、ターゲットオブジェクトが他の強いポインタで参照されている場合、すなわち、その強い参照カウントが0より大きく、INITIAL_に等しくない場合に分けて議論される.STRONG_VALUE、もう1つのケースは、ターゲットオブジェクトが強いポインタで参照されていないことです.すなわち、その強い参照数が0以下、またはINITIAL_に等しいことです.STRONG_VALUE.
1つ目のケースは簡単です.この場合、ターゲットオブジェクトが必ず存在することを示すので、この弱いポインタを強いポインタに引き上げることができます.この場合、ターゲットオブジェクトの強い参照カウント値を簡単に増加すればいいです.
ここでターゲットオブジェクトの強い参照カウントに対してプラス1操作を実行する場合、原子性を保証するには、他の場所でもこのターゲットオブジェクトの強い参照カウントにプラス1操作を実行している可能性があるため、android_を呼び出すのが一般的です.atomic_inc関数は完了しますが、ここではandroid_を呼び出すことによってatomic_cmpxchg関数で完了、android_atomic_cmpxchg関数はアーキテクチャに関連する関数であり、いくつかの特殊な命令を提供するアーキテクチャ上でandroid_を呼び出す.atomic_cmpxchg関数はandroidを呼び出すよりも1を加算する操作を実行する効率が高い.atomic_inc関数はもっと高いです.関数android_atomic_cmpxchgはsystem/core/include/cutils/atomicである.hファイルで定義されたマクロ:
実際に実行される関数はandroidですatomic_release_cas、この関数の動作原理は大体こうです:もしそれが*addr==oldvalueを発見したら、*addr=newvalueの操作を実行して、それから0を返して、さもなくば何もしないで、1を返します.このシナリオでは、oldvalueはcurCountに等しく、newvalueはcurCount+1に等しく、*addr==oldvalueの条件下では、ターゲットオブジェクトに対する強い参照カウント値が1増加することに相当する.どんな場合*addr!=oldvalueは?androidを呼び出すatomic_release_cas関数の前にoldvalueと値はアドレスaddrから読み出され、android_を実行するとatomic_release_cas関数の場合、アドレスaddrを他の場所で操作すると*addrが現れる可能性があります!=oldvalueの場合、他の場所でもターゲットオブジェクトの強い参照カウントが操作されていることを示します.そのため、ここではターゲットオブジェクトの強い参照カウントを増やす操作は実行できません.ターゲットオブジェクトの強い参照カウントを他の場所で操作してから再実行する必要があります.これは、whileループによって実行される理由です.
2つ目のケースは複雑です.この場合、ターゲットオブジェクトがまだ存在するか、存在しない可能性があるので、実際の状況から判断します.ターゲットオブジェクトの強い参照カウント値がINITIAL_に等しい場合STRONG_VALUEは、このターゲットオブジェクトがまだ強いポインタで参照されていないことを示します.この場合、弱いポインタが強いポインタに昇格できる条件は、次のとおりです.
allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
|| impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
- impl->mBase->onLastWeakRef(id);
- if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
- delete impl->mBase;
- }
理論的には、ターゲットオブジェクトのライフサイクルが弱い参照カウントによって制御されている場合、強い参照カウントと弱い参照カウントが両方とも0の場合、deleteターゲットオブジェクトが必要になりますが、ここではもう1つの制御があり、ターゲット対象のフラグ値をOBJECT_に設定することができます.LIFETIME_FOREVER、すなわちターゲットオブジェクトのライフサイクルが強参照カウントと弱参照カウントに全く制御されていない場合、ターゲットオブジェクトの強参照カウントと弱参照カウントが同時に0であっても、ここではdeleteというターゲットオブジェクトはできません.では、誰がdeleteを落とすのでしょうか.もちろん誰がnewから出てきたのか、誰がdeleteに来たのか、この時スマートポインタは完全に普通のポインタに退化し、ここのスマートポインタは非常に強く設計されています.
ここまで分析すると、小結する必要があります.
A.オブジェクトのフラグビットが0に設定されている場合、オブジェクトの強い参照カウント値が0であることを発見すると、自動的にdeleteがこのオブジェクトを削除します.
B.オブジェクトのフラグビットがOBJECT_に設定されている場合LIFETIME_WEAKでは、オブジェクトの強い参照カウントと弱い参照カウントが0の場合にのみ、自動的にdeleteがこのオブジェクトを削除します.
C.オブジェクトのフラグビットがOBJECT_に設定されている場合LIFETIME_FOREVERでは、オブジェクトは自動的にdeleteされず、誰がnewから出てきたオブジェクトがdeleteに落ちます.
ここまで来ると、強いポインタの分析が完了し、最後に弱いポインタを分析します.
4. 弱ポインタ
弱いポインタで使用する参照カウントクラスは、強いポインタと同様にRefBaseクラスであるため、ここでは重複して説明する、frameworks/base/include/utils/RefBaseで定義された弱いポインタの実装に直接来る.hファイル内:
- template
- class wp
- {
- public:
- typedef typename RefBase::weakref_type weakref_type;
-
- inline wp() : m_ptr(0) { }
-
- wp(T* other);
- wp(const wp
& other);
- wp(const sp
& other);
- template
wp(U* other);
- template
wp(const sp& other);
- template
wp(const wp& other);
-
- ~wp();
-
- // Assignment
-
- wp& operator = (T* other);
- wp& operator = (const wp
& other);
- wp& operator = (const sp
& other);
-
- template
wp& operator = (U* other);
- template
wp& operator = (const wp& other);
- template
wp& operator = (const sp& other);
-
- void set_object_and_refs(T* other, weakref_type* refs);
-
- // promotion to sp
-
- sp
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
& o) const {
- return (m_ptr == o.m_ptr) && (m_refs == o.m_refs);
- }
- template
- inline bool operator == (const wp& o) const {
- return m_ptr == o.m_ptr;
- }
-
- inline bool operator > (const wp
& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
- }
- template
- inline bool operator > (const wp& o) const {
- return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr);
- }
-
- inline bool operator & o) const {
- return (m_ptr == o.m_ptr) ? (m_refs
- }
- template
- inline bool operator & o) const {
- return (m_ptr == o.m_ptr) ? (m_refs
- }
- inline bool operator != (const wp
& o) const { return m_refs != o.m_refs; }
- template
inline bool operator != (const wp& o) const { return !operator == (o); }
- inline bool operator <= (const wp
& o) const { return !operator > (o); }
- template
inline bool operator <= (const wp& o) const { return !operator > (o); }
- inline bool operator >= (const wp
& o) const { return !operator
- template
inline bool operator >= (const wp& o) const { return !operator
-
- private:
- template
friend class sp;
- template
friend class wp;
-
- T* m_ptr;
- weakref_type* m_refs;
- };
強いポインタクラスに比べて、メンバー変数m_が1つあります.ptrはターゲットオブジェクトを指しますが、弱いポインタには追加のメンバー変数m_があります.refs、そのタイプはweakref_typeポインタ、次に弱いポインタの構造関数を分析したとき、初期化された場合を見てみましょう.ここで注目すべきは,依然として弱いポインタの構造関数と構造関数である.
まず、コンストラクション関数を見てみましょう.
- template
- wp
::wp(T* other)
- : m_ptr(other)
- {
- if (other) m_refs = other->createWeak(this);
- }
ここでのパラメータotherは必ずRefBaseクラスを継承するので、ここではframeworks/base/libs/utils/RefBaseで定義RefBaseクラスのcreateWeak関数を呼び出す.cppファイル:
- RefBase::weakref_type* RefBase::createWeak(const void* id) const
- {
- mRefs->incWeak(id);
- return mRefs;
- }
ここでのメンバー変数mRefsのタイプはweakref_implポインタ、weakref_implクラスのincWeak関数は、オブジェクトの弱い参照数を増やす役割を果たしています.関数は最後にmRefsを返し、弱いポインタオブジェクトのメンバー変数m_refsはターゲットオブジェクトを指すweakref_implオブジェクトです.
構造関数を見てみましょう.
- template
- wp
::~wp()
- {
- if (m_ptr) m_refs->decWeak(this);
- }
ここで、弱いポインタは、解析時に強いポインタとは異なり、ターゲットオブジェクトのweakref_を直接呼び出すimplオブジェクトのdecWeak関数により弱参照カウントが減少し、弱参照カウントが0の場合、ターゲットオブジェクトでのフラグビット(0、OBJECT_LIFETIME_WEAKまたはOBJECT_LIFETIME_FOREVER)に基づいてdeleteターゲットオブジェクトを決定しますが、前述したように、ここでは説明しません.
ここまで分析して、弱いポインタはまだ紹介していないで、その最も重要な特性は私達はまだ分析していません.前述したように、弱いポインタの最大の特徴は、ターゲットオブジェクトを直接操作できないことです.これはどのようにして行われていますか.秘密は、弱いポインタクラスに*と->操作記号が再ロードされず、強いポインタにはこの2つの操作記号が再ロードされていることです.しかし、ターゲットオブジェクトを操作するには、どうすればいいのでしょうか.弱いポインタを強いポインタにアップグレードします.
- template
- sp
wp::promote() const
- {
- return sp
(m_ptr, m_refs);
- }
メンバー変数m_を使用してアップグレードptrとm_refsは強いポインタspを構築し,ここでm_ptrはターゲットオブジェクトを指すポインタであり、m_refsはターゲットオブジェクトの中を指すweakref_implオブジェクト.
この強いポインタの構造過程を見てみましょう.
- template
- sp
::sp(T* p, weakref_type* refs)
- : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0)
- {
- }
主に、ターゲットオブジェクトを指すメンバー変数m_を初期化します.ptr、ターゲットオブジェクトがまだ存在する場合、このm_ptrはターゲットオブジェクトを指し、ターゲットオブジェクトが存在しない場合、m_ptrはNULLです.アップグレードが成功するかどうかは、refs->attemptIncStrong関数の戻り結果によって決まります.
- bool RefBase::weakref_type::attemptIncStrong(const void* id)
- {
- incWeak(id);
-
- weakref_impl* const impl = static_cast
(this);
-
- int32_t curCount = impl->mStrong;
- LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow",
- this);
- while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
- if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
- break;
- }
- curCount = impl->mStrong;
- }
-
- if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
- bool allow;
- if (curCount == INITIAL_STRONG_VALUE) {
- // Attempting to acquire first strong reference... this is allowed
- // if the object does NOT have a longer lifetime (meaning the
- // implementation doesn't need to see this), or if the implementation
- // allows it to happen.
- allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
- || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
- } else {
- // Attempting to revive the object... this is allowed
- // if the object DOES have a longer lifetime (so we can safely
- // call the object with only a weak ref) and the implementation
- // allows it to happen.
- allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK
- && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);
- }
- if (!allow) {
- decWeak(id);
- return false;
- }
- curCount = android_atomic_inc(&impl->mStrong);
-
- // If the strong reference count has already been incremented by
- // someone else, the implementor of onIncStrongAttempted() is holding
- // an unneeded reference. So call onLastStrongRef() here to remove it.
- // (No, this is not pretty.) Note that we MUST NOT do this if we
- // are in fact acquiring the first reference.
- if (curCount > 0 && curCount
- impl->mBase->onLastStrongRef(id);
- }
- }
-
- impl->addWeakRef(id);
- impl->addStrongRef(id);
-
- #if PRINT_REFS
- LOGD("attemptIncStrong of %p from %p: cnt=%d
", this, id, curCount);
- #endif
-
- if (curCount == INITIAL_STRONG_VALUE) {
- android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong);
- impl->mBase->onFirstRef();
- }
-
- return true;
- }
この関数の役割は、ターゲットオブジェクトの強い参照カウントを増やそうとすることですが、失敗する可能性があります.失敗した原因は、ターゲットオブジェクトがdeleteによって削除されたか、または他の原因である可能性があります.以下で分析します.先に強いポインタについて説明したように、ターゲットオブジェクトの強い参照カウントを増やすと同時に、ターゲットオブジェクトの弱い参照カウントも増加するので、関数は最初にincWeak関数を呼び出してターゲットオブジェクトの参照カウントを先に増加させ、後でターゲットオブジェクトの強い参照カウントを増加しようとすると失敗した場合、decWeak関数が呼び出され、前のincWeak操作がロールバックされます.
ここで、ターゲットオブジェクトの強い参照カウントを増やそうとすると、ターゲットオブジェクトが他の強いポインタで参照されている場合、すなわち、その強い参照カウントが0より大きく、INITIAL_に等しくない場合に分けて議論される.STRONG_VALUE、もう1つのケースは、ターゲットオブジェクトが強いポインタで参照されていないことです.すなわち、その強い参照数が0以下、またはINITIAL_に等しいことです.STRONG_VALUE.
1つ目のケースは簡単です.この場合、ターゲットオブジェクトが必ず存在することを示すので、この弱いポインタを強いポインタに引き上げることができます.この場合、ターゲットオブジェクトの強い参照カウント値を簡単に増加すればいいです.
- while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {
- if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) {
- break;
- }
- curCount = impl->mStrong;
- }
ここでターゲットオブジェクトの強い参照カウントに対してプラス1操作を実行する場合、原子性を保証するには、他の場所でもこのターゲットオブジェクトの強い参照カウントにプラス1操作を実行している可能性があるため、android_を呼び出すのが一般的です.atomic_inc関数は完了しますが、ここではandroid_を呼び出すことによってatomic_cmpxchg関数で完了、android_atomic_cmpxchg関数はアーキテクチャに関連する関数であり、いくつかの特殊な命令を提供するアーキテクチャ上でandroid_を呼び出す.atomic_cmpxchg関数はandroidを呼び出すよりも1を加算する操作を実行する効率が高い.atomic_inc関数はもっと高いです.関数android_atomic_cmpxchgはsystem/core/include/cutils/atomicである.hファイルで定義されたマクロ:
- int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue,
- volatile int32_t* addr);
-
- #define android_atomic_cmpxchg android_atomic_release_cas
実際に実行される関数はandroidですatomic_release_cas、この関数の動作原理は大体こうです:もしそれが*addr==oldvalueを発見したら、*addr=newvalueの操作を実行して、それから0を返して、さもなくば何もしないで、1を返します.このシナリオでは、oldvalueはcurCountに等しく、newvalueはcurCount+1に等しく、*addr==oldvalueの条件下では、ターゲットオブジェクトに対する強い参照カウント値が1増加することに相当する.どんな場合*addr!=oldvalueは?androidを呼び出すatomic_release_cas関数の前にoldvalueと値はアドレスaddrから読み出され、android_を実行するとatomic_release_cas関数の場合、アドレスaddrを他の場所で操作すると*addrが現れる可能性があります!=oldvalueの場合、他の場所でもターゲットオブジェクトの強い参照カウントが操作されていることを示します.そのため、ここではターゲットオブジェクトの強い参照カウントを増やす操作は実行できません.ターゲットオブジェクトの強い参照カウントを他の場所で操作してから再実行する必要があります.これは、whileループによって実行される理由です.
2つ目のケースは複雑です.この場合、ターゲットオブジェクトがまだ存在するか、存在しない可能性があるので、実際の状況から判断します.ターゲットオブジェクトの強い参照カウント値がINITIAL_に等しい場合STRONG_VALUEは、このターゲットオブジェクトがまだ強いポインタで参照されていないことを示します.この場合、弱いポインタが強いポインタに昇格できる条件は、次のとおりです.
allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK
|| impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id);