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/ref_counted_ptr.h"
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
trace_(trace)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.
RefNonZero()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   }
RefNonZero(const DebugLocation & location,const char * reason)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 
RefIfNonZero()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   }
RefIfNonZero(const DebugLocation & location,const char * reason)135   bool RefIfNonZero(const DebugLocation& location, const char* reason) {
136 #ifndef NDEBUG
137     if (trace_ != nullptr) {
138       const Value prior = get();
139       gpr_log(GPR_INFO,
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.
Unref()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   }
Unref(const DebugLocation & location,const char * reason)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:
get()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.
202 class PolymorphicRefCount {
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.
210 class NonPolymorphicRefCount {
211  public:
212   ~NonPolymorphicRefCount() = default;
213 };
214 
215 // Behavior of RefCounted<> upon ref count reaching 0.
216 
217 // Default behavior: Delete the object.
218 struct UnrefDelete {
219   template <typename T>
operatorUnrefDelete220   void operator()(T* p) {
221     delete p;
222   }
223 };
224 
225 // Do not delete the object upon unref.  This is useful in cases where all
226 // existing objects must be tracked in a registry but the object's entry in
227 // the registry cannot be removed from the object's dtor due to
228 // synchronization issues.  In this case, the registry can be cleaned up
229 // later by identifying entries for which RefIfNonZero() returns null.
230 struct UnrefNoDelete {
231   template <typename T>
operatorUnrefNoDelete232   void operator()(T* /*p*/) {}
233 };
234 
235 // Call the object's dtor but do not delete it.  This is useful for cases
236 // where the object is stored in memory allocated elsewhere (e.g., the call
237 // arena).
238 struct UnrefCallDtor {
239   template <typename T>
operatorUnrefCallDtor240   void operator()(T* p) {
241     p->~T();
242   }
243 };
244 
245 // A base class for reference-counted objects.
246 // New objects should be created via new and start with a refcount of 1.
247 // When the refcount reaches 0, executes the specified UnrefBehavior.
248 //
249 // This will commonly be used by CRTP (curiously-recurring template pattern)
250 // e.g., class MyClass : public RefCounted<MyClass>
251 //
252 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between
253 // different implementations of RefCounted.
254 //
255 // Note that NonPolymorphicRefCount does not support polymorphic destruction.
256 // So, use NonPolymorphicRefCount only when both of the following conditions
257 // are guaranteed to hold:
258 // (a) Child is a concrete leaf class in RefCounted<Child>, and
259 // (b) you are guaranteed to call Unref only on concrete leaf classes and not
260 //     their parents.
261 //
262 // The following example is illegal, because calling Unref() will not call
263 // the dtor of Child.
264 //
265 //    class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
266 //    class Child : public Parent {}
267 //
268 //    Child* ch;
269 //    ch->Unref();
270 //
271 template <typename Child, typename Impl = PolymorphicRefCount,
272           typename UnrefBehavior = UnrefDelete>
273 class RefCounted : public Impl {
274  public:
275   using RefCountedChildType = Child;
276 
277   // Note: Depending on the Impl used, this dtor can be implicitly virtual.
278   ~RefCounted() = default;
279 
Ref()280   RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
281     IncrementRefCount();
282     return RefCountedPtr<Child>(static_cast<Child*>(this));
283   }
284 
Ref(const DebugLocation & location,const char * reason)285   RefCountedPtr<Child> Ref(const DebugLocation& location,
286                            const char* reason) GRPC_MUST_USE_RESULT {
287     IncrementRefCount(location, reason);
288     return RefCountedPtr<Child>(static_cast<Child*>(this));
289   }
290 
291   // TODO(roth): Once all of our code is converted to C++ and can use
292   // RefCountedPtr<> instead of manual ref-counting, make this method
293   // private, since it will only be used by RefCountedPtr<>, which is a
294   // friend of this class.
Unref()295   void Unref() {
296     if (GPR_UNLIKELY(refs_.Unref())) {
297       unref_behavior_(static_cast<Child*>(this));
298     }
299   }
Unref(const DebugLocation & location,const char * reason)300   void Unref(const DebugLocation& location, const char* reason) {
301     if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
302       unref_behavior_(static_cast<Child*>(this));
303     }
304   }
305 
RefIfNonZero()306   RefCountedPtr<Child> RefIfNonZero() GRPC_MUST_USE_RESULT {
307     return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this)
308                                                      : nullptr);
309   }
RefIfNonZero(const DebugLocation & location,const char * reason)310   RefCountedPtr<Child> RefIfNonZero(const DebugLocation& location,
311                                     const char* reason) GRPC_MUST_USE_RESULT {
312     return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason)
313                                     ? static_cast<Child*>(this)
314                                     : nullptr);
315   }
316 
317   // Not copyable nor movable.
318   RefCounted(const RefCounted&) = delete;
319   RefCounted& operator=(const RefCounted&) = delete;
320 
321  protected:
322   // Note: Tracing is a no-op on non-debug builds.
323   explicit RefCounted(const char* trace = nullptr,
324                       intptr_t initial_refcount = 1)
refs_(initial_refcount,trace)325       : refs_(initial_refcount, trace) {}
326 
327   // Note: Tracing is a no-op on non-debug builds.
328   explicit RefCounted(UnrefBehavior b, const char* trace = nullptr,
329                       intptr_t initial_refcount = 1)
refs_(initial_refcount,trace)330       : refs_(initial_refcount, trace), unref_behavior_(b) {}
331 
332  private:
333   // Allow RefCountedPtr<> to access IncrementRefCount().
334   template <typename T>
335   friend class RefCountedPtr;
336 
IncrementRefCount()337   void IncrementRefCount() { refs_.Ref(); }
IncrementRefCount(const DebugLocation & location,const char * reason)338   void IncrementRefCount(const DebugLocation& location, const char* reason) {
339     refs_.Ref(location, reason);
340   }
341 
342   RefCount refs_;
343   GPR_NO_UNIQUE_ADDRESS UnrefBehavior unref_behavior_;
344 };
345 
346 }  // namespace grpc_core
347 
348 #endif  // GRPC_SRC_CORE_LIB_GPRPP_REF_COUNTED_H
349