• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_SRC_CORE_LIB_GPRPP_REF_COUNTED_H
20 #define GRPC_SRC_CORE_LIB_GPRPP_REF_COUNTED_H
21 
22 #include <grpc/support/port_platform.h>
23 
24 #include <atomic>
25 #include <cassert>
26 #include <cinttypes>
27 
28 #include <grpc/support/log.h>
29 
30 #include "src/core/lib/gprpp/atomic_utils.h"
31 #include "src/core/lib/gprpp/debug_location.h"
32 #include "src/core/lib/gprpp/down_cast.h"
33 #include "src/core/lib/gprpp/ref_counted_ptr.h"
34 
35 namespace grpc_core {
36 
37 // RefCount is a simple atomic ref-count.
38 //
39 // This is a C++ implementation of gpr_refcount, with inline functions. Due to
40 // inline functions, this class is significantly more efficient than
41 // gpr_refcount and should be preferred over gpr_refcount whenever possible.
42 //
43 // TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph
44 //               above.
45 class RefCount {
46  public:
47   using Value = intptr_t;
48 
RefCount()49   RefCount() : RefCount(1) {}
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,
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
trace_(trace)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_.fetch_add(n, std::memory_order_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_.fetch_add(n, std::memory_order_relaxed);
80 #endif
81   }
82   void Ref(const DebugLocation& location, const char* reason, Value n = 1) {
83 #ifndef NDEBUG
84     const Value prior = value_.fetch_add(n, std::memory_order_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_.fetch_add(n, std::memory_order_relaxed);
95 #endif
96   }
97 
98   // Similar to Ref() with an assert on the ref-count being non-zero.
RefNonZero()99   void RefNonZero() {
100 #ifndef NDEBUG
101     const Value prior = value_.fetch_add(1, std::memory_order_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_.fetch_add(1, std::memory_order_relaxed);
109 #endif
110   }
RefNonZero(const DebugLocation & location,const char * reason)111   void RefNonZero(const DebugLocation& location, const char* reason) {
112 #ifndef NDEBUG
113     const Value prior = value_.fetch_add(1, std::memory_order_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 
RefIfNonZero()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 IncrementIfNonzero(&value_);
137   }
RefIfNonZero(const DebugLocation & location,const char * reason)138   bool RefIfNonZero(const DebugLocation& location, const char* reason) {
139 #ifndef NDEBUG
140     if (trace_ != nullptr) {
141       const Value prior = get();
142       gpr_log(GPR_INFO,
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 IncrementIfNonzero(&value_);
152   }
153 
154   // Decrements the ref-count and returns true if the ref-count reaches 0.
Unref()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_.fetch_sub(1, std::memory_order_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   }
Unref(const DebugLocation & location,const char * reason)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_.fetch_sub(1, std::memory_order_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:
get()196   Value get() const { return value_.load(std::memory_order_relaxed); }
197 
198 #ifndef NDEBUG
199   const char* trace_;
200 #endif
201   std::atomic<Value> value_{0};
202 };
203 
204 // PolymorphicRefCount enforces polymorphic destruction of RefCounted.
205 class PolymorphicRefCount {
206  public:
207   virtual ~PolymorphicRefCount() = default;
208 };
209 
210 // NonPolymorphicRefCount does not enforce polymorphic destruction of
211 // RefCounted. Please refer to RefCounted for more details, and
212 // when in doubt use PolymorphicRefCount.
213 class NonPolymorphicRefCount {
214  public:
215   ~NonPolymorphicRefCount() = default;
216 };
217 
218 // Behavior of RefCounted<> upon ref count reaching 0.
219 
220 // Default behavior: Delete the object.
221 struct UnrefDelete {
222   template <typename T>
operatorUnrefDelete223   void operator()(T* p) const {
224     delete p;
225   }
226 };
227 
228 // Do not delete the object upon unref.  This is useful in cases where all
229 // existing objects must be tracked in a registry but the object's entry in
230 // the registry cannot be removed from the object's dtor due to
231 // synchronization issues.  In this case, the registry can be cleaned up
232 // later by identifying entries for which RefIfNonZero() returns null.
233 struct UnrefNoDelete {
234   template <typename T>
operatorUnrefNoDelete235   void operator()(T* /*p*/) const {}
236 };
237 
238 // Call the object's dtor but do not delete it.  This is useful for cases
239 // where the object is stored in memory allocated elsewhere (e.g., the call
240 // arena).
241 struct UnrefCallDtor {
242   template <typename T>
operatorUnrefCallDtor243   void operator()(T* p) const {
244     p->~T();
245   }
246 };
247 
248 // A base class for reference-counted objects.
249 // New objects should be created via new and start with a refcount of 1.
250 // When the refcount reaches 0, executes the specified UnrefBehavior.
251 //
252 // This will commonly be used by CRTP (curiously-recurring template pattern)
253 // e.g., class MyClass : public RefCounted<MyClass>
254 //
255 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between
256 // different implementations of RefCounted.
257 //
258 // Note that NonPolymorphicRefCount does not support polymorphic destruction.
259 // So, use NonPolymorphicRefCount only when both of the following conditions
260 // are guaranteed to hold:
261 // (a) Child is a concrete leaf class in RefCounted<Child>, and
262 // (b) you are guaranteed to call Unref only on concrete leaf classes and not
263 //     their parents.
264 //
265 // The following example is illegal, because calling Unref() will not call
266 // the dtor of Child.
267 //
268 //    class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
269 //    class Child : public Parent {}
270 //
271 //    Child* ch;
272 //    ch->Unref();
273 //
274 template <typename Child, typename Impl = PolymorphicRefCount,
275           typename UnrefBehavior = UnrefDelete>
276 class RefCounted : public Impl {
277  public:
278   using RefCountedChildType = Child;
279 
280   // Not copyable nor movable.
281   RefCounted(const RefCounted&) = delete;
282   RefCounted& operator=(const RefCounted&) = delete;
283 
284   // Note: Depending on the Impl used, this dtor can be implicitly virtual.
285   ~RefCounted() = default;
286 
287   // Ref() for mutable types.
Ref()288   GRPC_MUST_USE_RESULT RefCountedPtr<Child> Ref() {
289     IncrementRefCount();
290     return RefCountedPtr<Child>(static_cast<Child*>(this));
291   }
Ref(const DebugLocation & location,const char * reason)292   GRPC_MUST_USE_RESULT RefCountedPtr<Child> Ref(const DebugLocation& location,
293                                                 const char* reason) {
294     IncrementRefCount(location, reason);
295     return RefCountedPtr<Child>(static_cast<Child*>(this));
296   }
297 
298   // Ref() for const types.
Ref()299   GRPC_MUST_USE_RESULT RefCountedPtr<const Child> Ref() const {
300     IncrementRefCount();
301     return RefCountedPtr<const Child>(static_cast<const Child*>(this));
302   }
Ref(const DebugLocation & location,const char * reason)303   GRPC_MUST_USE_RESULT RefCountedPtr<const Child> Ref(
304       const DebugLocation& location, const char* reason) const {
305     IncrementRefCount(location, reason);
306     return RefCountedPtr<const Child>(static_cast<const Child*>(this));
307   }
308 
309   template <
310       typename Subclass,
311       std::enable_if_t<std::is_base_of<Child, Subclass>::value, bool> = true>
RefAsSubclass()312   RefCountedPtr<Subclass> RefAsSubclass() {
313     IncrementRefCount();
314     return RefCountedPtr<Subclass>(
315         DownCast<Subclass*>(static_cast<Child*>(this)));
316   }
317   template <
318       typename Subclass,
319       std::enable_if_t<std::is_base_of<Child, Subclass>::value, bool> = true>
RefAsSubclass(const DebugLocation & location,const char * reason)320   RefCountedPtr<Subclass> RefAsSubclass(const DebugLocation& location,
321                                         const char* reason) {
322     IncrementRefCount(location, reason);
323     return RefCountedPtr<Subclass>(
324         DownCast<Subclass*>(static_cast<Child*>(this)));
325   }
326 
327   // RefIfNonZero() for mutable types.
RefIfNonZero()328   GRPC_MUST_USE_RESULT RefCountedPtr<Child> RefIfNonZero() {
329     return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this)
330                                                      : nullptr);
331   }
RefIfNonZero(const DebugLocation & location,const char * reason)332   GRPC_MUST_USE_RESULT RefCountedPtr<Child> RefIfNonZero(
333       const DebugLocation& location, const char* reason) {
334     return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason)
335                                     ? static_cast<Child*>(this)
336                                     : nullptr);
337   }
338 
339   // RefIfNonZero() for const types.
RefIfNonZero()340   GRPC_MUST_USE_RESULT RefCountedPtr<const Child> RefIfNonZero() const {
341     return RefCountedPtr<const Child>(
342         refs_.RefIfNonZero() ? static_cast<const Child*>(this) : nullptr);
343   }
RefIfNonZero(const DebugLocation & location,const char * reason)344   GRPC_MUST_USE_RESULT RefCountedPtr<const Child> RefIfNonZero(
345       const DebugLocation& location, const char* reason) const {
346     return RefCountedPtr<const Child>(refs_.RefIfNonZero(location, reason)
347                                           ? static_cast<const Child*>(this)
348                                           : nullptr);
349   }
350 
351   // TODO(roth): Once all of our code is converted to C++ and can use
352   // RefCountedPtr<> instead of manual ref-counting, make this method
353   // private, since it will only be used by RefCountedPtr<>, which is a
354   // friend of this class.
Unref()355   void Unref() const {
356     if (GPR_UNLIKELY(refs_.Unref())) {
357       unref_behavior_(static_cast<const Child*>(this));
358     }
359   }
Unref(const DebugLocation & location,const char * reason)360   void Unref(const DebugLocation& location, const char* reason) const {
361     if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
362       unref_behavior_(static_cast<const Child*>(this));
363     }
364   }
365 
366  protected:
367   // Note: Tracing is a no-op on non-debug builds.
368   explicit RefCounted(const char* trace = nullptr,
369                       intptr_t initial_refcount = 1)
refs_(initial_refcount,trace)370       : refs_(initial_refcount, trace) {}
371 
372   // Note: Tracing is a no-op on non-debug builds.
373   explicit RefCounted(UnrefBehavior b, const char* trace = nullptr,
374                       intptr_t initial_refcount = 1)
refs_(initial_refcount,trace)375       : refs_(initial_refcount, trace), unref_behavior_(b) {}
376 
377  private:
378   // Allow RefCountedPtr<> to access IncrementRefCount().
379   template <typename T>
380   friend class RefCountedPtr;
381 
IncrementRefCount()382   void IncrementRefCount() const { refs_.Ref(); }
IncrementRefCount(const DebugLocation & location,const char * reason)383   void IncrementRefCount(const DebugLocation& location,
384                          const char* reason) const {
385     refs_.Ref(location, reason);
386   }
387 
388   mutable RefCount refs_;
389   GPR_NO_UNIQUE_ADDRESS UnrefBehavior unref_behavior_;
390 };
391 
392 }  // namespace grpc_core
393 
394 #endif  // GRPC_SRC_CORE_LIB_GPRPP_REF_COUNTED_H
395