Line data Source code
1 : #pragma once 2 : 3 : #include <atomic> 4 : 5 : #include "envoy/common/pure.h" 6 : 7 : #include "source/common/common/assert.h" 8 : 9 : namespace Envoy { 10 : namespace Stats { 11 : 12 : // Implements a reference-counted pointer to a class, so that its external usage 13 : // model is identical to std::shared_ptr, but the reference count itself is held 14 : // in the class. The class is expected to implement three methods: 15 : // void incRefCount() 16 : // bool decRefCount() -- returns true if the reference count goes to zero. 17 : // uint32_t use_count() 18 : // It may implement them by delegating to RefcountHelper (see below), or by 19 : // inheriting from RefcountInterface (see below). 20 : // 21 : // TODO(jmarantz): replace this with an absl or std implementation when 22 : // available. See https://github.com/abseil/abseil-cpp/issues/344, issued June 23 : // 26, 2019, and http://wg21.link/p0406, issued 2017. Note that the turnaround 24 : // time for getting an absl API added is measurable in months, and for a std 25 : // API in years. 26 : template <class T> class RefcountPtr { 27 : public: 28 53994 : RefcountPtr() : ptr_(nullptr) {} 29 : 30 : // Constructing a reference-counted object from a pointer; this is safe to 31 : // do when the reference-count is held in the object. For example, this code 32 : // crashes: 33 : // { 34 : // std::shared_ptr<std::string> a = std::make_shared<std::string>("x"); 35 : // std::shared_ptr<std::string> b(a.get()); 36 : // } 37 : // whereas the analogous code for RefcountPtr works fine. 38 1686261 : RefcountPtr(T* ptr) : ptr_(ptr) { 39 1686261 : if (ptr_ != nullptr) { 40 1686261 : ptr_->incRefCount(); 41 1686261 : } 42 1686261 : } 43 : 44 306885 : RefcountPtr(const RefcountPtr& src) : RefcountPtr(src.get()) {} 45 : 46 : // Constructor for up-casting reference-counted pointers. This doesn't change 47 : // the underlying object; it just upcasts the DerivedClass* in src.ptr_ to a 48 : // BaseClass* for assignment to this->ptr_. For usage of this to compile, 49 : // DerivedClass* must be assignable to BaseClass* without explicit casts. 50 : template <class DerivedClass> 51 1267 : RefcountPtr(const RefcountPtr<DerivedClass>& src) : RefcountPtr(src.get()) {} 52 : 53 : // Move-construction is used by absl::flat_hash_map during resizes. 54 1734248 : RefcountPtr(RefcountPtr&& src) noexcept : ptr_(src.ptr_) { src.ptr_ = nullptr; } 55 : 56 52335 : RefcountPtr& operator=(const RefcountPtr& src) { 57 52335 : if (&src != this && src.ptr_ != ptr_) { 58 52335 : resetInternal(); 59 52335 : ptr_ = src.get(); 60 52335 : if (ptr_ != nullptr) { 61 52335 : ptr_->incRefCount(); 62 52335 : } 63 52335 : } 64 52335 : return *this; 65 52335 : } 66 : 67 : // Move-assignment is used during std::vector resizes. 68 2341 : RefcountPtr& operator=(RefcountPtr&& src) noexcept { 69 2341 : if (&src != this && src.ptr_ != ptr_) { 70 1659 : resetInternal(); 71 1659 : ptr_ = src.ptr_; 72 1659 : src.ptr_ = nullptr; 73 1659 : } 74 2341 : return *this; 75 2341 : } 76 : 77 3474501 : ~RefcountPtr() { resetInternal(); } 78 : 79 : // Implements required subset of the shared_ptr API; 80 : // see https://en.cppreference.com/w/cpp/memory/shared_ptr for details. 81 931967 : T* operator->() const { return ptr_; } 82 689663 : T* get() const { return ptr_; } 83 409305 : T& operator*() const { return *ptr_; } 84 0 : operator bool() const { return ptr_ != nullptr; } 85 1814 : bool operator==(const T* ptr) const { return ptr_ == ptr; } 86 : // In C++20, operator!= can be defaulted and returns !(x == y) or !(y == x). 87 : // Defining it prevents the compiler from considering the reversed-operand 88 : // versions of equality comparisons, so leave it to be defaulted. 89 : #if __cplusplus < 202002L 90 4025 : bool operator!=(const T* ptr) const { return ptr_ != ptr; } 91 : #endif 92 : bool operator==(const RefcountPtr& a) const { return ptr_ == a.ptr_; } 93 : bool operator!=(const RefcountPtr& a) const { return ptr_ != a.ptr_; } 94 : // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete) 95 : uint32_t use_count() const { return ptr_->use_count(); } 96 : void reset() { 97 : resetInternal(); 98 : ptr_ = nullptr; 99 : } 100 : 101 : private: 102 : // Like reset() but does not bother to clear ptr_, as it is about to be 103 : // overwritten or destroyed. 104 3528488 : void resetInternal() { 105 3528488 : if (ptr_ != nullptr && ptr_->decRefCount()) { 106 393241 : delete ptr_; 107 393241 : } 108 3528488 : } 109 : 110 : T* ptr_; 111 : }; 112 : 113 : // In C++20, reversed-operand versions of comparison operators are considered 114 : // during overload resolution, so these functions are no longer necessary and 115 : // cause segfaults due to attempting to infinitely reverse the operands back and 116 : // forth. 117 : #if __cplusplus < 202002L 118 : template <class T> static bool operator==(std::nullptr_t, const RefcountPtr<T>& a) { 119 : return a == nullptr; // NOLINT(clang-analyzer-cplusplus.Move) 120 : } 121 : template <class T> static bool operator!=(std::nullptr_t, const RefcountPtr<T>& a) { 122 : return a != nullptr; 123 : } 124 : #endif 125 : 126 : // Helper interface for classes to derive from, enabling implementation of the 127 : // three methods as part of derived classes. It is not necessary to inherit from 128 : // this interface to wrap a class in RefcountPtr; instead the class can just 129 : // implement the same method. 130 : class RefcountInterface { 131 : public: 132 433485 : virtual ~RefcountInterface() = default; 133 : 134 : /** 135 : * Increments the reference count. 136 : */ 137 : virtual void incRefCount() PURE; 138 : 139 : /** 140 : * Decrements the reference count. 141 : * @return true if the reference count has gone to zero, so the object should be freed. 142 : */ 143 : virtual bool decRefCount() PURE; 144 : 145 : /** 146 : * @return the number of references to the object. 147 : */ 148 : virtual uint32_t use_count() const PURE; 149 : }; 150 : 151 : // Delegation helper for RefcountPtr. This can be instantiated in a class, but 152 : // explicit delegation will be needed for each of the three methods. 153 : struct RefcountHelper { 154 : // Implements the RefcountInterface API. 155 85119 : void incRefCount() { 156 0 : // Note: The ++ref_count_ here and --ref_count_ below have implicit memory 157 0 : // orderings of sequentially consistent. Relaxed on addition and 158 0 : // acquire/release on subtraction is the typical use for reference 159 0 : // counting. On x86, the difference in instruction count is minimal, but the 160 0 : // savings are greater on other platforms. 161 0 : // 162 0 : // https://www.boost.org/doc/libs/1_69_0/doc/html/atomic/usage_examples.html#boost_atomic.usage_examples.example_reference_counters 163 85119 : ++ref_count_; 164 85119 : } 165 85119 : bool decRefCount() { 166 85119 : ASSERT(ref_count_ >= 1); 167 85119 : return --ref_count_ == 0; 168 85119 : } 169 0 : uint32_t use_count() const { return ref_count_; } 170 : 171 : std::atomic<uint32_t> ref_count_{0}; 172 : }; 173 : 174 : } // namespace Stats 175 : } // namespace Envoy