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 <atomic>
25 #include <cassert>
26 #include <cinttypes>
27 
28 #include <grpc/support/log.h>
29 
33 
34 namespace grpc_core {
35 
36 // RefCount is a simple atomic ref-count.
37 //
38 // This is a C++ implementation of gpr_refcount, with inline functions. Due to
39 // inline functions, this class is significantly more efficient than
40 // gpr_refcount and should be preferred over gpr_refcount whenever possible.
41 //
42 // TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph
43 // above.
44 class RefCount {
45  public:
46  using Value = intptr_t;
47 
48  // `init` is the initial refcount stored in this object.
49  //
50  // `trace` is a string to be logged with trace events; if null, no
51  // trace logging will be done. Tracing is a no-op in non-debug builds.
52  explicit RefCount(
53  Value init = 1,
54  const char*
55 #ifndef NDEBUG
56  // Leave unnamed if NDEBUG to avoid unused parameter warning
57  trace
58 #endif
59  = nullptr)
60  :
61 #ifndef NDEBUG
62  trace_(trace),
63 #endif
64  value_(init) {
65  }
66 
67  // Increases the ref-count by `n`.
68  void Ref(Value n = 1) {
69 #ifndef NDEBUG
70  const Value prior = value_.fetch_add(n, std::memory_order_relaxed);
71  if (trace_ != nullptr) {
72  gpr_log(GPR_INFO, "%s:%p ref %" PRIdPTR " -> %" PRIdPTR, trace_, this,
73  prior, prior + n);
74  }
75 #else
76  value_.fetch_add(n, std::memory_order_relaxed);
77 #endif
78  }
79  void Ref(const DebugLocation& location, const char* reason, Value n = 1) {
80 #ifndef NDEBUG
81  const Value prior = value_.fetch_add(n, std::memory_order_relaxed);
82  if (trace_ != nullptr) {
83  gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
84  trace_, this, location.file(), location.line(), prior, prior + n,
85  reason);
86  }
87 #else
88  // Use conditionally-important parameters
89  (void)location;
90  (void)reason;
91  value_.fetch_add(n, std::memory_order_relaxed);
92 #endif
93  }
94 
95  // Similar to Ref() with an assert on the ref-count being non-zero.
96  void RefNonZero() {
97 #ifndef NDEBUG
98  const Value prior = value_.fetch_add(1, std::memory_order_relaxed);
99  if (trace_ != nullptr) {
100  gpr_log(GPR_INFO, "%s:%p ref %" PRIdPTR " -> %" PRIdPTR, trace_, this,
101  prior, prior + 1);
102  }
103  assert(prior > 0);
104 #else
105  value_.fetch_add(1, std::memory_order_relaxed);
106 #endif
107  }
108  void RefNonZero(const DebugLocation& location, const char* reason) {
109 #ifndef NDEBUG
110  const Value prior = value_.fetch_add(1, std::memory_order_relaxed);
111  if (trace_ != nullptr) {
112  gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
113  trace_, this, location.file(), location.line(), prior, prior + 1,
114  reason);
115  }
116  assert(prior > 0);
117 #else
118  // Avoid unused-parameter warnings for debug-only parameters
119  (void)location;
120  (void)reason;
121  RefNonZero();
122 #endif
123  }
124 
125  bool RefIfNonZero() {
126 #ifndef NDEBUG
127  if (trace_ != nullptr) {
128  const Value prior = get();
129  gpr_log(GPR_INFO, "%s:%p ref_if_non_zero %" PRIdPTR " -> %" PRIdPTR,
130  trace_, this, prior, prior + 1);
131  }
132 #endif
133  return IncrementIfNonzero(&value_);
134  }
135  bool RefIfNonZero(const DebugLocation& location, const char* reason) {
136 #ifndef NDEBUG
137  if (trace_ != nullptr) {
138  const Value prior = get();
140  "%s:%p %s:%d ref_if_non_zero %" PRIdPTR " -> %" PRIdPTR " %s",
141  trace_, this, location.file(), location.line(), prior, prior + 1,
142  reason);
143  }
144 #endif
145  // Avoid unused-parameter warnings for debug-only parameters
146  (void)location;
147  (void)reason;
148  return IncrementIfNonzero(&value_);
149  }
150 
151  // Decrements the ref-count and returns true if the ref-count reaches 0.
152  bool Unref() {
153 #ifndef NDEBUG
154  // Grab a copy of the trace flag before the atomic change, since we
155  // will no longer be holding a ref afterwards and therefore can't
156  // safely access it, since another thread might free us in the interim.
157  auto* trace = trace_;
158 #endif
159  const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel);
160 #ifndef NDEBUG
161  if (trace != nullptr) {
162  gpr_log(GPR_INFO, "%s:%p unref %" PRIdPTR " -> %" PRIdPTR, trace, this,
163  prior, prior - 1);
164  }
165  GPR_DEBUG_ASSERT(prior > 0);
166 #endif
167  return prior == 1;
168  }
169  bool Unref(const DebugLocation& location, const char* reason) {
170 #ifndef NDEBUG
171  // Grab a copy of the trace flag before the atomic change, since we
172  // will no longer be holding a ref afterwards and therefore can't
173  // safely access it, since another thread might free us in the interim.
174  auto* trace = trace_;
175 #endif
176  const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel);
177 #ifndef NDEBUG
178  if (trace != nullptr) {
179  gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s",
180  trace, this, location.file(), location.line(), prior, prior - 1,
181  reason);
182  }
183  GPR_DEBUG_ASSERT(prior > 0);
184 #else
185  // Avoid unused-parameter warnings for debug-only parameters
186  (void)location;
187  (void)reason;
188 #endif
189  return prior == 1;
190  }
191 
192  private:
193  Value get() const { return value_.load(std::memory_order_relaxed); }
194 
195 #ifndef NDEBUG
196  const char* trace_;
197 #endif
198  std::atomic<Value> value_{0};
199 };
200 
201 // PolymorphicRefCount enforces polymorphic destruction of RefCounted.
203  public:
204  virtual ~PolymorphicRefCount() = default;
205 };
206 
207 // NonPolymorphicRefCount does not enforce polymorphic destruction of
208 // RefCounted. Please refer to RefCounted for more details, and
209 // when in doubt use PolymorphicRefCount.
211  public:
212  ~NonPolymorphicRefCount() = default;
213 };
214 
215 // Behavior of RefCounted<> upon ref count reaching 0.
217  // Default behavior: Delete the object.
219  // Do not delete the object upon unref. This is useful in cases where all
220  // existing objects must be tracked in a registry but the object's entry in
221  // the registry cannot be removed from the object's dtor due to
222  // synchronization issues. In this case, the registry can be cleaned up
223  // later by identifying entries for which RefIfNonZero() returns null.
225  // Call the object's dtor but do not delete it. This is useful for cases
226  // where the object is stored in memory allocated elsewhere (e.g., the call
227  // arena).
229 };
230 
231 namespace internal {
232 template <typename T, UnrefBehavior UnrefBehaviorArg>
233 class Delete;
234 
235 template <typename T>
237  public:
238  explicit Delete(T* t) { delete t; }
239 };
240 template <typename T>
242  public:
243  explicit Delete(T* /*t*/) {}
244 };
245 template <typename T>
247  public:
248  explicit Delete(T* t) { t->~T(); }
249 };
250 } // namespace internal
251 
252 // A base class for reference-counted objects.
253 // New objects should be created via new and start with a refcount of 1.
254 // When the refcount reaches 0, executes the specified UnrefBehavior.
255 //
256 // This will commonly be used by CRTP (curiously-recurring template pattern)
257 // e.g., class MyClass : public RefCounted<MyClass>
258 //
259 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between
260 // different implementations of RefCounted.
261 //
262 // Note that NonPolymorphicRefCount does not support polymorphic destruction.
263 // So, use NonPolymorphicRefCount only when both of the following conditions
264 // are guaranteed to hold:
265 // (a) Child is a concrete leaf class in RefCounted<Child>, and
266 // (b) you are guaranteed to call Unref only on concrete leaf classes and not
267 // their parents.
268 //
269 // The following example is illegal, because calling Unref() will not call
270 // the dtor of Child.
271 //
272 // class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
273 // class Child : public Parent {}
274 //
275 // Child* ch;
276 // ch->Unref();
277 //
278 template <typename Child, typename Impl = PolymorphicRefCount,
279  UnrefBehavior UnrefBehaviorArg = kUnrefDelete>
280 class RefCounted : public Impl {
281  public:
282  using RefCountedChildType = Child;
283 
284  // Note: Depending on the Impl used, this dtor can be implicitly virtual.
285  ~RefCounted() = default;
286 
289  return RefCountedPtr<Child>(static_cast<Child*>(this));
290  }
291 
293  const char* reason) GRPC_MUST_USE_RESULT {
294  IncrementRefCount(location, reason);
295  return RefCountedPtr<Child>(static_cast<Child*>(this));
296  }
297 
298  // TODO(roth): Once all of our code is converted to C++ and can use
299  // RefCountedPtr<> instead of manual ref-counting, make this method
300  // private, since it will only be used by RefCountedPtr<>, which is a
301  // friend of this class.
302  void Unref() {
303  if (GPR_UNLIKELY(refs_.Unref())) {
304  internal::Delete<Child, UnrefBehaviorArg>(static_cast<Child*>(this));
305  }
306  }
307  void Unref(const DebugLocation& location, const char* reason) {
308  if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
309  internal::Delete<Child, UnrefBehaviorArg>(static_cast<Child*>(this));
310  }
311  }
312 
314  return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this)
315  : nullptr);
316  }
318  const char* reason) GRPC_MUST_USE_RESULT {
319  return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason)
320  ? static_cast<Child*>(this)
321  : nullptr);
322  }
323 
324  // Not copyable nor movable.
325  RefCounted(const RefCounted&) = delete;
326  RefCounted& operator=(const RefCounted&) = delete;
327 
328  protected:
329  // Note: Tracing is a no-op on non-debug builds.
330  explicit RefCounted(const char* trace = nullptr,
331  intptr_t initial_refcount = 1)
332  : refs_(initial_refcount, trace) {}
333 
334  private:
335  // Allow RefCountedPtr<> to access IncrementRefCount().
336  template <typename T>
337  friend class RefCountedPtr;
338 
340  void IncrementRefCount(const DebugLocation& location, const char* reason) {
341  refs_.Ref(location, reason);
342  }
343 
345 };
346 
347 } // namespace grpc_core
348 
349 #endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */
grpc_core::RefCount::RefNonZero
void RefNonZero()
Definition: ref_counted.h:96
grpc_core::UnrefBehavior
UnrefBehavior
Definition: ref_counted.h:216
grpc_core::RefCount::Ref
void Ref(Value n=1)
Definition: ref_counted.h:68
grpc_core::RefCount::get
Value get() const
Definition: ref_counted.h:193
GPR_INFO
#define GPR_INFO
Definition: include/grpc/impl/codegen/log.h:56
atomic_utils.h
init
const char * init
Definition: upb/upb/bindings/lua/main.c:49
log.h
grpc_core::RefCount::RefNonZero
void RefNonZero(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:108
grpc_core::RefCount::trace_
const char * trace_
Definition: ref_counted.h:196
grpc_core::DebugLocation
Definition: debug_location.h:31
grpc_core::RefCount::value_
std::atomic< Value > value_
Definition: ref_counted.h:198
GPR_DEBUG_ASSERT
#define GPR_DEBUG_ASSERT(x)
Definition: include/grpc/impl/codegen/log.h:103
grpc_core::RefCounted::RefIfNonZero
RefCountedPtr< Child > RefIfNonZero() GRPC_MUST_USE_RESULT
Definition: ref_counted.h:313
grpc_core
Definition: call_metric_recorder.h:31
grpc_core::RefCounted::Unref
void Unref(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:307
grpc_core::RefCounted::~RefCounted
~RefCounted()=default
Value
Definition: bloaty/third_party/protobuf/src/google/protobuf/struct.pb.h:306
grpc_core::RefCounted::RefCounted
RefCounted(const char *trace=nullptr, intptr_t initial_refcount=1)
Definition: ref_counted.h:330
grpc_core::DebugLocation::file
const char * file() const
Definition: debug_location.h:34
grpc_core::RefCounted::IncrementRefCount
void IncrementRefCount(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:340
T
#define T(upbtypeconst, upbtype, ctype, default_value)
grpc_core::RefCounted::refs_
RefCount refs_
Definition: ref_counted.h:344
grpc_core::RefCounted::Ref
RefCountedPtr< Child > Ref(const DebugLocation &location, const char *reason) GRPC_MUST_USE_RESULT
Definition: ref_counted.h:292
grpc_core::internal::Delete< T, kUnrefDelete >::Delete
Delete(T *t)
Definition: ref_counted.h:238
grpc_core::RefCounted::IncrementRefCount
void IncrementRefCount()
Definition: ref_counted.h:339
grpc_core::RefCounted::RefIfNonZero
RefCountedPtr< Child > RefIfNonZero(const DebugLocation &location, const char *reason) GRPC_MUST_USE_RESULT
Definition: ref_counted.h:317
grpc_core::RefCounted::Unref
void Unref()
Definition: ref_counted.h:302
grpc_core::internal::Delete
Definition: ref_counted.h:233
grpc_core::internal::Delete< T, kUnrefNoDelete >::Delete
Delete(T *)
Definition: ref_counted.h:243
grpc_core::RefCounted::operator=
RefCounted & operator=(const RefCounted &)=delete
grpc_core::kUnrefCallDtor
@ kUnrefCallDtor
Definition: ref_counted.h:228
grpc_core::RefCountedPtr
Definition: ref_counted_ptr.h:35
grpc_core::DebugLocation::line
int line() const
Definition: debug_location.h:35
gpr_log
GPRAPI void gpr_log(const char *file, int line, gpr_log_severity severity, const char *format,...) GPR_PRINT_FORMAT_CHECK(4
GPR_UNLIKELY
#define GPR_UNLIKELY(x)
Definition: impl/codegen/port_platform.h:770
grpc_core::RefCount::Unref
bool Unref()
Definition: ref_counted.h:152
grpc_core::NonPolymorphicRefCount::~NonPolymorphicRefCount
~NonPolymorphicRefCount()=default
intptr_t
_W64 signed int intptr_t
Definition: stdint-msvc2008.h:118
grpc_core::NonPolymorphicRefCount
Definition: ref_counted.h:210
grpc_core::RefCounted
Definition: ref_counted.h:280
grpc_core::RefCount::Ref
void Ref(const DebugLocation &location, const char *reason, Value n=1)
Definition: ref_counted.h:79
n
int n
Definition: abseil-cpp/absl/container/btree_test.cc:1080
grpc_core::RefCounted< Security >::RefCountedChildType
Security RefCountedChildType
Definition: ref_counted.h:282
debug_location.h
GRPC_MUST_USE_RESULT
#define GRPC_MUST_USE_RESULT
Definition: impl/codegen/port_platform.h:584
grpc_core::RefCount::Unref
bool Unref(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:169
ref_counted_ptr.h
grpc_core::RefCount::RefIfNonZero
bool RefIfNonZero()
Definition: ref_counted.h:125
grpc_core::kUnrefNoDelete
@ kUnrefNoDelete
Definition: ref_counted.h:224
grpc_core::IncrementIfNonzero
bool IncrementIfNonzero(std::atomic< T > *p)
Definition: atomic_utils.h:31
internal
Definition: benchmark/test/output_test_helper.cc:20
grpc_core::RefCount::RefIfNonZero
bool RefIfNonZero(const DebugLocation &location, const char *reason)
Definition: ref_counted.h:135
grpc_core::PolymorphicRefCount
Definition: ref_counted.h:202
grpc_core::PolymorphicRefCount::~PolymorphicRefCount
virtual ~PolymorphicRefCount()=default
grpc_core::RefCounted::RefCounted
RefCounted(const RefCounted &)=delete
grpc_core::RefCount
Definition: ref_counted.h:44
grpc_core::kUnrefDelete
@ kUnrefDelete
Definition: ref_counted.h:218
grpc_core::RefCounted::Ref
RefCountedPtr< Child > Ref() GRPC_MUST_USE_RESULT
Definition: ref_counted.h:287
grpc_core::RefCount::RefCount
RefCount(Value init=1, const char *trace=nullptr)
Definition: ref_counted.h:52
grpc_core::internal::Delete< T, kUnrefCallDtor >::Delete
Delete(T *t)
Definition: ref_counted.h:248
port_platform.h


grpc
Author(s):
autogenerated on Thu Mar 13 2025 03:01:07