Imported Upstream version 1.36.0
[platform/upstream/grpc.git] / src / core / lib / gprpp / ref_counted.h
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
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.
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   }
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
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   }
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.
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   }
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:
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 namespace internal {
219 template <typename T, bool DoDelete>
220 class Delete;
221 template <typename T>
222 class Delete<T, true> {
223  public:
224   explicit Delete(T* t) { delete t; }
225 };
226 template <typename T>
227 class Delete<T, false> {
228  public:
229   explicit Delete(T* /*t*/) {}
230 };
231 }  // namespace internal
232
233 // A base class for reference-counted objects.
234 // New objects should be created via new and start with a refcount of 1.
235 // When the refcount reaches 0, the object will be deleted via delete.
236 //
237 // If DeleteUponUnref is false, deletion will not occur when the ref
238 // count reaches 0.  This is useful in cases where all existing objects
239 // must be tracked in a registry but the object's entry in the registry
240 // cannot be removed from the object's dtor due to synchronization issues.
241 // In this case, the registry can be cleaned up later by identifying
242 // entries for which RefIfNonZero() returns null.
243 //
244 // This will commonly be used by CRTP (curiously-recurring template pattern)
245 // e.g., class MyClass : public RefCounted<MyClass>
246 //
247 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between
248 // different implementations of RefCounted.
249 //
250 // Note that NonPolymorphicRefCount does not support polymorphic destruction.
251 // So, use NonPolymorphicRefCount only when both of the following conditions
252 // are guaranteed to hold:
253 // (a) Child is a concrete leaf class in RefCounted<Child>, and
254 // (b) you are guaranteed to call Unref only on concrete leaf classes and not
255 //     their parents.
256 //
257 // The following example is illegal, because calling Unref() will not call
258 // the dtor of Child.
259 //
260 //    class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
261 //    class Child : public Parent {}
262 //
263 //    Child* ch;
264 //    ch->Unref();
265 //
266 template <typename Child, typename Impl = PolymorphicRefCount,
267           bool DeleteUponUnref = true>
268 class RefCounted : public Impl {
269  public:
270   // Note: Depending on the Impl used, this dtor can be implicitly virtual.
271   ~RefCounted() = default;
272
273   RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
274     IncrementRefCount();
275     return RefCountedPtr<Child>(static_cast<Child*>(this));
276   }
277
278   RefCountedPtr<Child> Ref(const DebugLocation& location,
279                            const char* reason) GRPC_MUST_USE_RESULT {
280     IncrementRefCount(location, reason);
281     return RefCountedPtr<Child>(static_cast<Child*>(this));
282   }
283
284   // TODO(roth): Once all of our code is converted to C++ and can use
285   // RefCountedPtr<> instead of manual ref-counting, make this method
286   // private, since it will only be used by RefCountedPtr<>, which is a
287   // friend of this class.
288   void Unref() {
289     if (GPR_UNLIKELY(refs_.Unref())) {
290       internal::Delete<Child, DeleteUponUnref>(static_cast<Child*>(this));
291     }
292   }
293   void Unref(const DebugLocation& location, const char* reason) {
294     if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
295       internal::Delete<Child, DeleteUponUnref>(static_cast<Child*>(this));
296     }
297   }
298
299   RefCountedPtr<Child> RefIfNonZero() GRPC_MUST_USE_RESULT {
300     return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this)
301                                                      : nullptr);
302   }
303   RefCountedPtr<Child> RefIfNonZero(const DebugLocation& location,
304                                     const char* reason) GRPC_MUST_USE_RESULT {
305     return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason)
306                                     ? static_cast<Child*>(this)
307                                     : nullptr);
308   }
309
310   // Not copyable nor movable.
311   RefCounted(const RefCounted&) = delete;
312   RefCounted& operator=(const RefCounted&) = delete;
313
314  protected:
315   // Note: Tracing is a no-op on non-debug builds.
316   explicit RefCounted(const char* trace = nullptr,
317                       intptr_t initial_refcount = 1)
318       : refs_(initial_refcount, trace) {}
319
320  private:
321   // Allow RefCountedPtr<> to access IncrementRefCount().
322   template <typename T>
323   friend class RefCountedPtr;
324
325   void IncrementRefCount() { refs_.Ref(); }
326   void IncrementRefCount(const DebugLocation& location, const char* reason) {
327     refs_.Ref(location, reason);
328   }
329
330   RefCount refs_;
331 };
332
333 }  // namespace grpc_core
334
335 #endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */