| /* |
| * Copyright 2006 The Android Open Source Project |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef SkRefCnt_DEFINED |
| #define SkRefCnt_DEFINED |
| |
| #include "include/core/SkTypes.h" |
| |
| #include <atomic> // std::atomic, std::memory_order_* |
| #include <cstddef> // std::nullptr_t |
| #include <iosfwd> // std::basic_ostream |
| #include <memory> // TODO: unused |
| #include <type_traits> // std::enable_if, std::is_convertible |
| #include <utility> // std::forward, std::swap |
| |
| /** \class SkRefCntBase |
| |
| SkRefCntBase is the base class for objects that may be shared by multiple |
| objects. When an existing owner wants to share a reference, it calls ref(). |
| When an owner wants to release its reference, it calls unref(). When the |
| shared object's reference count goes to zero as the result of an unref() |
| call, its (virtual) destructor is called. It is an error for the |
| destructor to be called explicitly (or via the object going out of scope on |
| the stack or calling delete) if getRefCnt() > 1. |
| */ |
| class SK_API SkRefCntBase { |
| public: |
| /** Default construct, initializing the reference count to 1. |
| */ |
| SkRefCntBase() : fRefCnt(1) {} |
| |
| /** Destruct, asserting that the reference count is 1. |
| */ |
| virtual ~SkRefCntBase() { |
| #ifdef SK_DEBUG |
| SkASSERTF(this->getRefCnt() == 1, "fRefCnt was %d", this->getRefCnt()); |
| // illegal value, to catch us if we reuse after delete |
| fRefCnt.store(0, std::memory_order_relaxed); |
| #endif |
| } |
| |
| /** May return true if the caller is the only owner. |
| * Ensures that all previous owner's actions are complete. |
| */ |
| bool unique() const { |
| if (1 == fRefCnt.load(std::memory_order_acquire)) { |
| // The acquire barrier is only really needed if we return true. It |
| // prevents code conditioned on the result of unique() from running |
| // until previous owners are all totally done calling unref(). |
| return true; |
| } |
| return false; |
| } |
| |
| /** Increment the reference count. Must be balanced by a call to unref(). |
| */ |
| void ref() const { |
| SkASSERT(this->getRefCnt() > 0); |
| // No barrier required. |
| (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed); |
| } |
| |
| /** Decrement the reference count. If the reference count is 1 before the |
| decrement, then delete the object. Note that if this is the case, then |
| the object needs to have been allocated via new, and not on the stack. |
| */ |
| void unref() const { |
| SkASSERT(this->getRefCnt() > 0); |
| // A release here acts in place of all releases we "should" have been doing in ref(). |
| if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) { |
| // Like unique(), the acquire is only needed on success, to make sure |
| // code in internal_dispose() doesn't happen before the decrement. |
| this->internal_dispose(); |
| } |
| } |
| |
| private: |
| |
| #ifdef SK_DEBUG |
| /** Return the reference count. Use only for debugging. */ |
| int32_t getRefCnt() const { |
| return fRefCnt.load(std::memory_order_relaxed); |
| } |
| #endif |
| |
| /** |
| * Called when the ref count goes to 0. |
| */ |
| virtual void internal_dispose() const { |
| #ifdef SK_DEBUG |
| SkASSERT(0 == this->getRefCnt()); |
| fRefCnt.store(1, std::memory_order_relaxed); |
| #endif |
| delete this; |
| } |
| |
| // The following friends are those which override internal_dispose() |
| // and conditionally call SkRefCnt::internal_dispose(). |
| friend class SkWeakRefCnt; |
| |
| mutable std::atomic<int32_t> fRefCnt; |
| |
| SkRefCntBase(SkRefCntBase&&) = delete; |
| SkRefCntBase(const SkRefCntBase&) = delete; |
| SkRefCntBase& operator=(SkRefCntBase&&) = delete; |
| SkRefCntBase& operator=(const SkRefCntBase&) = delete; |
| }; |
| |
| #ifdef SK_REF_CNT_MIXIN_INCLUDE |
| // It is the responsibility of the following include to define the type SkRefCnt. |
| // This SkRefCnt should normally derive from SkRefCntBase. |
| #include SK_REF_CNT_MIXIN_INCLUDE |
| #else |
| class SK_API SkRefCnt : public SkRefCntBase { |
| // "#include SK_REF_CNT_MIXIN_INCLUDE" doesn't work with this build system. |
| #if defined(SK_BUILD_FOR_GOOGLE3) |
| public: |
| void deref() const { this->unref(); } |
| #endif |
| }; |
| #endif |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| /** Call obj->ref() and return obj. The obj must not be nullptr. |
| */ |
| template <typename T> static inline T* SkRef(T* obj) { |
| SkASSERT(obj); |
| obj->ref(); |
| return obj; |
| } |
| |
| /** Check if the argument is non-null, and if so, call obj->ref() and return obj. |
| */ |
| template <typename T> static inline T* SkSafeRef(T* obj) { |
| if (obj) { |
| obj->ref(); |
| } |
| return obj; |
| } |
| |
| /** Check if the argument is non-null, and if so, call obj->unref() |
| */ |
| template <typename T> static inline void SkSafeUnref(T* obj) { |
| if (obj) { |
| obj->unref(); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| // This is a variant of SkRefCnt that's Not Virtual, so weighs 4 bytes instead of 8 or 16. |
| // There's only benefit to using this if the deriving class does not otherwise need a vtable. |
| template <typename Derived> |
| class SkNVRefCnt { |
| public: |
| SkNVRefCnt() : fRefCnt(1) {} |
| ~SkNVRefCnt() { |
| #ifdef SK_DEBUG |
| int rc = fRefCnt.load(std::memory_order_relaxed); |
| SkASSERTF(rc == 1, "NVRefCnt was %d", rc); |
| #endif |
| } |
| |
| // Implementation is pretty much the same as SkRefCntBase. All required barriers are the same: |
| // - unique() needs acquire when it returns true, and no barrier if it returns false; |
| // - ref() doesn't need any barrier; |
| // - unref() needs a release barrier, and an acquire if it's going to call delete. |
| |
| bool unique() const { return 1 == fRefCnt.load(std::memory_order_acquire); } |
| void ref() const { (void)fRefCnt.fetch_add(+1, std::memory_order_relaxed); } |
| void unref() const { |
| if (1 == fRefCnt.fetch_add(-1, std::memory_order_acq_rel)) { |
| // restore the 1 for our destructor's assert |
| SkDEBUGCODE(fRefCnt.store(1, std::memory_order_relaxed)); |
| delete (const Derived*)this; |
| } |
| } |
| void deref() const { this->unref(); } |
| |
| // This must be used with caution. It is only valid to call this when 'threadIsolatedTestCnt' |
| // refs are known to be isolated to the current thread. That is, it is known that there are at |
| // least 'threadIsolatedTestCnt' refs for which no other thread may make a balancing unref() |
| // call. Assuming the contract is followed, if this returns false then no other thread has |
| // ownership of this. If it returns true then another thread *may* have ownership. |
| bool refCntGreaterThan(int32_t threadIsolatedTestCnt) const { |
| int cnt = fRefCnt.load(std::memory_order_acquire); |
| // If this fails then the above contract has been violated. |
| SkASSERT(cnt >= threadIsolatedTestCnt); |
| return cnt > threadIsolatedTestCnt; |
| } |
| |
| private: |
| mutable std::atomic<int32_t> fRefCnt; |
| |
| SkNVRefCnt(SkNVRefCnt&&) = delete; |
| SkNVRefCnt(const SkNVRefCnt&) = delete; |
| SkNVRefCnt& operator=(SkNVRefCnt&&) = delete; |
| SkNVRefCnt& operator=(const SkNVRefCnt&) = delete; |
| }; |
| |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Shared pointer class to wrap classes that support a ref()/unref() interface. |
| * |
| * This can be used for classes inheriting from SkRefCnt, but it also works for other |
| * classes that match the interface, but have different internal choices: e.g. the hosted class |
| * may have its ref/unref be thread-safe, but that is not assumed/imposed by sk_sp. |
| */ |
| template <typename T> class sk_sp { |
| public: |
| using element_type = T; |
| |
| constexpr sk_sp() : fPtr(nullptr) {} |
| constexpr sk_sp(std::nullptr_t) : fPtr(nullptr) {} |
| |
| /** |
| * Shares the underlying object by calling ref(), so that both the argument and the newly |
| * created sk_sp both have a reference to it. |
| */ |
| sk_sp(const sk_sp<T>& that) : fPtr(SkSafeRef(that.get())) {} |
| template <typename U, |
| typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type> |
| sk_sp(const sk_sp<U>& that) : fPtr(SkSafeRef(that.get())) {} |
| |
| /** |
| * Move the underlying object from the argument to the newly created sk_sp. Afterwards only |
| * the new sk_sp will have a reference to the object, and the argument will point to null. |
| * No call to ref() or unref() will be made. |
| */ |
| sk_sp(sk_sp<T>&& that) : fPtr(that.release()) {} |
| template <typename U, |
| typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type> |
| sk_sp(sk_sp<U>&& that) : fPtr(that.release()) {} |
| |
| /** |
| * Adopt the bare pointer into the newly created sk_sp. |
| * No call to ref() or unref() will be made. |
| */ |
| explicit sk_sp(T* obj) : fPtr(obj) {} |
| |
| /** |
| * Calls unref() on the underlying object pointer. |
| */ |
| ~sk_sp() { |
| SkSafeUnref(fPtr); |
| SkDEBUGCODE(fPtr = nullptr); |
| } |
| |
| sk_sp<T>& operator=(std::nullptr_t) { this->reset(); return *this; } |
| |
| /** |
| * Shares the underlying object referenced by the argument by calling ref() on it. If this |
| * sk_sp previously had a reference to an object (i.e. not null) it will call unref() on that |
| * object. |
| */ |
| sk_sp<T>& operator=(const sk_sp<T>& that) { |
| if (this != &that) { |
| this->reset(SkSafeRef(that.get())); |
| } |
| return *this; |
| } |
| template <typename U, |
| typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type> |
| sk_sp<T>& operator=(const sk_sp<U>& that) { |
| this->reset(SkSafeRef(that.get())); |
| return *this; |
| } |
| |
| /** |
| * Move the underlying object from the argument to the sk_sp. If the sk_sp previously held |
| * a reference to another object, unref() will be called on that object. No call to ref() |
| * will be made. |
| */ |
| sk_sp<T>& operator=(sk_sp<T>&& that) { |
| this->reset(that.release()); |
| return *this; |
| } |
| template <typename U, |
| typename = typename std::enable_if<std::is_convertible<U*, T*>::value>::type> |
| sk_sp<T>& operator=(sk_sp<U>&& that) { |
| this->reset(that.release()); |
| return *this; |
| } |
| |
| T& operator*() const { |
| SkASSERT(this->get() != nullptr); |
| return *this->get(); |
| } |
| |
| explicit operator bool() const { return this->get() != nullptr; } |
| |
| T* get() const { return fPtr; } |
| T* operator->() const { return fPtr; } |
| |
| /** |
| * Adopt the new bare pointer, and call unref() on any previously held object (if not null). |
| * No call to ref() will be made. |
| */ |
| void reset(T* ptr = nullptr) { |
| // Calling fPtr->unref() may call this->~() or this->reset(T*). |
| // http://wg21.cmeerw.net/lwg/issue998 |
| // http://wg21.cmeerw.net/lwg/issue2262 |
| T* oldPtr = fPtr; |
| fPtr = ptr; |
| SkSafeUnref(oldPtr); |
| } |
| |
| /** |
| * Return the bare pointer, and set the internal object pointer to nullptr. |
| * The caller must assume ownership of the object, and manage its reference count directly. |
| * No call to unref() will be made. |
| */ |
| T* SK_WARN_UNUSED_RESULT release() { |
| T* ptr = fPtr; |
| fPtr = nullptr; |
| return ptr; |
| } |
| |
| void swap(sk_sp<T>& that) /*noexcept*/ { |
| using std::swap; |
| swap(fPtr, that.fPtr); |
| } |
| |
| private: |
| T* fPtr; |
| }; |
| |
| template <typename T> inline void swap(sk_sp<T>& a, sk_sp<T>& b) /*noexcept*/ { |
| a.swap(b); |
| } |
| |
| template <typename T, typename U> inline bool operator==(const sk_sp<T>& a, const sk_sp<U>& b) { |
| return a.get() == b.get(); |
| } |
| template <typename T> inline bool operator==(const sk_sp<T>& a, std::nullptr_t) /*noexcept*/ { |
| return !a; |
| } |
| template <typename T> inline bool operator==(std::nullptr_t, const sk_sp<T>& b) /*noexcept*/ { |
| return !b; |
| } |
| |
| template <typename T, typename U> inline bool operator!=(const sk_sp<T>& a, const sk_sp<U>& b) { |
| return a.get() != b.get(); |
| } |
| template <typename T> inline bool operator!=(const sk_sp<T>& a, std::nullptr_t) /*noexcept*/ { |
| return static_cast<bool>(a); |
| } |
| template <typename T> inline bool operator!=(std::nullptr_t, const sk_sp<T>& b) /*noexcept*/ { |
| return static_cast<bool>(b); |
| } |
| |
| template <typename C, typename CT, typename T> |
| auto operator<<(std::basic_ostream<C, CT>& os, const sk_sp<T>& sp) -> decltype(os << sp.get()) { |
| return os << sp.get(); |
| } |
| |
| template <typename T, typename... Args> |
| sk_sp<T> sk_make_sp(Args&&... args) { |
| return sk_sp<T>(new T(std::forward<Args>(args)...)); |
| } |
| |
| /* |
| * Returns a sk_sp wrapping the provided ptr AND calls ref on it (if not null). |
| * |
| * This is different than the semantics of the constructor for sk_sp, which just wraps the ptr, |
| * effectively "adopting" it. |
| */ |
| template <typename T> sk_sp<T> sk_ref_sp(T* obj) { |
| return sk_sp<T>(SkSafeRef(obj)); |
| } |
| |
| template <typename T> sk_sp<T> sk_ref_sp(const T* obj) { |
| return sk_sp<T>(const_cast<T*>(SkSafeRef(obj))); |
| } |
| |
| #endif |