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