• 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_CORE_LIB_GPRPP_REF_COUNTED_H
20 #define GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
21 
22 #include <grpc/support/port_platform.h>
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 
32 #include "src/core/lib/gprpp/atomic.h"
33 #include "src/core/lib/gprpp/debug_location.h"
34 #include "src/core/lib/gprpp/memory.h"
35 #include "src/core/lib/gprpp/ref_counted_ptr.h"
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
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_.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.
RefNonZero()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   }
RefNonZero(const DebugLocation & location,const char * reason)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 
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 value_.IncrementIfNonzero();
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 value_.IncrementIfNonzero();
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_.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   }
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_.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:
get()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.
205 class PolymorphicRefCount {
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.
213 class NonPolymorphicRefCount {
214  public:
215   ~NonPolymorphicRefCount() = default;
216 };
217 
218 // Behavior of RefCounted<> upon ref count reaching 0.
219 enum UnrefBehavior {
220   // Default behavior: Delete the object.
221   kUnrefDelete,
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.
227   kUnrefNoDelete,
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).
231   kUnrefCallDtor,
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:
Delete(T * t)240   explicit Delete(T* t) { delete t; }
241 };
242 template <typename T>
243 class Delete<T, kUnrefNoDelete> {
244  public:
Delete(T *)245   explicit Delete(T* /*t*/) {}
246 };
247 template <typename T>
248 class Delete<T, kUnrefCallDtor> {
249  public:
Delete(T * t)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 
Ref()287   RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
288     IncrementRefCount();
289     return RefCountedPtr<Child>(static_cast<Child*>(this));
290   }
291 
Ref(const DebugLocation & location,const char * reason)292   RefCountedPtr<Child> Ref(const DebugLocation& location,
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.
Unref()302   void Unref() {
303     if (GPR_UNLIKELY(refs_.Unref())) {
304       internal::Delete<Child, UnrefBehaviorArg>(static_cast<Child*>(this));
305     }
306   }
Unref(const DebugLocation & location,const char * reason)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 
RefIfNonZero()313   RefCountedPtr<Child> RefIfNonZero() GRPC_MUST_USE_RESULT {
314     return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this)
315                                                      : nullptr);
316   }
RefIfNonZero(const DebugLocation & location,const char * reason)317   RefCountedPtr<Child> RefIfNonZero(const DebugLocation& location,
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)
refs_(initial_refcount,trace)332       : refs_(initial_refcount, trace) {}
333 
334  private:
335   // Allow RefCountedPtr<> to access IncrementRefCount().
336   template <typename T>
337   friend class RefCountedPtr;
338 
IncrementRefCount()339   void IncrementRefCount() { refs_.Ref(); }
IncrementRefCount(const DebugLocation & location,const char * reason)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 */
350