3 * Copyright 2017 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
20 #define GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
22 #include <grpc/support/port_platform.h>
24 #include <grpc/support/atm.h>
25 #include <grpc/support/log.h>
26 #include <grpc/support/sync.h>
32 #include "src/core/lib/debug/trace.h"
33 #include "src/core/lib/gprpp/abstract.h"
34 #include "src/core/lib/gprpp/atomic.h"
35 #include "src/core/lib/gprpp/debug_location.h"
36 #include "src/core/lib/gprpp/memory.h"
37 #include "src/core/lib/gprpp/ref_counted_ptr.h"
41 // PolymorphicRefCount enforces polymorphic destruction of RefCounted.
42 class PolymorphicRefCount {
44 GRPC_ABSTRACT_BASE_CLASS
47 GRPC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
49 virtual ~PolymorphicRefCount() = default;
52 // NonPolymorphicRefCount does not enforce polymorphic destruction of
53 // RefCounted. Please refer to grpc_core::RefCounted for more details, and
54 // when in doubt use PolymorphicRefCount.
55 class NonPolymorphicRefCount {
57 GRPC_ABSTRACT_BASE_CLASS
60 GRPC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
62 ~NonPolymorphicRefCount() = default;
65 // RefCount is a simple atomic ref-count.
67 // This is a C++ implementation of gpr_refcount, with inline functions. Due to
68 // inline functions, this class is significantly more efficient than
69 // gpr_refcount and should be preferred over gpr_refcount whenever possible.
71 // TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph
75 using Value = intptr_t;
77 // `init` is the initial refcount stored in this object.
79 // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag.
80 // Note: RefCount tracing is only enabled on debug builds, even when a
82 template <typename TraceFlagT = TraceFlag>
83 constexpr explicit RefCount(Value init = 1, TraceFlagT* trace_flag = nullptr)
86 trace_flag_(trace_flag),
91 // Increases the ref-count by `n`.
92 void Ref(Value n = 1) {
94 const Value prior = value_.FetchAdd(n, MemoryOrder::RELAXED);
95 if (trace_flag_ != nullptr && trace_flag_->enabled()) {
96 gpr_log(GPR_INFO, "%s:%p ref %" PRIdPTR " -> %" PRIdPTR,
97 trace_flag_->name(), this, prior, prior + n);
100 value_.FetchAdd(n, MemoryOrder::RELAXED);
103 void Ref(const DebugLocation& location, const char* reason, Value n = 1) {
105 const Value prior = value_.FetchAdd(n, MemoryOrder::RELAXED);
106 if (trace_flag_ != nullptr && trace_flag_->enabled()) {
107 gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
108 trace_flag_->name(), this, location.file(), location.line(),
109 prior, prior + n, reason);
112 value_.FetchAdd(n, MemoryOrder::RELAXED);
116 // Similar to Ref() with an assert on the ref-count being non-zero.
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);
126 value_.FetchAdd(1, MemoryOrder::RELAXED);
129 void RefNonZero(const DebugLocation& location, const char* reason) {
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);
143 bool RefIfNonZero() {
145 if (trace_flag_ != nullptr && trace_flag_->enabled()) {
146 const Value prior = get();
147 gpr_log(GPR_INFO, "%s:%p ref_if_non_zero %" PRIdPTR " -> %" PRIdPTR,
148 trace_flag_->name(), this, prior, prior + 1);
151 return value_.IncrementIfNonzero();
153 bool RefIfNonZero(const DebugLocation& location, const char* reason) {
155 if (trace_flag_ != nullptr && trace_flag_->enabled()) {
156 const Value prior = get();
158 "%s:%p %s:%d ref_if_non_zero "
159 "%" PRIdPTR " -> %" PRIdPTR " %s",
160 trace_flag_->name(), this, location.file(), location.line(),
161 prior, prior + 1, reason);
164 return value_.IncrementIfNonzero();
167 // Decrements the ref-count and returns true if the ref-count reaches 0.
170 // Grab a copy of the trace flag before the atomic change, since we
171 // can't safely access it afterwards if we're going to be freed.
172 auto* trace_flag = trace_flag_;
174 const Value prior = value_.FetchSub(1, MemoryOrder::ACQ_REL);
176 if (trace_flag != nullptr && trace_flag->enabled()) {
177 gpr_log(GPR_INFO, "%s:%p unref %" PRIdPTR " -> %" PRIdPTR,
178 trace_flag->name(), this, prior, prior - 1);
180 GPR_DEBUG_ASSERT(prior > 0);
184 bool Unref(const DebugLocation& location, const char* reason) {
186 // Grab a copy of the trace flag before the atomic change, since we
187 // can't safely access it afterwards if we're going to be freed.
188 auto* trace_flag = trace_flag_;
190 const Value prior = value_.FetchSub(1, MemoryOrder::ACQ_REL);
192 if (trace_flag != nullptr && trace_flag->enabled()) {
193 gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s",
194 trace_flag->name(), this, location.file(), location.line(), prior,
197 GPR_DEBUG_ASSERT(prior > 0);
203 Value get() const { return value_.Load(MemoryOrder::RELAXED); }
206 TraceFlag* trace_flag_;
208 Atomic<Value> value_;
211 // A base class for reference-counted objects.
212 // New objects should be created via New() and start with a refcount of 1.
213 // When the refcount reaches 0, the object will be deleted via Delete().
215 // This will commonly be used by CRTP (curiously-recurring template pattern)
216 // e.g., class MyClass : public RefCounted<MyClass>
218 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between
219 // different implementations of RefCounted.
221 // Note that NonPolymorphicRefCount does not support polymorphic destruction.
222 // So, use NonPolymorphicRefCount only when both of the following conditions
223 // are guaranteed to hold:
224 // (a) Child is a concrete leaf class in RefCounted<Child>, and
225 // (b) you are guaranteed to call Unref only on concrete leaf classes and not
228 // The following example is illegal, because calling Unref() will not call
229 // the dtor of Child.
231 // class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
232 // class Child : public Parent {}
237 template <typename Child, typename Impl = PolymorphicRefCount>
238 class RefCounted : public Impl {
240 RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
242 return RefCountedPtr<Child>(static_cast<Child*>(this));
245 RefCountedPtr<Child> Ref(const DebugLocation& location,
246 const char* reason) GRPC_MUST_USE_RESULT {
247 IncrementRefCount(location, reason);
248 return RefCountedPtr<Child>(static_cast<Child*>(this));
251 // TODO(roth): Once all of our code is converted to C++ and can use
252 // RefCountedPtr<> instead of manual ref-counting, make this method
253 // private, since it will only be used by RefCountedPtr<>, which is a
254 // friend of this class.
256 if (GPR_UNLIKELY(refs_.Unref())) {
257 Delete(static_cast<Child*>(this));
260 void Unref(const DebugLocation& location, const char* reason) {
261 if (GPR_UNLIKELY(refs_.Unref(location, reason))) {
262 Delete(static_cast<Child*>(this));
266 bool RefIfNonZero() { return refs_.RefIfNonZero(); }
267 bool RefIfNonZero(const DebugLocation& location, const char* reason) {
268 return refs_.RefIfNonZero(location, reason);
271 // Not copyable nor movable.
272 RefCounted(const RefCounted&) = delete;
273 RefCounted& operator=(const RefCounted&) = delete;
275 GRPC_ABSTRACT_BASE_CLASS
278 GRPC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE
280 // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag.
281 // Note: RefCount tracing is only enabled on debug builds, even when a
282 // TraceFlag is used.
283 template <typename TraceFlagT = TraceFlag>
284 explicit RefCounted(TraceFlagT* trace_flag = nullptr,
285 intptr_t initial_refcount = 1)
286 : refs_(initial_refcount, trace_flag) {}
288 // Note: Depending on the Impl used, this dtor can be implicitly virtual.
289 ~RefCounted() = default;
292 // Allow RefCountedPtr<> to access IncrementRefCount().
293 template <typename T>
294 friend class RefCountedPtr;
296 void IncrementRefCount() { refs_.Ref(); }
297 void IncrementRefCount(const DebugLocation& location, const char* reason) {
298 refs_.Ref(location, reason);
304 } // namespace grpc_core
306 #endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */