GRPC C++  1.26.0
ref_counted.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2017 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 #ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
20 #define GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
21 
23 
24 #include <grpc/support/atm.h>
25 #include <grpc/support/log.h>
26 #include <grpc/support/sync.h>
27 
28 #include <atomic>
29 #include <cassert>
30 #include <cinttypes>
31 
37 
38 namespace grpc_core {
39 
40 // PolymorphicRefCount enforces polymorphic destruction of RefCounted.
42  public:
43  virtual ~PolymorphicRefCount() = default;
44 };
45 
46 // NonPolymorphicRefCount does not enforce polymorphic destruction of
47 // RefCounted. Please refer to grpc_core::RefCounted for more details, and
48 // when in doubt use PolymorphicRefCount.
50  public:
51  ~NonPolymorphicRefCount() = default;
52 };
53 
54 // RefCount is a simple atomic ref-count.
55 //
56 // This is a C++ implementation of gpr_refcount, with inline functions. Due to
57 // inline functions, this class is significantly more efficient than
58 // gpr_refcount and should be preferred over gpr_refcount whenever possible.
59 //
60 // TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph
61 // above.
62 class RefCount {
63  public:
64  using Value = intptr_t;
65 
66  // `init` is the initial refcount stored in this object.
67  //
68  // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag.
69  // Note: RefCount tracing is only enabled on debug builds, even when a
70  // TraceFlag is used.
71  template <typename TraceFlagT = TraceFlag>
72  constexpr explicit RefCount(
73  Value init = 1,
74  TraceFlagT*
75 #ifndef NDEBUG
76  // Leave unnamed if NDEBUG to avoid unused parameter warning
77  trace_flag
78 #endif
79  = nullptr)
80  :
81 #ifndef NDEBUG
82  trace_flag_(trace_flag),
83 #endif
84  value_(init) {
85  }
86 
87  // Increases the ref-count by `n`.
88  void Ref(Value n = 1) {
89 #ifndef NDEBUG
90  const Value prior = value_.FetchAdd(n, MemoryOrder::RELAXED);
91  if (trace_flag_ != nullptr && trace_flag_->enabled()) {
92  gpr_log(GPR_INFO, "%s:%p ref %" PRIdPTR " -> %" PRIdPTR,
93  trace_flag_->name(), this, prior, prior + n);
94  }
95 #else
96  value_.FetchAdd(n, MemoryOrder::RELAXED);
97 #endif
98  }
99  void Ref(const DebugLocation& location, const char* reason, Value n = 1) {
100 #ifndef NDEBUG
101  const Value prior = value_.FetchAdd(n, MemoryOrder::RELAXED);
102  if (trace_flag_ != nullptr && trace_flag_->enabled()) {
103  gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
104  trace_flag_->name(), this, location.file(), location.line(),
105  prior, prior + n, reason);
106  }
107 #else
108  // Use conditionally-important parameters
109  (void)location;
110  (void)reason;
111 
112  value_.FetchAdd(n, MemoryOrder::RELAXED);
113 #endif
114  }
115 
116  // Similar to Ref() with an assert on the ref-count being non-zero.
117  void RefNonZero() {
118 #ifndef NDEBUG
119  const Value prior = value_.FetchAdd(1, MemoryOrder::RELAXED);
120  if (trace_flag_ != nullptr && trace_flag_->enabled()) {
121  gpr_log(GPR_INFO, "%s:%p ref %" PRIdPTR " -> %" PRIdPTR,
122  trace_flag_->name(), this, prior, prior + 1);
123  }
124  assert(prior > 0);
125 #else
126  value_.FetchAdd(1, MemoryOrder::RELAXED);
127 #endif
128  }
129  void RefNonZero(const DebugLocation& location, const char* reason) {
130 #ifndef NDEBUG
131  const Value prior = value_.FetchAdd(1, MemoryOrder::RELAXED);
132  if (trace_flag_ != nullptr && trace_flag_->enabled()) {
133  gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
134  trace_flag_->name(), this, location.file(), location.line(),
135  prior, prior + 1, reason);
136  }
137  assert(prior > 0);
138 #else
139  // Avoid unused-parameter warnings for debug-only parameters
140  (void)location;
141  (void)reason;
142  RefNonZero();
143 #endif
144  }
145 
146  bool RefIfNonZero() {
147 #ifndef NDEBUG
148  if (trace_flag_ != nullptr && trace_flag_->enabled()) {
149  const Value prior = get();
150  gpr_log(GPR_INFO, "%s:%p ref_if_non_zero %" PRIdPTR " -> %" PRIdPTR,
151  trace_flag_->name(), this, prior, prior + 1);
152  }
153 #endif
154  return value_.IncrementIfNonzero();
155  }
156  bool RefIfNonZero(const DebugLocation& location, const char* reason) {
157 #ifndef NDEBUG
158  if (trace_flag_ != nullptr && trace_flag_->enabled()) {
159  const Value prior = get();
161  "%s:%p %s:%d ref_if_non_zero "
162  "%" PRIdPTR " -> %" PRIdPTR " %s",
163  trace_flag_->name(), this, location.file(), location.line(),
164  prior, prior + 1, reason);
165  }
166 #endif
167  // Avoid unused-parameter warnings for debug-only parameters
168  (void)location;
169  (void)reason;
170  return value_.IncrementIfNonzero();
171  }
172 
173  // Decrements the ref-count and returns true if the ref-count reaches 0.
174  bool Unref() {
175 #ifndef NDEBUG
176  // Grab a copy of the trace flag before the atomic change, since we
177  // can't safely access it afterwards if we're going to be freed.
178  auto* trace_flag = trace_flag_;
179 #endif
180  const Value prior = value_.FetchSub(1, MemoryOrder::ACQ_REL);
181 #ifndef NDEBUG
182  if (trace_flag != nullptr && trace_flag->enabled()) {
183  gpr_log(GPR_INFO, "%s:%p unref %" PRIdPTR " -> %" PRIdPTR,
184  trace_flag->name(), this, prior, prior - 1);
185  }
186  GPR_DEBUG_ASSERT(prior > 0);
187 #endif
188  return prior == 1;
189  }
190  bool Unref(const DebugLocation& location, const char* reason) {
191 #ifndef NDEBUG
192  // Grab a copy of the trace flag before the atomic change, since we
193  // can't safely access it afterwards if we're going to be freed.
194  auto* trace_flag = trace_flag_;
195 #endif
196  const Value prior = value_.FetchSub(1, MemoryOrder::ACQ_REL);
197 #ifndef NDEBUG
198  if (trace_flag != nullptr && trace_flag->enabled()) {
199  gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s",
200  trace_flag->name(), this, location.file(), location.line(), prior,
201  prior - 1, reason);
202  }
203  GPR_DEBUG_ASSERT(prior > 0);
204 #else
205  // Avoid unused-parameter warnings for debug-only parameters
206  (void)location;
207  (void)reason;
208 #endif
209  return prior == 1;
210  }
211 
212  private:
213  Value get() const { return value_.Load(MemoryOrder::RELAXED); }
214 
215 #ifndef NDEBUG
216  TraceFlag* trace_flag_;
217 #endif
218  Atomic<Value> value_;
219 };
220 
221 // A base class for reference-counted objects.
222 // New objects should be created via New() and start with a refcount of 1.
223 // When the refcount reaches 0, the object will be deleted via delete .
224 //
225 // This will commonly be used by CRTP (curiously-recurring template pattern)
226 // e.g., class MyClass : public RefCounted<MyClass>
227 //
228 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between
229 // different implementations of RefCounted.
230 //
231 // Note that NonPolymorphicRefCount does not support polymorphic destruction.
232 // So, use NonPolymorphicRefCount only when both of the following conditions
233 // are guaranteed to hold:
234 // (a) Child is a concrete leaf class in RefCounted<Child>, and
235 // (b) you are guaranteed to call Unref only on concrete leaf classes and not
236 // their parents.
237 //
238 // The following example is illegal, because calling Unref() will not call
239 // the dtor of Child.
240 //
241 // class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
242 // class Child : public Parent {}
243 //
244 // Child* ch;
245 // ch->Unref();
246 //
247 template <typename Child, typename Impl = PolymorphicRefCount>
248 class RefCounted : public Impl {
249  public:
250  // Note: Depending on the Impl used, this dtor can be implicitly virtual.
251  ~RefCounted() = default;
252 
254  IncrementRefCount();
255  return RefCountedPtr<Child>(static_cast<Child*>(this));
256  }
257 
259  const char* reason) GRPC_MUST_USE_RESULT {
260  IncrementRefCount(location, reason);
261  return RefCountedPtr<Child>(static_cast<Child*>(this));
262  }
263 
264  // TODO(roth): Once all of our code is converted to C++ and can use
265  // RefCountedPtr<> instead of manual ref-counting, make this method
266  // private, since it will only be used by RefCountedPtr<>, which is a
267  // friend of this class.
268  void Unref() {
269  if (GPR_UNLIKELY(refs_.Unref())) {
270  delete static_cast<Child*>(this);
271  }
272  }
273  void Unref(const DebugLocation& location, const char* reason) {
274  if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
275  delete static_cast<Child*>(this);
276  }
277  }
278 
279  bool RefIfNonZero() { return refs_.RefIfNonZero(); }
280  bool RefIfNonZero(const DebugLocation& location, const char* reason) {
281  return refs_.RefIfNonZero(location, reason);
282  }
283 
284  // Not copyable nor movable.
285  RefCounted(const RefCounted&) = delete;
286  RefCounted& operator=(const RefCounted&) = delete;
287 
288  protected:
289  // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag.
290  // Note: RefCount tracing is only enabled on debug builds, even when a
291  // TraceFlag is used.
292  template <typename TraceFlagT = TraceFlag>
293  explicit RefCounted(TraceFlagT* trace_flag = nullptr,
294  intptr_t initial_refcount = 1)
295  : refs_(initial_refcount, trace_flag) {}
296 
297  private:
298  // Allow RefCountedPtr<> to access IncrementRefCount().
299  template <typename T>
300  friend class RefCountedPtr;
301 
302  void IncrementRefCount() { refs_.Ref(); }
303  void IncrementRefCount(const DebugLocation& location, const char* reason) {
304  refs_.Ref(location, reason);
305  }
306 
307  RefCount refs_;
308 };
309 
310 } // namespace grpc_core
311 
312 #endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */
bool enabled()
Definition: trace.h:80
#define GPR_INFO
Definition: log.h:56
const char * name() const
Definition: trace.h:68
const char * file() const
Definition: debug_location.h:34
intptr_t Value
Definition: ref_counted.h:64
void RefNonZero()
Definition: ref_counted.h:117
RefCountedPtr< Child > Ref() GRPC_MUST_USE_RESULT
Definition: ref_counted.h:253
Definition: debug_location.h:31
bool RefIfNonZero()
Definition: ref_counted.h:146
void Ref(Value n=1)
Definition: ref_counted.h:88
#define GPR_UNLIKELY(x)
Definition: port_platform.h:702
GPRAPI void gpr_log(const char *file, int line, gpr_log_severity severity, const char *format,...) GPR_PRINT_FORMAT_CHECK(4
Log a message.
T FetchAdd(Arg arg, MemoryOrder order=MemoryOrder::SEQ_CST)
Definition: atomic.h:71
bool RefIfNonZero(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:280
Definition: ref_counted.h:62
bool RefIfNonZero(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:156
Definition: ref_counted.h:41
Internal thread interface.
Definition: backoff.h:26
void Unref(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:273
constexpr RefCount(Value init=1, TraceFlagT *trace_flag=nullptr)
Definition: ref_counted.h:72
Definition: ref_counted_ptr.h:35
void Ref(const DebugLocation &location, const char *reason, Value n=1)
Definition: ref_counted.h:99
int line() const
Definition: debug_location.h:35
bool IncrementIfNonzero(MemoryOrder load_order=MemoryOrder::ACQUIRE)
Definition: atomic.h:84
T Load(MemoryOrder order) const
Definition: atomic.h:44
#define GRPC_MUST_USE_RESULT
Definition: port_platform.h:570
void Unref()
Definition: ref_counted.h:268
bool Unref()
Definition: ref_counted.h:174
bool Unref(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:190
Definition: ref_counted.h:248
T FetchSub(Arg arg, MemoryOrder order=MemoryOrder::SEQ_CST)
Definition: atomic.h:77
RefCounted(const RefCounted &)=delete
RefCounted & operator=(const RefCounted &)=delete
#define GPR_DEBUG_ASSERT(x)
Definition: log.h:103
RefCounted(TraceFlagT *trace_flag=nullptr, intptr_t initial_refcount=1)
Definition: ref_counted.h:293
void RefNonZero(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:129
bool RefIfNonZero()
Definition: ref_counted.h:279
RefCountedPtr< Child > Ref(const DebugLocation &location, const char *reason) GRPC_MUST_USE_RESULT
Definition: ref_counted.h:258
Definition: ref_counted.h:49
virtual ~PolymorphicRefCount()=default