LCOV - code coverage report
Current view: top level - envoy/stats - refcount_ptr.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 46 55 83.6 %
Date: 2024-01-05 06:35:25 Functions: 84 99 84.8 %

          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

Generated by: LCOV version 1.15