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
5713476
  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
156452348
  RefcountPtr(T* ptr) : ptr_(ptr) {
39
156452349
    if (ptr_ != nullptr) {
40
156452125
      ptr_->incRefCount();
41
156452125
    }
42
156452348
  }
43

            
44
28028560
  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
158625
  RefcountPtr(const RefcountPtr<DerivedClass>& src) : RefcountPtr(src.get()) {}
52

            
53
  // Move-construction is used by absl::flat_hash_map during resizes.
54
135553178
  RefcountPtr(RefcountPtr&& src) noexcept : ptr_(src.ptr_) { src.ptr_ = nullptr; }
55

            
56
5663431
  RefcountPtr& operator=(const RefcountPtr& src) {
57
5663431
    if (&src != this && src.ptr_ != ptr_) {
58
5481219
      resetInternal();
59
5481219
      ptr_ = src.get();
60
5481219
      if (ptr_ != nullptr) {
61
5481219
        ptr_->incRefCount();
62
5481219
      }
63
5481219
    }
64
5663431
    return *this;
65
5663431
  }
66

            
67
  // Move-assignment is used during std::vector resizes.
68
390997
  RefcountPtr& operator=(RefcountPtr&& src) noexcept {
69
390997
    if (&src != this && src.ptr_ != ptr_) {
70
200807
      resetInternal();
71
200807
      ptr_ = src.ptr_;
72
200807
      src.ptr_ = nullptr;
73
200807
    }
74
390997
    return *this;
75
390997
  }
76

            
77
297717626
  ~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
90139957
  T* operator->() const { return ptr_; }
82
63530957
  T* get() const { return ptr_; }
83
33487084
  T& operator*() const { return *ptr_; }
84
32644
  operator bool() const { return ptr_ != nullptr; }
85
722141
  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
  bool operator!=(const T* ptr) const { return ptr_ != ptr; }
91
#endif
92
6
  bool operator==(const RefcountPtr& a) const { return ptr_ == a.ptr_; }
93
3
  bool operator!=(const RefcountPtr& a) const { return ptr_ != a.ptr_; }
94
  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
95
24
  uint32_t use_count() const { return ptr_->use_count(); }
96
2
  void reset() {
97
2
    resetInternal();
98
2
    ptr_ = nullptr;
99
2
  }
100

            
101
private:
102
  // Like reset() but does not bother to clear ptr_, as it is about to be
103
  // overwritten or destroyed.
104
303398593
  void resetInternal() {
105
303398593
    if (ptr_ != nullptr && ptr_->decRefCount()) {
106
32796098
      delete ptr_;
107
32796098
    }
108
303398593
  }
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
44353963
  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
5833779
  void incRefCount() {
156
    // Note: The ++ref_count_ here and --ref_count_ below have implicit memory
157
    // orderings of sequentially consistent. Relaxed on addition and
158
    // acquire/release on subtraction is the typical use for reference
159
    // counting. On x86, the difference in instruction count is minimal, but the
160
    // savings are greater on other platforms.
161
    //
162
    // https://www.boost.org/doc/libs/1_69_0/doc/html/atomic/usage_examples.html#boost_atomic.usage_examples.example_reference_counters
163
5833779
    ++ref_count_;
164
5833779
  }
165
5833779
  bool decRefCount() {
166
5833779
    ASSERT(ref_count_ >= 1);
167
5833779
    return --ref_count_ == 0;
168
5833779
  }
169
9
  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