“This is the 16th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”
The paper
Android smart pointer, mainly in order to deepen the influence of smart pointer after reading some blogs in detail, and write a summary, or self-understanding (analysis) of an article. Smart Pointers exist for easier management of the life cycle of objects.
Break down
Lightweight pointer
Analysis of implementation Principle
The lightweight pointer class in Android is the LigheRefBase class, which is defined in
system/core/include/utils/LightRefBase.h
template <class T>
class LightRefBase
{
public:
inline LightRefBase(a) : mCount(0) { }
inline void incStrong(__attribute__((unused)) const void* id) const {
mCount.fetch_add(1, std::memory_order_relaxed);
}
inline void decStrong(__attribute__((unused)) const void* id) const {
if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
delete static_cast<const T*>(this); }}/ /! DEBUGGING ONLY: Get current strong ref count.
inline int32_t getStrongCount(a) const {
return mCount.load(std::memory_order_relaxed);
}
typedef LightRefBase<T> basetype;
protected:
inline ~LightRefBase() {}private:
friend class ReferenceMover;
inline static void renameRefs(size_t /*n*/.const ReferenceRenamer& /*renamer*/) {}inline static void renameRefId(T* /*ref*/.const void* /*old_id*/ , const void* /*new_id*/) {}private:
mutable std::atomic<int32_t> mCount;
};
Copy the code
This is a template class that contains a member variable mCount, three member methods incStrong, decStrong, and getStrongCount, where 1. The mCount member variable is int32_t, initialized to 0, and counts the number of references to the current template class 2. The member methods incStrong and decStrong are used as mCount to change the number of references to a template class. The member method getStrongCount completes the count of template class references. Observe the member method decStrong. While reducing the member variable mCount, it will check if mCount is 0 and delete the object directly, so as to achieve resource reclamation
The smart pointer class that works with the reference counter for the lightweight pointer is SP, which is located
/system/core/include/utils/StrongPointer.h
template<typename T>
class sp {
public:
inline sp(a) : m_ptr(nullptr) { }
sp(T* other); // NOLINT(implicit)
sp(const sp<T>& other);
sp(sp<T>&& other) noexcept;
template<typename U> sp(U* other); // NOLINT(implicit)
template<typename U> sp(const sp<U>& other); // NOLINT(implicit)
template<typename U> sp(sp<U>&& other); // NOLINT(implicit)
~sp(a);// Assignment
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
sp& operator=(sp<T>&& other) noexcept;
template<typename U> sp& operator = (const sp<U>& other);
template<typename U> sp& operator = (sp<U>&& other);
template<typename U> sp& operator = (U* other);
/ /! Special optimization for use by ProcessState (and nobody else).
void force_set(T* other);
// Reset
void clear(a);
// Accessors
inline T& operator* () const { return *m_ptr; }
inline T* operator- > ()const { return m_ptr; }
inline T* get(a) const { return m_ptr; }
inline explicit operator bool (a) const { returnm_ptr ! =nullptr; }
// Operators
COMPARE(= =)COMPARE(! =)COMPARE(>)
COMPARE(<)
COMPARE(< =)COMPARE(> =)private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
void set_pointer(T* ptr);
T* m_ptr;
};
Copy the code
The sp member variable m_ptr refers to the template class 2. Identify the sp constructor
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);
}
Copy the code
We know that T always inherits LightRefBase, so m_ptr’s incStrong method, which calls LightRefBase’s incStrong method, Therefore, the destructor of LightRefBase’s mCount member is increased directly
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
Copy the code
In the destructor, it is mainly to confirm that m_ptr is not NULL, call its decStrong method, that is, call the devStrong method of LightRefBase class, so as to reduce the member variable mCount of the template class LightRefBase, and confirm that if mCount is 0, Reclaim template class objects to free resources
Strong and weak Pointers
RefBase base class
define
Before introducing strong and weak Pointers, it’s worth examining the RefBase class, which is the base class for strong and weak Pointers and is located
/system/core/include/utils/RefBase.h
class RefBase
{
public:
void incStrong(const void* id) const;
void decStrong(const void* id) const;
void forceIncStrong(const void* id) const;
/ /! DEBUGGING ONLY: Get current strong ref count.
int32_t getStrongCount(a) const;
class weakref_type {
public:
RefBase* refBase(a) 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);
// acquires a weak reference if there is already one.
// This is not always safe. see ProcessState.cpp and BpBinder.cpp
// for proper use.
bool attemptIncWeak(const void* id);
/ /! DEBUGGING ONLY: Get current weak ref count.
int32_t getWeakCount(a) const;
/ /! DEBUGGING ONLY: Print references held on object.
void printRefs(a) const;
/ /! DEBUGGING ONLY: Enable tracking for this object.
// enable -- enable/disable tracking
// retain -- when tracking is enable, if true, then we save a stack trace
// for each reference and dereference; when retain == false, we
// match up references and dereferences and keep only the
// outstanding ones.
void trackMe(bool enable, bool retain);
};
weakref_type* createWeak(const void* id) const;
weakref_type* getWeakRefs(a) const;
/ /! DEBUGGING ONLY: Print references held on object.
inline void printRefs(a) const {
getWeakRefs() - >printRefs(a); }/ /! DEBUGGING ONLY: Enable tracking of object.
inline void trackMe(bool enable, bool retain) {
getWeakRefs() - >trackMe(enable, retain);
}
typedef RefBase basetype;
protected:
RefBase(a);virtual ~RefBase(a);/ /! Flags for extendObjectLifetime()
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001
};
void extendObjectLifetime(int32_t mode);
/ /! Flags for onIncStrongAttempted()
enum {
FIRST_INC_STRONG = 0x0001
};
// Invoked after creation of initial strong pointer/reference.
virtual void onFirstRef(a);
// Invoked when either the last strong reference goes away, or we need to undo
// the effect of an unnecessary onIncStrongAttempted.
virtual void onLastStrongRef(const void* id);
// Only called in OBJECT_LIFETIME_WEAK case. Returns true if OK to promote to
// strong reference. May have side effects if it returns true.
// The first flags argument is always FIRST_INC_STRONG.
// TODO: Remove initial flag argument.
virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
// Invoked in the OBJECT_LIFETIME_WEAK case when the last reference of either
// kind goes away. Unused.
// TODO: Remove.
virtual void onLastWeakRef(const void* id);
private:
friend class weakref_type;
class weakref_impl;
RefBase(const RefBase& o);
RefBase& operator= (const RefBase& o);
private:
friend class ReferenceMover;
static void renameRefs(size_t n, const ReferenceRenamer& renamer);
static void renameRefId(weakref_type* ref, const void* old_id,
const void* new_id);
static void renameRefId(RefBase* ref, const void* old_id,
const void* new_id);
weakref_impl* const mRefs;
};
Copy the code
Weakref_type class weakRef_type class weakRef_type 2. Contains a member variable of type WeakRef_impl address mRefs, and weakref_impl is defined in
/system/core/libutils/RefBase.cpp
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
explicit weakref_impl(RefBase* base) :
mStrong(INITIAL_STRONG_VALUE), mWeak(0), mBase(base), mFlags(0) {
}
void addStrongRef(const void* /*id*/) {}
void removeStrongRef(const void* /*id*/) {}
void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) {}
void addWeakRef(const void* /*id*/) {}
void removeWeakRef(const void* /*id*/) {}
void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) {}
void printRefs() const {}
void trackMe(bool, bool) {}
#else
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
, mStrongRefs(NULL)
, mWeakRefs(NULL)
, mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
, mRetain(false)
{}
~weakref_impl()
{
bool dumpStack = false;
if (!mRetain && mStrongRefs != NULL) {
dumpStack = true;
ALOGE("Strong references remain:");
ref_entry* refs = mStrongRefs;
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
}
}
if (!mRetain && mWeakRefs != NULL) {
dumpStack = true;
ALOGE("Weak references remain!");
ref_entry* refs = mWeakRefs;
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
}
}
if (dumpStack) {
ALOGE("above errors at:");
CallStack::logStack(LOG_TAG);
}
}
void addStrongRef(const void* id) {
//ALOGD_IF(mTrackEnabled,
// "addStrongRef: RefBase=%p, id=%p", mBase, id);
addRef(&mStrongRefs, id, mStrong.load(std::memory_order_relaxed));
}
void removeStrongRef(const void* id) {
//ALOGD_IF(mTrackEnabled,
// "removeStrongRef: RefBase=%p, id=%p", mBase, id);
if (!mRetain) {
removeRef(&mStrongRefs, id);
} else {
addRef(&mStrongRefs, id, -mStrong.load(std::memory_order_relaxed));
}
}
void renameStrongRefId(const void* old_id, const void* new_id) {
//ALOGD_IF(mTrackEnabled,
// "renameStrongRefId: RefBase=%p, oid=%p, nid=%p",
// mBase, old_id, new_id);
renameRefsId(mStrongRefs, old_id, new_id);
}
void addWeakRef(const void* id) {
addRef(&mWeakRefs, id, mWeak.load(std::memory_order_relaxed));
}
void removeWeakRef(const void* id) {
if (!mRetain) {
removeRef(&mWeakRefs, id);
} else {
addRef(&mWeakRefs, id, -mWeak.load(std::memory_order_relaxed));
}
}
void renameWeakRefId(const void* old_id, const void* new_id) {
renameRefsId(mWeakRefs, old_id, new_id);
}
void trackMe(bool track, bool retain)
{
mTrackEnabled = track;
mRetain = retain;
}
void printRefs() const
{
String8 text;
{
Mutex::Autolock _l(mMutex);
char buf[128];
snprintf(buf, sizeof(buf),
"Strong references on RefBase %p (weakref_type %p):\n",
mBase, this);
text.append(buf);
printRefsLocked(&text, mStrongRefs);
snprintf(buf, sizeof(buf),
"Weak references on RefBase %p (weakref_type %p):\n",
mBase, this);
text.append(buf);
printRefsLocked(&text, mWeakRefs);
}
{
char name[100];
snprintf(name, sizeof(name), DEBUG_REFS_CALLSTACK_PATH "/%p.stack",
this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
if (rc >= 0) {
(void)write(rc, text.string(), text.length());
close(rc);
ALOGD("STACK TRACE for %p saved in %s", this, name);
}
else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this,
name, strerror(errno));
}
}
private:
struct ref_entry
{
ref_entry* next;
const void* id;
#if DEBUG_REFS_CALLSTACK_ENABLED
CallStack::CallStackUPtr stack;
#endif
int32_t ref;
};
void addRef(ref_entry** refs, const void* id, int32_t mRef)
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
ref_entry* ref = new ref_entry;
// Reference count at the time of the snapshot, but before the
// update. Positive value means we increment, negative--we
// decrement the reference count.
ref->ref = mRef;
ref->id = id;
#if DEBUG_REFS_CALLSTACK_ENABLED
ref->stack = CallStack::getCurrent(2);
#endif
ref->next = *refs;
*refs = ref;
}
}
void removeRef(ref_entry** refs, const void* id)
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
ref_entry* const head = *refs;
ref_entry* ref = head;
while (ref != NULL) {
if (ref->id == id) {
*refs = ref->next;
delete ref;
return;
}
refs = &ref->next;
ref = *refs;
}
ALOGE("RefBase: removing id %p on RefBase %p"
"(weakref_type %p) that doesn't exist!",
id, mBase, this);
ref = head;
while (ref) {
char inc = ref->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref);
ref = ref->next;
}
CallStack::logStack(LOG_TAG);
}
}
void renameRefsId(ref_entry* r, const void* old_id, const void* new_id)
{
if (mTrackEnabled) {
AutoMutex _l(mMutex);
ref_entry* ref = r;
while (ref != NULL) {
if (ref->id == old_id) {
ref->id = new_id;
}
ref = ref->next;
}
}
}
void printRefsLocked(String8* out, const ref_entry* refs) const
{
char buf[128];
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
inc, refs->id, refs->ref);
out->append(buf);
#if DEBUG_REFS_CALLSTACK_ENABLED
out->append(CallStack::stackToString("\t\t", refs->stack.get()));
#else
out->append("\t\t(call stacks disabled)");
#endif
refs = refs->next;
}
}
mutable Mutex mMutex;
ref_entry* mStrongRefs;
ref_entry* mWeakRefs;
bool mTrackEnabled;
// Collect stack traces on addref and removeref, instead of deleting the stack references
// on removeref that match the address ones.
bool mRetain;
#endif
};
Copy the code
As you can see, this class inherits the WeakRef_type class defined in refBase.h, and this code looks very complicated, but if analysis finds out
#if! DEBUG_REFS
// Release the branch to which the version goes...#else
// Debug the branch to which the version goes...#endif
Copy the code
As you can see from the Release section, all defined functions are empty implementations, and only some initialization operations are done in constructors. Notice that the RefBase definition contains an enumeration type
enum {
OBJECT_LIFETIME_STRONG = 0x0000,
OBJECT_LIFETIME_WEAK = 0x0001,
OBJECT_LIFETIME_MASK = 0x0001}; OBJECT_LIFETIME_STRONG specifies that the life cycle of an object is only affected by a strong reference count. OBJECT_LIFETIME_WEAK Specifies that the life cycle of an object is only affected by a weak reference count. OBJECT_LIFETIME_MASK is a benchmarkCopy the code
Construction method and destructor method
RefBase constructor
RefBase::RefBase()
: mRefs(new weakref_impl(this))
{}
Copy the code
MRefs is initialized
The destructor
RefBase::~RefBase() { int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed); // Life-time of this object is extended to WEAK, in // which case weakref_impl doesn't out-live the object and we // can free it now. if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { // It's possible that the weak count is not 0 if the object // re-acquired a weak reference in its destructor if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) { delete mRefs; } else if (mRefs-> mstrong.load (STD ::memory_order_relaxed) == INITIAL_STRONG_VALUE) {... } // For debugging purposes, clear mRefs. Ineffective against outstanding wp's. const_cast<weakref_impl*&>(mRefs) = nullptr; }Copy the code
First get Flags, verify that if the object’s lifetime is affected by weak reference counting, the strong reference count must be 0 when weak reference is 0 (since strong reference counting must be less than or equal to weak reference counting), Release the memory occupied by weakRef_IMPl. If the life cycle of the object is not affected by weak reference count (that is, strong reference count is affected), judge the strong reference count as the initialization value, do no operation and finally forcibly set mRefs to NullPTR.
Analysis of implementation principle of strong pointer
The smart pointer class for strong Pointers is also SP, which is located
/system/core/include/utils/StrongPointer.h
template<typename T>
class sp {
public:
inline sp(a) : m_ptr(nullptr) { }
sp(T* other); // NOLINT(implicit)
sp(const sp<T>& other);
sp(sp<T>&& other) noexcept;
template<typename U> sp(U* other); // NOLINT(implicit)
template<typename U> sp(const sp<U>& other); // NOLINT(implicit)
template<typename U> sp(sp<U>&& other); // NOLINT(implicit)
~sp(a);// Assignment
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
sp& operator=(sp<T>&& other) noexcept;
template<typename U> sp& operator = (const sp<U>& other);
template<typename U> sp& operator = (sp<U>&& other);
template<typename U> sp& operator = (U* other);
/ /! Special optimization for use by ProcessState (and nobody else).
void force_set(T* other);
// Reset
void clear(a);
// Accessors
inline T& operator* () const { return *m_ptr; }
inline T* operator- > ()const { return m_ptr; }
inline T* get(a) const { return m_ptr; }
inline explicit operator bool (a) const { returnm_ptr ! =nullptr; }
// Operators
COMPARE(= =)COMPARE(! =)COMPARE(>)
COMPARE(<)
COMPARE(< =)COMPARE(> =)private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
void set_pointer(T* ptr);
T* m_ptr;
};
Copy the code
As mentioned earlier, this is a template class. Its constructor is as follows:
template<typename T>
sp<T>::sp(const sp<T>& other)
: m_ptr(other.m_ptr) {
if (m_ptr)
m_ptr->incStrong(this);
}
Copy the code
Since this m_ptr is now a RefBase class, it calls the incStrong method of the RefBase class
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 __unused = 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(a); }Copy the code
So if you look at it from this side, you increase the weak reference count by calling incWeak and then you call addStrongRef, and as we’ve seen before, in the Release version, this method does nothing and then increases the strong reference count of the object, and then you determine if it’s the first time you’ve entered the constructor, if it’s the first time, The onFirstRef destructor of the onFirstRef method is called for the first time, and it does the following:
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
Copy the code
Again, as analyzed earlier, this method calls the decStrong method of the RefBase class
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
LOG_ALWAYS_FATAL_IF(BAD_STRONG(c), "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;
// The destructor does not delete refs in this case.}}// 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.
//
// Since we're doing atomic loads of `flags`, the static analyzer assumes
// they can change between `delete this; ` and `refs->decWeak(id); `. This is
// not the case. The analyzer may become more okay with this patten when
// https://bugs.llvm.org/show_bug.cgi?id=34365 gets resolved. NOLINTNEXTLINE
refs->decWeak(id);
}
Copy the code
Mostly I did
- First, the removeStrongRef method of WeakRef_IMPl is called. According to the previous analysis, this method does not do any operation in the Release version
- Call the fetch_sub method of the atomic and decrement the strong reference pointer count by one
- When the strong pointer count is exactly 1, when flags is affected by the strong pointer reference count, the object is released directly, that is, the destructor of RefBase is called
- After the above analysis, the destructor of RefBase determines whether the weak pointer count is 1. If it is 1, the mRefs object memory is directly freed
- After that, the weak reference count is reduced by 1
To sum up, the destructor of SP mainly calls the decStrong method of RefBase, which is used as the strong pointer count decreases by 1. Then, when the strong pointer decreases by 1, it becomes 0, and the object is affected by the strong pointer reference count, the object is released, and the weak reference count decreases by 1 thereafter
Analysis of the realization principle of weak pointer
The weakly pointer class, wp, is located
/system/core/include/utils/RefBase.h
template <typename T>
class wp
{
public:
typedef typename RefBase::weakref_type weakref_type;
inline wp(a) : m_ptr(nullptr) { }
wp(T* other); // NOLINT(implicit)
wp(const wp<T>& other);
explicit wp(const sp<T>& other);
template<typename U> wp(U* other); // NOLINT(implicit)
template<typename U> wp(const sp<U>& other); // NOLINT(implicit)
template<typename U> wp(const wp<U>& other); // NOLINT(implicit)
~wp(a);// Assignment
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(a) const;
// Reset
void clear(a);
// Accessors
inline weakref_type* get_refs(a) const { return m_refs; }
inline T* unsafe_get(a) 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 { returnm_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 sp;
template<typename Y> friend class wp;
T* m_ptr;
weakref_type* m_refs;
};
Copy the code
From the above class definition, the weak pointer reference class WP is also a template class. The constructor
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
if (other) m_refs = other->createWeak(this);
}
Copy the code
This function calls the createWeak method of the template T class, which must inherit from the RefBase class, i.e., the createWeak method of the RefBase class is called
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id);
return mRefs;
}
Copy the code
Weakref_impl object (mRefs) of The RefBase weakRef_impL object
The destructor
template<typename T>
wp<T>::~wp()
{
if (m_ptr) m_refs->decWeak(this);
}
Copy the code
Well, the decWeak function of mRefs is called to reduce the weak reference count
Weak pointer Upgrades strong pointer reference
A weak pointer cannot operate on a referenced object directly. Therefore, if you need to operate on a referenced object, you must upgrade a weak reference pointer to a strong reference pointer. Therefore, wp provides a promote method to upgrade a weak reference count to a strong reference count
template<typename T>
sp<T> wp<T>::promote(a)const {
sp<T> result;
if (m_ptr && m_refs->attemptIncStrong(&result)) {
result.set_pointer(m_ptr);
}
returnresult; } m_ptr is the actual type of the object, which is initialized in the constructor, and then m_refs is a WeakRef_IMPl object, which is initialized in the constructor to return the mRefs object as RefBase, sobool 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);
ALOG_ASSERT(curCount >= 0."attemptIncStrong called on %p after underflow".this);
while (curCount > 0&& curCount ! = INITIAL_STRONG_VALUE) {// we're in the easy/common case of promoting a weak-reference
// from an existing strong reference.
if (impl->mStrong.compare_exchange_weak(curCount, curCount + 1,
std::memory_order_relaxed)) {
break;
}
// the strong count has changed on us, we need to re-assert our
// situation. curCount was updated by compare_exchange_weak.
}
if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {
// we're now in the harder case of either:
// - there never was a strong reference on us
// - or, all strong references have been released
int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
// this object has a "normal" life-time, i.e.: it gets destroyed
// when the last strong reference goes away
if (curCount <= 0) {
// the last strong-reference got released, the object cannot
// be revived.
decWeak(id);
return false;
}
// here, curCount == INITIAL_STRONG_VALUE, which means
// there never was a strong-reference, so we can try to
// promote this object; we need to do that atomically.
while (curCount > 0) {
if (impl->mStrong.compare_exchange_weak(curCount, curCount + 1,
std::memory_order_relaxed)) {
break;
}
// the strong count has changed on us, we need to re-assert our
// situation (e.g.: another thread has inc/decStrong'ed us)
// curCount has been updated.
}
if (curCount <= 0) {
// promote() failed, some other thread destroyed us in the
// meantime (i.e.: strong count reached zero).
decWeak(id);
return false; }}else {
// this object has an "extended" life-time, i.e.: it can be
// revived from a weak-reference only.
// Ask the object's implementation if it agrees to be revived
if(! impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {
// it didn't so give-up.
decWeak(id);
return false;
}
// grab a strong-reference, which is always safe due to the
// extended life-time.
curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed);
// 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 ! = INITIAL_STRONG_VALUE) { impl->mBase->onLastStrongRef(id);
}
}
}
impl->addStrongRef(id);
// curCount is the value of mStrong before we incremented it.
// Now we need to fix-up the count if it was INITIAL_STRONG_VALUE.
// This must be done safely, i.e.: handle the case where several threads
// were here in attemptIncStrong().
// curCount > INITIAL_STRONG_VALUE is OK, and can happen if we're doing
// this in the middle of another incStrong. The subtraction is handled
// by the thread that started with INITIAL_STRONG_VALUE.
if (curCount == INITIAL_STRONG_VALUE) {
impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
}
return true;
}
Copy the code
- First, we convert this pointer to a Weakref_impl pointer and save it in the IMPL
- Next, get the value of mStrong and store it in curCount
- Check whether the current strong pointer count is greater than 0 and not equal to INITIAL_STRONG_VALUE, which indicates that another object is using the strong reference pointer, and increment the strong reference count by 1. In this case, the following if statement will not be true and will be skipped. The addStrongRef method will then be called directly, which, according to the previous analysis, does nothing in the Release version.
- If the if statement in article 3 above is true, it is a different case, that is, the object is never referred to by a strong reference count, and will enter the if statement.
There are two ways this could happen,
- If the strong reference count is less than or equal to 0, that is, the strong reference count has been destroyed, weak reference Pointers cannot be upgraded to strong Pointers, and false is returned. When curCount is greater than 0, that is, curCount is INITIAL_STRONG_VALUE, in this case, the strong reference pointer needs to be increased by 1, which indicates that the weak reference pointer can be upgraded to the strong reference pointer, and then determine whether it is destroyed by other threads. If it is destroyed, the upgrade fails; otherwise, the upgrade succeeds.
- When the life cycle of an object is not affected by strong references, it indicates that it can be upgraded directly, increasing the strong reference pointer count by one
Next, the set_pointer method is called if the strong reference pointer count is successfully incremented by one, that is, the weak reference pointer was successfully upgraded to a strong reference pointer
template<typename T>
void sp<T>::set_pointer(T* ptr) {
m_ptr = ptr;
}
Copy the code
That is, the direct assignment operation returns the object corresponding to the actual type of the template parameter
Application Case Analysis
extension
In c + + explicit
Explicit is a C++ keyword that can only be used with constructors that take one argument. Explicit is a pair of keywords with implicit, which stands for an implicit constructor
Atomic (fence_add, fence_sub, load, store, compare_exchange_weak) in C++
Fence_add method
The prototype
T fetch_add (T val, memory_order sync = memory_order_seq_cst) volatile ---- T fetch_add (ptrdiff_t val, Memory_order sync = memory_order_seq_cst) volatile ---- the parameter val is a pointerCopy the code
meaning
The atomic type of the C++ 11 standard library is mainly to add the encapsulated value of the atomic object to val, and return the old value of the atomic object, the whole process is atomic.
parameter
Val ---- Of the two constructors, the first represents the template parameter of the atom type, and the second represents the synchronization mode of the atom operation of the pointer type sync ----, which is an enumeration type memory_ORDER value. If not specified, the default is memory_order_seq_cstCopy the code
The return value
The template type of the corresponding atom object
Fence_sub method
The prototype
T fetch_sub (T val, memory_order sync = memory_order_seq_cst) volatile ---- T fetch_sub (ptrdiff_t val, Memory_order sync = memory_order_seq_cst) volatile ---- the parameter val is a pointerCopy the code
meaning
The atomic type of the C++ 11 standard library is mainly to reduce the encapsulated value of the atomic object by val, and return the old value of the atomic object, the whole process is atomic
parameter
Val ---- Of the two constructors, the first represents the template parameter of the atom type, and the second represents the synchronization mode of the atom operation of the pointer type sync ----, which is an enumeration type memory_ORDER value. If not specified, the default is memory_order_seq_cstCopy the code
The return value
The template type of the corresponding atom object
The load method
The prototype
T load (memory_order sync = memory_order_seq_cst) const volatile;
Copy the code
meaning
Loads the value of the corresponding atomic object
parameter
Sync ---- Synchronization mode for atomic operations, which is an enumerated type memory_ORDER value. If not specified, the default is memory_order_seq_cstCopy the code
The return value
The template type of the corresponding atom object
Store method
The prototype
void store (T val, memory_order sync = memory_order_seq_cst) volatile
Copy the code
meaning
Stores the template type val of the corresponding atomic object into the atomic object
parameter
Sync ---- Synchronization mode for atomic operations, which is an enumerated type memory_ORDER value. If not specified, the default is memory_order_seq_cstCopy the code
Return value (void)
Compare_exchange_weak method and compare_exchange_strong method
The prototype
bool compare_exchange_weak (T& expected, T val, memory_order sync = memory_order_seq_cst) volatile;
bool compare_exchange_weak (T& expected, T val, memory_order success, memory_order failure) volatile;
bool compare_exchange_strong (T& expected, T val, memory_order sync = memory_order_seq_cst) volatile;
bool compare_exchange_strong (T& expected, T val, memory_order success, memory_order failure) volatile;
Copy the code
meaning
Compare the object value to the Expected value
The implication of the two memory_order methods is to use the memory_order of SUCCESS when the result is true and failure when the result is failure
The weak form allows false failures. This function directly compares the encapsulated value of the atomic object to the expected physical content of the argument. Therefore, in some cases, the comparison of objects is equal using operator==(), but compare_exchange_weak may fail. This is because there may be bit-alignment or other values in the underlying physical content of the object that logically represent the same but physically represent different values (such as true and 2 or 3, both of which logically mean “true” but physically do not mean the same). False can be returned (same as expected). If this atomic T value is the same as expected, replace this atomic T value with val, and return true. If not, replace the expected with this atomic’s T value and return false.
Unlike compare_exchange_weak, the strong version of the compare-and-exchange operation does not allow (spuriously) to return false, that is, the value encapsulated by the atomic object is the same as the physical content of the argument expected, The comparison operation must be true. However, on some platforms, compare_exchange_weak performs better if the algorithm itself requires a loop operation to check. Therefore, for some algorithms that do not require a loop operation, compare_exchange_strong is usually better
parameter
T indicates the template type of the atom object, and others are described as aboveCopy the code
The return value
If the comparison returns true, replace the object value with the value of val. If the comparison returns false, replace the object value with the expectedCopy the code
The value and meaning of the enumeration type memory_ORDER
The definition of memory_order
typedef enum memory_order {
memory_order_relaxed, // relaxed
memory_order_consume, // consume
memory_order_acquire, // acquire
memory_order_release, // release
memory_order_acq_rel, // acquire/release
memory_order_seq_cst // sequentially consistent
} memory_order;
Copy the code
The meaning of each enumerated value
Memory_order_relaxed implies that atomic operations in the same thread are ordered when manipulating atomic types, whereas memory_order_consume is not ordered when manipulating atomic types in different threads. The memory_order_acquire and memory_order_RELEASE values are a pair of values, Follow the order of release-acquire MemorY_ORDER_ACq_rel MemorY_ORDER_seq_CST indicates that when manipulating atomic types, operations are performed in the exact order in which the program is run, with only one atom operating at a time. Sacrifice optimization efficiency to ensure consistency of instruction order. For example, for store and Load methods, first confirm that each thread operates in its own order, and then ensure that the Load method operates after the store method is called in different threadsCopy the code
Reference documentation
Summary of C++11’s six memory sequences
The realization principle analysis of intelligent pointer (lightweight pointer, strong pointer and weak pointer) of Android system
fetch_add
fetch_sub
store
load
compare_exchange_weak
compare_exchange_strong
Using C++11 to implement a lock-free stack