• 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_UTIL_REF_COUNTED_H
20 #define GRPC_SRC_CORE_UTIL_REF_COUNTED_H
21 
22 #include <grpc/support/port_platform.h>
23 
24 #include <atomic>
25 #include <cassert>
26 #include <cinttypes>
27 
28 #include "absl/log/check.h"
29 #include "absl/log/log.h"
30 #include "src/core/util/atomic_utils.h"
31 #include "src/core/util/debug_location.h"
32 #include "src/core/util/down_cast.h"
33 #include "src/core/util/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       LOG(INFO) << trace_ << ":" << this << " ref " << prior << " -> "
76                 << 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       LOG(INFO) << trace_ << ":" << this << " " << location.file() << ":"
87                 << location.line() << " ref " << 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       LOG(INFO) << trace_ << ":" << this << " ref " << prior << " -> "
104                 << 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       LOG(INFO) << trace_ << ":" << this << " " << location.file() << ":"
116                 << location.line() << " ref " << 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       LOG(INFO) << trace_ << ":" << this << " ref_if_non_zero " << prior
133                 << " -> " << 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       LOG(INFO) << trace_ << ":" << this << " " << location.file() << ":"
143                 << location.line() << " ref_if_non_zero " << prior << " -> "
144                 << prior + 1 << " " << reason;
145     }
146 #endif
147     // Avoid unused-parameter warnings for debug-only parameters
148     (void)location;
149     (void)reason;
150     return IncrementIfNonzero(&value_);
151   }
152 
153   // Decrements the ref-count and returns true if the ref-count reaches 0.
Unref()154   bool Unref() {
155 #ifndef NDEBUG
156     // Grab a copy of the trace flag before the atomic change, since we
157     // will no longer be holding a ref afterwards and therefore can't
158     // safely access it, since another thread might free us in the interim.
159     auto* trace = trace_;
160 #endif
161     const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel);
162 #ifndef NDEBUG
163     if (trace != nullptr) {
164       LOG(INFO) << trace << ":" << this << " unref " << prior << " -> "
165                 << prior - 1;
166     }
167     DCHECK_GT(prior, 0);
168 #endif
169     return prior == 1;
170   }
Unref(const DebugLocation & location,const char * reason)171   bool Unref(const DebugLocation& location, const char* reason) {
172 #ifndef NDEBUG
173     // Grab a copy of the trace flag before the atomic change, since we
174     // will no longer be holding a ref afterwards and therefore can't
175     // safely access it, since another thread might free us in the interim.
176     auto* trace = trace_;
177 #endif
178     const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel);
179 #ifndef NDEBUG
180     if (trace != nullptr) {
181       LOG(INFO) << trace << ":" << this << " " << location.file() << ":"
182                 << location.line() << " unref " << prior << " -> " << prior - 1
183                 << " " << reason;
184     }
185     DCHECK_GT(prior, 0);
186 #else
187     // Avoid unused-parameter warnings for debug-only parameters
188     (void)location;
189     (void)reason;
190 #endif
191     return prior == 1;
192   }
193 
194  private:
get()195   Value get() const { return value_.load(std::memory_order_relaxed); }
196 
197 #ifndef NDEBUG
198   const char* trace_;
199 #endif
200   std::atomic<Value> value_{0};
201 };
202 
203 // PolymorphicRefCount enforces polymorphic destruction of RefCounted.
204 class PolymorphicRefCount {
205  public:
206   virtual ~PolymorphicRefCount() = default;
207 };
208 
209 // NonPolymorphicRefCount does not enforce polymorphic destruction of
210 // RefCounted. Please refer to RefCounted for more details, and
211 // when in doubt use PolymorphicRefCount.
212 class NonPolymorphicRefCount {
213  public:
214   ~NonPolymorphicRefCount() = default;
215 };
216 
217 // Behavior of RefCounted<> upon ref count reaching 0.
218 
219 // Default behavior: Delete the object.
220 struct UnrefDelete {
221   template <typename T>
operatorUnrefDelete222   void operator()(T* p) const {
223     delete p;
224   }
225 };
226 
227 // Do not delete the object upon unref.  This is useful in cases where all
228 // existing objects must be tracked in a registry but the object's entry in
229 // the registry cannot be removed from the object's dtor due to
230 // synchronization issues.  In this case, the registry can be cleaned up
231 // later by identifying entries for which RefIfNonZero() returns null.
232 struct UnrefNoDelete {
233   template <typename T>
operatorUnrefNoDelete234   void operator()(T* /*p*/) const {}
235 };
236 
237 // Call the object's dtor but do not delete it.  This is useful for cases
238 // where the object is stored in memory allocated elsewhere (e.g., the call
239 // arena).
240 struct UnrefCallDtor {
241   template <typename T>
operatorUnrefCallDtor242   void operator()(T* p) const {
243     p->~T();
244   }
245 };
246 
247 // Call the Destroy method on the object. This is useful when the object
248 // needs precise control of how it's deallocated.
249 struct UnrefCallDestroy {
250   template <typename T>
operatorUnrefCallDestroy251   void operator()(T* p) const {
252     p->Destroy();
253   }
254 };
255 
256 // A base class for reference-counted objects.
257 // New objects should be created via new and start with a refcount of 1.
258 // When the refcount reaches 0, executes the specified UnrefBehavior.
259 //
260 // This will commonly be used by CRTP (curiously-recurring template pattern)
261 // e.g., class MyClass : public RefCounted<MyClass>
262 //
263 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between
264 // different implementations of RefCounted.
265 //
266 // Note that NonPolymorphicRefCount does not support polymorphic destruction.
267 // So, use NonPolymorphicRefCount only when both of the following conditions
268 // are guaranteed to hold:
269 // (a) Child is a concrete leaf class in RefCounted<Child>, and
270 // (b) you are guaranteed to call Unref only on concrete leaf classes and not
271 //     their parents.
272 //
273 // The following example is illegal, because calling Unref() will not call
274 // the dtor of Child.
275 //
276 //    class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
277 //    class Child : public Parent {}
278 //
279 //    Child* ch;
280 //    ch->Unref();
281 //
282 template <typename Child, typename Impl = PolymorphicRefCount,
283           typename UnrefBehavior = UnrefDelete>
284 class RefCounted : public Impl {
285  public:
286   using RefCountedChildType = Child;
287   using RefCountedUnrefBehaviorType = UnrefBehavior;
288 
289   // Not copyable nor movable.
290   RefCounted(const RefCounted&) = delete;
291   RefCounted& operator=(const RefCounted&) = delete;
292 
293   // Note: Depending on the Impl used, this dtor can be implicitly virtual.
294   ~RefCounted() = default;
295 
296   // Ref() for mutable types.
Ref()297   GRPC_MUST_USE_RESULT RefCountedPtr<Child> Ref() {
298     IncrementRefCount();
299     return RefCountedPtr<Child>(static_cast<Child*>(this));
300   }
Ref(const DebugLocation & location,const char * reason)301   GRPC_MUST_USE_RESULT RefCountedPtr<Child> Ref(const DebugLocation& location,
302                                                 const char* reason) {
303     IncrementRefCount(location, reason);
304     return RefCountedPtr<Child>(static_cast<Child*>(this));
305   }
306 
307   // Ref() for const types.
Ref()308   GRPC_MUST_USE_RESULT RefCountedPtr<const Child> Ref() const {
309     IncrementRefCount();
310     return RefCountedPtr<const Child>(static_cast<const Child*>(this));
311   }
Ref(const DebugLocation & location,const char * reason)312   GRPC_MUST_USE_RESULT RefCountedPtr<const Child> Ref(
313       const DebugLocation& location, const char* reason) const {
314     IncrementRefCount(location, reason);
315     return RefCountedPtr<const Child>(static_cast<const Child*>(this));
316   }
317 
318   template <
319       typename Subclass,
320       std::enable_if_t<std::is_base_of<Child, Subclass>::value, bool> = true>
RefAsSubclass()321   RefCountedPtr<Subclass> RefAsSubclass() {
322     IncrementRefCount();
323     return RefCountedPtr<Subclass>(
324         DownCast<Subclass*>(static_cast<Child*>(this)));
325   }
326   template <
327       typename Subclass,
328       std::enable_if_t<std::is_base_of<Child, Subclass>::value, bool> = true>
RefAsSubclass(const DebugLocation & location,const char * reason)329   RefCountedPtr<Subclass> RefAsSubclass(const DebugLocation& location,
330                                         const char* reason) {
331     IncrementRefCount(location, reason);
332     return RefCountedPtr<Subclass>(
333         DownCast<Subclass*>(static_cast<Child*>(this)));
334   }
335 
336   // RefIfNonZero() for mutable types.
RefIfNonZero()337   GRPC_MUST_USE_RESULT RefCountedPtr<Child> RefIfNonZero() {
338     return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this)
339                                                      : nullptr);
340   }
RefIfNonZero(const DebugLocation & location,const char * reason)341   GRPC_MUST_USE_RESULT RefCountedPtr<Child> RefIfNonZero(
342       const DebugLocation& location, const char* reason) {
343     return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason)
344                                     ? static_cast<Child*>(this)
345                                     : nullptr);
346   }
347 
348   // RefIfNonZero() for const types.
RefIfNonZero()349   GRPC_MUST_USE_RESULT RefCountedPtr<const Child> RefIfNonZero() const {
350     return RefCountedPtr<const Child>(
351         refs_.RefIfNonZero() ? static_cast<const Child*>(this) : nullptr);
352   }
RefIfNonZero(const DebugLocation & location,const char * reason)353   GRPC_MUST_USE_RESULT RefCountedPtr<const Child> RefIfNonZero(
354       const DebugLocation& location, const char* reason) const {
355     return RefCountedPtr<const Child>(refs_.RefIfNonZero(location, reason)
356                                           ? static_cast<const Child*>(this)
357                                           : nullptr);
358   }
359 
360   // TODO(roth): Once all of our code is converted to C++ and can use
361   // RefCountedPtr<> instead of manual ref-counting, make this method
362   // private, since it will only be used by RefCountedPtr<>, which is a
363   // friend of this class.
Unref()364   void Unref() const {
365     if (GPR_UNLIKELY(refs_.Unref())) {
366       unref_behavior_(static_cast<const Child*>(this));
367     }
368   }
Unref(const DebugLocation & location,const char * reason)369   void Unref(const DebugLocation& location, const char* reason) const {
370     if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
371       unref_behavior_(static_cast<const Child*>(this));
372     }
373   }
374 
375  protected:
376   // Note: Tracing is a no-op on non-debug builds.
377   explicit RefCounted(const char* trace = nullptr,
378                       intptr_t initial_refcount = 1)
refs_(initial_refcount,trace)379       : refs_(initial_refcount, trace) {}
380 
381   // Note: Tracing is a no-op on non-debug builds.
382   explicit RefCounted(UnrefBehavior b, const char* trace = nullptr,
383                       intptr_t initial_refcount = 1)
refs_(initial_refcount,trace)384       : refs_(initial_refcount, trace), unref_behavior_(b) {}
385 
386  private:
387   // Allow RefCountedPtr<> to access IncrementRefCount().
388   template <typename T>
389   friend class RefCountedPtr;
390 
IncrementRefCount()391   void IncrementRefCount() const { refs_.Ref(); }
IncrementRefCount(const DebugLocation & location,const char * reason)392   void IncrementRefCount(const DebugLocation& location,
393                          const char* reason) const {
394     refs_.Ref(location, reason);
395   }
396 
397   mutable RefCount refs_;
398   GPR_NO_UNIQUE_ADDRESS UnrefBehavior unref_behavior_;
399 };
400 
401 }  // namespace grpc_core
402 
403 #endif  // GRPC_SRC_CORE_UTIL_REF_COUNTED_H
404