dual_ref_counted.h
Go to the documentation of this file.
1 //
2 // Copyright 2020 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #ifndef GRPC_CORE_LIB_GPRPP_DUAL_REF_COUNTED_H
18 #define GRPC_CORE_LIB_GPRPP_DUAL_REF_COUNTED_H
19 
21 
22 #include <atomic>
23 #include <cstdint>
24 
25 #include <grpc/support/log.h>
26 
30 
31 namespace grpc_core {
32 
33 // DualRefCounted is an interface for reference-counted objects with two
34 // classes of refs: strong refs (usually just called "refs") and weak refs.
35 // This supports cases where an object needs to start shutting down when
36 // all external callers are done with it (represented by strong refs) but
37 // cannot be destroyed until all internal callbacks are complete
38 // (represented by weak refs).
39 //
40 // Each class of refs can be incremented and decremented independently.
41 // Objects start with 1 strong ref and 0 weak refs at instantiation.
42 // When the strong refcount reaches 0, the object's Orphan() method is called.
43 // When the weak refcount reaches 0, the object is destroyed.
44 //
45 // This will be used by CRTP (curiously-recurring template pattern), e.g.:
46 // class MyClass : public RefCounted<MyClass> { ... };
47 template <typename Child>
48 class DualRefCounted : public Orphanable {
49  public:
50  ~DualRefCounted() override = default;
51 
54  return RefCountedPtr<Child>(static_cast<Child*>(this));
55  }
56 
58  const char* reason) GRPC_MUST_USE_RESULT {
59  IncrementRefCount(location, reason);
60  return RefCountedPtr<Child>(static_cast<Child*>(this));
61  }
62 
63  void Unref() {
64  // Convert strong ref to weak ref.
65  const uint64_t prev_ref_pair =
66  refs_.fetch_add(MakeRefPair(-1, 1), std::memory_order_acq_rel);
67  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
68 #ifndef NDEBUG
69  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
70  if (trace_ != nullptr) {
71  gpr_log(GPR_INFO, "%s:%p unref %d -> %d, weak_ref %d -> %d", trace_, this,
72  strong_refs, strong_refs - 1, weak_refs, weak_refs + 1);
73  }
74  GPR_ASSERT(strong_refs > 0);
75 #endif
76  if (GPR_UNLIKELY(strong_refs == 1)) {
77  Orphan();
78  }
79  // Now drop the weak ref.
80  WeakUnref();
81  }
82  void Unref(const DebugLocation& location, const char* reason) {
83  const uint64_t prev_ref_pair =
84  refs_.fetch_add(MakeRefPair(-1, 1), std::memory_order_acq_rel);
85  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
86 #ifndef NDEBUG
87  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
88  if (trace_ != nullptr) {
89  gpr_log(GPR_INFO, "%s:%p %s:%d unref %d -> %d, weak_ref %d -> %d) %s",
90  trace_, this, location.file(), location.line(), strong_refs,
91  strong_refs - 1, weak_refs, weak_refs + 1, reason);
92  }
93  GPR_ASSERT(strong_refs > 0);
94 #else
95  // Avoid unused-parameter warnings for debug-only parameters
96  (void)location;
97  (void)reason;
98 #endif
99  if (GPR_UNLIKELY(strong_refs == 1)) {
100  Orphan();
101  }
102  // Now drop the weak ref.
103  WeakUnref(location, reason);
104  }
105 
107  uint64_t prev_ref_pair = refs_.load(std::memory_order_acquire);
108  do {
109  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
110 #ifndef NDEBUG
111  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
112  if (trace_ != nullptr) {
113  gpr_log(GPR_INFO, "%s:%p ref_if_non_zero %d -> %d (weak_refs=%d)",
114  trace_, this, strong_refs, strong_refs + 1, weak_refs);
115  }
116 #endif
117  if (strong_refs == 0) return nullptr;
118  } while (!refs_.compare_exchange_weak(
119  prev_ref_pair, prev_ref_pair + MakeRefPair(1, 0),
120  std::memory_order_acq_rel, std::memory_order_acquire));
121  return RefCountedPtr<Child>(static_cast<Child*>(this));
122  }
123 
125  const char* reason) GRPC_MUST_USE_RESULT {
126  uint64_t prev_ref_pair = refs_.load(std::memory_order_acquire);
127  do {
128  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
129 #ifndef NDEBUG
130  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
131  if (trace_ != nullptr) {
133  "%s:%p %s:%d ref_if_non_zero %d -> %d (weak_refs=%d) %s",
134  trace_, this, location.file(), location.line(), strong_refs,
135  strong_refs + 1, weak_refs, reason);
136  }
137 #else
138  // Avoid unused-parameter warnings for debug-only parameters
139  (void)location;
140  (void)reason;
141 #endif
142  if (strong_refs == 0) return nullptr;
143  } while (!refs_.compare_exchange_weak(
144  prev_ref_pair, prev_ref_pair + MakeRefPair(1, 0),
145  std::memory_order_acq_rel, std::memory_order_acquire));
146  return RefCountedPtr<Child>(static_cast<Child*>(this));
147  }
148 
151  return WeakRefCountedPtr<Child>(static_cast<Child*>(this));
152  }
153 
155  const char* reason) GRPC_MUST_USE_RESULT {
156  IncrementWeakRefCount(location, reason);
157  return WeakRefCountedPtr<Child>(static_cast<Child*>(this));
158  }
159 
160  void WeakUnref() {
161 #ifndef NDEBUG
162  // Grab a copy of the trace flag before the atomic change, since we
163  // will no longer be holding a ref afterwards and therefore can't
164  // safely access it, since another thread might free us in the interim.
165  const char* trace = trace_;
166 #endif
167  const uint64_t prev_ref_pair =
168  refs_.fetch_sub(MakeRefPair(0, 1), std::memory_order_acq_rel);
169 #ifndef NDEBUG
170  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
171  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
172  if (trace != nullptr) {
173  gpr_log(GPR_INFO, "%s:%p weak_unref %d -> %d (refs=%d)", trace, this,
174  weak_refs, weak_refs - 1, strong_refs);
175  }
176  GPR_ASSERT(weak_refs > 0);
177 #endif
178  if (GPR_UNLIKELY(prev_ref_pair == MakeRefPair(0, 1))) {
179  delete static_cast<Child*>(this);
180  }
181  }
182  void WeakUnref(const DebugLocation& location, const char* reason) {
183 #ifndef NDEBUG
184  // Grab a copy of the trace flag before the atomic change, since we
185  // will no longer be holding a ref afterwards and therefore can't
186  // safely access it, since another thread might free us in the interim.
187  const char* trace = trace_;
188 #endif
189  const uint64_t prev_ref_pair =
190  refs_.fetch_sub(MakeRefPair(0, 1), std::memory_order_acq_rel);
191 #ifndef NDEBUG
192  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
193  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
194  if (trace != nullptr) {
195  gpr_log(GPR_INFO, "%s:%p %s:%d weak_unref %d -> %d (refs=%d) %s", trace,
196  this, location.file(), location.line(), weak_refs, weak_refs - 1,
197  strong_refs, reason);
198  }
199  GPR_ASSERT(weak_refs > 0);
200 #else
201  // Avoid unused-parameter warnings for debug-only parameters
202  (void)location;
203  (void)reason;
204 #endif
205  if (GPR_UNLIKELY(prev_ref_pair == MakeRefPair(0, 1))) {
206  delete static_cast<Child*>(this);
207  }
208  }
209 
210  // Not copyable nor movable.
211  DualRefCounted(const DualRefCounted&) = delete;
212  DualRefCounted& operator=(const DualRefCounted&) = delete;
213 
214  protected:
215  // Note: Tracing is a no-op in non-debug builds.
216  explicit DualRefCounted(
217  const char*
218 #ifndef NDEBUG
219  // Leave unnamed if NDEBUG to avoid unused parameter warning
220  trace
221 #endif
222  = nullptr,
223  int32_t initial_refcount = 1)
224  :
225 #ifndef NDEBUG
226  trace_(trace),
227 #endif
228  refs_(MakeRefPair(initial_refcount, 0)) {
229  }
230 
231  private:
232  // Allow RefCountedPtr<> to access IncrementRefCount().
233  template <typename T>
234  friend class RefCountedPtr;
235  // Allow WeakRefCountedPtr<> to access IncrementWeakRefCount().
236  template <typename T>
237  friend class WeakRefCountedPtr;
238 
239  // First 32 bits are strong refs, next 32 bits are weak refs.
240  static uint64_t MakeRefPair(uint32_t strong, uint32_t weak) {
241  return (static_cast<uint64_t>(strong) << 32) + static_cast<int64_t>(weak);
242  }
243  static uint32_t GetStrongRefs(uint64_t ref_pair) {
244  return static_cast<uint32_t>(ref_pair >> 32);
245  }
246  static uint32_t GetWeakRefs(uint64_t ref_pair) {
247  return static_cast<uint32_t>(ref_pair & 0xffffffffu);
248  }
249 
251 #ifndef NDEBUG
252  const uint64_t prev_ref_pair =
253  refs_.fetch_add(MakeRefPair(1, 0), std::memory_order_relaxed);
254  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
255  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
256  GPR_ASSERT(strong_refs != 0);
257  if (trace_ != nullptr) {
258  gpr_log(GPR_INFO, "%s:%p ref %d -> %d; (weak_refs=%d)", trace_, this,
259  strong_refs, strong_refs + 1, weak_refs);
260  }
261 #else
262  refs_.fetch_add(MakeRefPair(1, 0), std::memory_order_relaxed);
263 #endif
264  }
265  void IncrementRefCount(const DebugLocation& location, const char* reason) {
266 #ifndef NDEBUG
267  const uint64_t prev_ref_pair =
268  refs_.fetch_add(MakeRefPair(1, 0), std::memory_order_relaxed);
269  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
270  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
271  GPR_ASSERT(strong_refs != 0);
272  if (trace_ != nullptr) {
273  gpr_log(GPR_INFO, "%s:%p %s:%d ref %d -> %d (weak_refs=%d) %s", trace_,
274  this, location.file(), location.line(), strong_refs,
275  strong_refs + 1, weak_refs, reason);
276  }
277 #else
278  // Use conditionally-important parameters
279  (void)location;
280  (void)reason;
281  refs_.fetch_add(MakeRefPair(1, 0), std::memory_order_relaxed);
282 #endif
283  }
284 
286 #ifndef NDEBUG
287  const uint64_t prev_ref_pair =
288  refs_.fetch_add(MakeRefPair(0, 1), std::memory_order_relaxed);
289  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
290  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
291  if (trace_ != nullptr) {
292  gpr_log(GPR_INFO, "%s:%p weak_ref %d -> %d; (refs=%d)", trace_, this,
293  weak_refs, weak_refs + 1, strong_refs);
294  }
295 #else
296  refs_.fetch_add(MakeRefPair(0, 1), std::memory_order_relaxed);
297 #endif
298  }
299  void IncrementWeakRefCount(const DebugLocation& location,
300  const char* reason) {
301 #ifndef NDEBUG
302  const uint64_t prev_ref_pair =
303  refs_.fetch_add(MakeRefPair(0, 1), std::memory_order_relaxed);
304  const uint32_t strong_refs = GetStrongRefs(prev_ref_pair);
305  const uint32_t weak_refs = GetWeakRefs(prev_ref_pair);
306  if (trace_ != nullptr) {
307  gpr_log(GPR_INFO, "%s:%p %s:%d weak_ref %d -> %d (refs=%d) %s", trace_,
308  this, location.file(), location.line(), weak_refs, weak_refs + 1,
309  strong_refs, reason);
310  }
311 #else
312  // Use conditionally-important parameters
313  (void)location;
314  (void)reason;
315  refs_.fetch_add(MakeRefPair(0, 1), std::memory_order_relaxed);
316 #endif
317  }
318 
319 #ifndef NDEBUG
320  const char* trace_;
321 #endif
322  std::atomic<uint64_t> refs_{0};
323 };
324 
325 } // namespace grpc_core
326 
327 #endif /* GRPC_CORE_LIB_GPRPP_DUAL_REF_COUNTED_H */
grpc_core::DualRefCounted::IncrementRefCount
void IncrementRefCount(const DebugLocation &location, const char *reason)
Definition: dual_ref_counted.h:265
GPR_INFO
#define GPR_INFO
Definition: include/grpc/impl/codegen/log.h:56
grpc_core::DualRefCounted::~DualRefCounted
~DualRefCounted() override=default
orphanable.h
log.h
grpc_core::DualRefCounted::IncrementRefCount
void IncrementRefCount()
Definition: dual_ref_counted.h:250
grpc_core::DebugLocation
Definition: debug_location.h:31
grpc_core::Orphanable
Definition: orphanable.h:39
grpc_core
Definition: call_metric_recorder.h:31
grpc_core::DualRefCounted
Definition: dual_ref_counted.h:48
grpc_core::DualRefCounted::WeakRef
WeakRefCountedPtr< Child > WeakRef(const DebugLocation &location, const char *reason) GRPC_MUST_USE_RESULT
Definition: dual_ref_counted.h:154
u
OPENSSL_EXPORT pem_password_cb void * u
Definition: pem.h:351
grpc_core::DualRefCounted::WeakRef
WeakRefCountedPtr< Child > WeakRef() GRPC_MUST_USE_RESULT
Definition: dual_ref_counted.h:149
grpc_core::DualRefCounted::operator=
DualRefCounted & operator=(const DualRefCounted &)=delete
grpc_core::DebugLocation::file
const char * file() const
Definition: debug_location.h:34
grpc_core::DualRefCounted::refs_
std::atomic< uint64_t > refs_
Definition: dual_ref_counted.h:322
grpc_core::DualRefCounted::DualRefCounted
DualRefCounted(const DualRefCounted &)=delete
uint32_t
unsigned int uint32_t
Definition: stdint-msvc2008.h:80
grpc_core::DualRefCounted::Ref
RefCountedPtr< Child > Ref(const DebugLocation &location, const char *reason) GRPC_MUST_USE_RESULT
Definition: dual_ref_counted.h:57
grpc_core::RefCountedPtr
Definition: ref_counted_ptr.h:35
GPR_ASSERT
#define GPR_ASSERT(x)
Definition: include/grpc/impl/codegen/log.h:94
int64_t
signed __int64 int64_t
Definition: stdint-msvc2008.h:89
grpc_core::DebugLocation::line
int line() const
Definition: debug_location.h:35
grpc_core::DualRefCounted::WeakUnref
void WeakUnref()
Definition: dual_ref_counted.h:160
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::DualRefCounted::Unref
void Unref(const DebugLocation &location, const char *reason)
Definition: dual_ref_counted.h:82
grpc_core::DualRefCounted::GetStrongRefs
static uint32_t GetStrongRefs(uint64_t ref_pair)
Definition: dual_ref_counted.h:243
uint64_t
unsigned __int64 uint64_t
Definition: stdint-msvc2008.h:90
grpc_core::Orphanable::Orphan
virtual void Orphan()=0
grpc_core::DualRefCounted::Ref
RefCountedPtr< Child > Ref() GRPC_MUST_USE_RESULT
Definition: dual_ref_counted.h:52
grpc_core::DualRefCounted::RefIfNonZero
RefCountedPtr< Child > RefIfNonZero(const DebugLocation &location, const char *reason) GRPC_MUST_USE_RESULT
Definition: dual_ref_counted.h:124
grpc_core::DualRefCounted::Unref
void Unref()
Definition: dual_ref_counted.h:63
grpc_core::DualRefCounted::WeakUnref
void WeakUnref(const DebugLocation &location, const char *reason)
Definition: dual_ref_counted.h:182
debug_location.h
GRPC_MUST_USE_RESULT
#define GRPC_MUST_USE_RESULT
Definition: impl/codegen/port_platform.h:584
grpc_core::DualRefCounted::IncrementWeakRefCount
void IncrementWeakRefCount(const DebugLocation &location, const char *reason)
Definition: dual_ref_counted.h:299
grpc_core::WeakRefCountedPtr
Definition: ref_counted_ptr.h:185
grpc_core::DualRefCounted::RefIfNonZero
RefCountedPtr< Child > RefIfNonZero() GRPC_MUST_USE_RESULT
Definition: dual_ref_counted.h:106
ref_counted_ptr.h
grpc_core::DualRefCounted::DualRefCounted
DualRefCounted(const char *trace=nullptr, int32_t initial_refcount=1)
Definition: dual_ref_counted.h:216
grpc_core::DualRefCounted::trace_
const char * trace_
Definition: dual_ref_counted.h:320
grpc_core::DualRefCounted::IncrementWeakRefCount
void IncrementWeakRefCount()
Definition: dual_ref_counted.h:285
int32_t
signed int int32_t
Definition: stdint-msvc2008.h:77
grpc_core::DualRefCounted::MakeRefPair
static uint64_t MakeRefPair(uint32_t strong, uint32_t weak)
Definition: dual_ref_counted.h:240
grpc_core::DualRefCounted::GetWeakRefs
static uint32_t GetWeakRefs(uint64_t ref_pair)
Definition: dual_ref_counted.h:246
port_platform.h


grpc
Author(s):
autogenerated on Fri May 16 2025 02:58:17