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/debug/trace.h"
33 #include "src/core/lib/gprpp/atomic.h"
34 #include "src/core/lib/gprpp/debug_location.h"
35 #include "src/core/lib/gprpp/memory.h"
36 #include "src/core/lib/gprpp/ref_counted_ptr.h"
37 
38 namespace grpc_core {
39 
40 // PolymorphicRefCount enforces polymorphic destruction of RefCounted.
41 class PolymorphicRefCount {
42  public:
43   virtual ~PolymorphicRefCount() = default;
44 };
45 
46 // NonPolymorphicRefCount does not enforce polymorphic destruction of
47 // RefCounted. Please refer to grpc_core::RefCounted for more details, and
48 // when in doubt use PolymorphicRefCount.
49 class NonPolymorphicRefCount {
50  public:
51   ~NonPolymorphicRefCount() = default;
52 };
53 
54 // RefCount is a simple atomic ref-count.
55 //
56 // This is a C++ implementation of gpr_refcount, with inline functions. Due to
57 // inline functions, this class is significantly more efficient than
58 // gpr_refcount and should be preferred over gpr_refcount whenever possible.
59 //
60 // TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph
61 //               above.
62 class RefCount {
63  public:
64   using Value = intptr_t;
65 
66   // `init` is the initial refcount stored in this object.
67   //
68   // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag.
69   // Note: RefCount tracing is only enabled on debug builds, even when a
70   //       TraceFlag is used.
71   template <typename TraceFlagT = TraceFlag>
72   constexpr explicit RefCount(
73       Value init = 1,
74       TraceFlagT*
75 #ifndef NDEBUG
76           // Leave unnamed if NDEBUG to avoid unused parameter warning
77           trace_flag
78 #endif
79       = nullptr)
80       :
81 #ifndef NDEBUG
trace_flag_(trace_flag)82         trace_flag_(trace_flag),
83 #endif
84         value_(init) {
85   }
86 
87   // Increases the ref-count by `n`.
88   void Ref(Value n = 1) {
89 #ifndef NDEBUG
90     const Value prior = value_.FetchAdd(n, MemoryOrder::RELAXED);
91     if (trace_flag_ != nullptr && trace_flag_->enabled()) {
92       gpr_log(GPR_INFO, "%s:%p ref %" PRIdPTR " -> %" PRIdPTR,
93               trace_flag_->name(), this, prior, prior + n);
94     }
95 #else
96     value_.FetchAdd(n, MemoryOrder::RELAXED);
97 #endif
98   }
99   void Ref(const DebugLocation& location, const char* reason, Value n = 1) {
100 #ifndef NDEBUG
101     const Value prior = value_.FetchAdd(n, MemoryOrder::RELAXED);
102     if (trace_flag_ != nullptr && trace_flag_->enabled()) {
103       gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
104               trace_flag_->name(), this, location.file(), location.line(),
105               prior, prior + n, reason);
106     }
107 #else
108     // Use conditionally-important parameters
109     (void)location;
110     (void)reason;
111 
112     value_.FetchAdd(n, MemoryOrder::RELAXED);
113 #endif
114   }
115 
116   // Similar to Ref() with an assert on the ref-count being non-zero.
RefNonZero()117   void RefNonZero() {
118 #ifndef NDEBUG
119     const Value prior = value_.FetchAdd(1, MemoryOrder::RELAXED);
120     if (trace_flag_ != nullptr && trace_flag_->enabled()) {
121       gpr_log(GPR_INFO, "%s:%p ref %" PRIdPTR " -> %" PRIdPTR,
122               trace_flag_->name(), this, prior, prior + 1);
123     }
124     assert(prior > 0);
125 #else
126     value_.FetchAdd(1, MemoryOrder::RELAXED);
127 #endif
128   }
RefNonZero(const DebugLocation & location,const char * reason)129   void RefNonZero(const DebugLocation& location, const char* reason) {
130 #ifndef NDEBUG
131     const Value prior = value_.FetchAdd(1, MemoryOrder::RELAXED);
132     if (trace_flag_ != nullptr && trace_flag_->enabled()) {
133       gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
134               trace_flag_->name(), this, location.file(), location.line(),
135               prior, prior + 1, reason);
136     }
137     assert(prior > 0);
138 #else
139     // Avoid unused-parameter warnings for debug-only parameters
140     (void)location;
141     (void)reason;
142     RefNonZero();
143 #endif
144   }
145 
RefIfNonZero()146   bool RefIfNonZero() {
147 #ifndef NDEBUG
148     if (trace_flag_ != nullptr && trace_flag_->enabled()) {
149       const Value prior = get();
150       gpr_log(GPR_INFO, "%s:%p ref_if_non_zero %" PRIdPTR " -> %" PRIdPTR,
151               trace_flag_->name(), this, prior, prior + 1);
152     }
153 #endif
154     return value_.IncrementIfNonzero();
155   }
RefIfNonZero(const DebugLocation & location,const char * reason)156   bool RefIfNonZero(const DebugLocation& location, const char* reason) {
157 #ifndef NDEBUG
158     if (trace_flag_ != nullptr && trace_flag_->enabled()) {
159       const Value prior = get();
160       gpr_log(GPR_INFO,
161               "%s:%p %s:%d ref_if_non_zero "
162               "%" PRIdPTR " -> %" PRIdPTR " %s",
163               trace_flag_->name(), this, location.file(), location.line(),
164               prior, prior + 1, reason);
165     }
166 #endif
167     // Avoid unused-parameter warnings for debug-only parameters
168     (void)location;
169     (void)reason;
170     return value_.IncrementIfNonzero();
171   }
172 
173   // Decrements the ref-count and returns true if the ref-count reaches 0.
Unref()174   bool Unref() {
175 #ifndef NDEBUG
176     // Grab a copy of the trace flag before the atomic change, since we
177     // can't safely access it afterwards if we're going to be freed.
178     auto* trace_flag = trace_flag_;
179 #endif
180     const Value prior = value_.FetchSub(1, MemoryOrder::ACQ_REL);
181 #ifndef NDEBUG
182     if (trace_flag != nullptr && trace_flag->enabled()) {
183       gpr_log(GPR_INFO, "%s:%p unref %" PRIdPTR " -> %" PRIdPTR,
184               trace_flag->name(), this, prior, prior - 1);
185     }
186     GPR_DEBUG_ASSERT(prior > 0);
187 #endif
188     return prior == 1;
189   }
Unref(const DebugLocation & location,const char * reason)190   bool Unref(const DebugLocation& location, const char* reason) {
191 #ifndef NDEBUG
192     // Grab a copy of the trace flag before the atomic change, since we
193     // can't safely access it afterwards if we're going to be freed.
194     auto* trace_flag = trace_flag_;
195 #endif
196     const Value prior = value_.FetchSub(1, MemoryOrder::ACQ_REL);
197 #ifndef NDEBUG
198     if (trace_flag != nullptr && trace_flag->enabled()) {
199       gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s",
200               trace_flag->name(), this, location.file(), location.line(), prior,
201               prior - 1, reason);
202     }
203     GPR_DEBUG_ASSERT(prior > 0);
204 #else
205     // Avoid unused-parameter warnings for debug-only parameters
206     (void)location;
207     (void)reason;
208 #endif
209     return prior == 1;
210   }
211 
212  private:
get()213   Value get() const { return value_.Load(MemoryOrder::RELAXED); }
214 
215 #ifndef NDEBUG
216   TraceFlag* trace_flag_;
217 #endif
218   Atomic<Value> value_;
219 };
220 
221 // A base class for reference-counted objects.
222 // New objects should be created via New() and start with a refcount of 1.
223 // When the refcount reaches 0, the object will be deleted via delete .
224 //
225 // This will commonly be used by CRTP (curiously-recurring template pattern)
226 // e.g., class MyClass : public RefCounted<MyClass>
227 //
228 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between
229 // different implementations of RefCounted.
230 //
231 // Note that NonPolymorphicRefCount does not support polymorphic destruction.
232 // So, use NonPolymorphicRefCount only when both of the following conditions
233 // are guaranteed to hold:
234 // (a) Child is a concrete leaf class in RefCounted<Child>, and
235 // (b) you are guaranteed to call Unref only on concrete leaf classes and not
236 //     their parents.
237 //
238 // The following example is illegal, because calling Unref() will not call
239 // the dtor of Child.
240 //
241 //    class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
242 //    class Child : public Parent {}
243 //
244 //    Child* ch;
245 //    ch->Unref();
246 //
247 template <typename Child, typename Impl = PolymorphicRefCount>
248 class RefCounted : public Impl {
249  public:
250   // Note: Depending on the Impl used, this dtor can be implicitly virtual.
251   ~RefCounted() = default;
252 
Ref()253   RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
254     IncrementRefCount();
255     return RefCountedPtr<Child>(static_cast<Child*>(this));
256   }
257 
Ref(const DebugLocation & location,const char * reason)258   RefCountedPtr<Child> Ref(const DebugLocation& location,
259                            const char* reason) GRPC_MUST_USE_RESULT {
260     IncrementRefCount(location, reason);
261     return RefCountedPtr<Child>(static_cast<Child*>(this));
262   }
263 
264   // TODO(roth): Once all of our code is converted to C++ and can use
265   // RefCountedPtr<> instead of manual ref-counting, make this method
266   // private, since it will only be used by RefCountedPtr<>, which is a
267   // friend of this class.
Unref()268   void Unref() {
269     if (GPR_UNLIKELY(refs_.Unref())) {
270       delete static_cast<Child*>(this);
271     }
272   }
Unref(const DebugLocation & location,const char * reason)273   void Unref(const DebugLocation& location, const char* reason) {
274     if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
275       delete static_cast<Child*>(this);
276     }
277   }
278 
RefIfNonZero()279   bool RefIfNonZero() { return refs_.RefIfNonZero(); }
RefIfNonZero(const DebugLocation & location,const char * reason)280   bool RefIfNonZero(const DebugLocation& location, const char* reason) {
281     return refs_.RefIfNonZero(location, reason);
282   }
283 
284   // Not copyable nor movable.
285   RefCounted(const RefCounted&) = delete;
286   RefCounted& operator=(const RefCounted&) = delete;
287 
288  protected:
289   // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag.
290   // Note: RefCount tracing is only enabled on debug builds, even when a
291   //       TraceFlag is used.
292   template <typename TraceFlagT = TraceFlag>
293   explicit RefCounted(TraceFlagT* trace_flag = nullptr,
294                       intptr_t initial_refcount = 1)
refs_(initial_refcount,trace_flag)295       : refs_(initial_refcount, trace_flag) {}
296 
297  private:
298   // Allow RefCountedPtr<> to access IncrementRefCount().
299   template <typename T>
300   friend class RefCountedPtr;
301 
IncrementRefCount()302   void IncrementRefCount() { refs_.Ref(); }
IncrementRefCount(const DebugLocation & location,const char * reason)303   void IncrementRefCount(const DebugLocation& location, const char* reason) {
304     refs_.Ref(location, reason);
305   }
306 
307   RefCount refs_;
308 };
309 
310 }  // namespace grpc_core
311 
312 #endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */
313