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/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"
39 // RefCount is a simple atomic ref-count.
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.
45 // TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph
49 using Value = intptr_t;
51 // `init` is the initial refcount stored in this object.
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.
59 // Leave unnamed if NDEBUG to avoid unused parameter warning
70 // Increases the ref-count by `n`.
71 void Ref(Value n = 1) {
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,
79 value_.FetchAdd(n, MemoryOrder::RELAXED);
82 void Ref(const DebugLocation& location, const char* reason, Value n = 1) {
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,
91 // Use conditionally-important parameters
94 value_.FetchAdd(n, MemoryOrder::RELAXED);
98 // Similar to Ref() with an assert on the ref-count being non-zero.
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,
108 value_.FetchAdd(1, MemoryOrder::RELAXED);
111 void RefNonZero(const DebugLocation& location, const char* reason) {
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,
121 // Avoid unused-parameter warnings for debug-only parameters
128 bool RefIfNonZero() {
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);
136 return value_.IncrementIfNonzero();
138 bool RefIfNonZero(const DebugLocation& location, const char* reason) {
140 if (trace_ != nullptr) {
141 const Value prior = get();
143 "%s:%p %s:%d ref_if_non_zero %" PRIdPTR " -> %" PRIdPTR " %s",
144 trace_, this, location.file(), location.line(), prior, prior + 1,
148 // Avoid unused-parameter warnings for debug-only parameters
151 return value_.IncrementIfNonzero();
154 // Decrements the ref-count and returns true if the ref-count reaches 0.
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_;
162 const Value prior = value_.FetchSub(1, MemoryOrder::ACQ_REL);
164 if (trace != nullptr) {
165 gpr_log(GPR_INFO, "%s:%p unref %" PRIdPTR " -> %" PRIdPTR, trace, this,
168 GPR_DEBUG_ASSERT(prior > 0);
172 bool Unref(const DebugLocation& location, const char* reason) {
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_;
179 const Value prior = value_.FetchSub(1, MemoryOrder::ACQ_REL);
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,
186 GPR_DEBUG_ASSERT(prior > 0);
188 // Avoid unused-parameter warnings for debug-only parameters
196 Value get() const { return value_.Load(MemoryOrder::RELAXED); }
201 Atomic<Value> value_;
204 // PolymorphicRefCount enforces polymorphic destruction of RefCounted.
205 class PolymorphicRefCount {
207 virtual ~PolymorphicRefCount() = default;
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 {
215 ~NonPolymorphicRefCount() = default;
219 template <typename T, bool DoDelete>
221 template <typename T>
222 class Delete<T, true> {
224 explicit Delete(T* t) { delete t; }
226 template <typename T>
227 class Delete<T, false> {
229 explicit Delete(T* /*t*/) {}
231 } // namespace internal
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.
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.
244 // This will commonly be used by CRTP (curiously-recurring template pattern)
245 // e.g., class MyClass : public RefCounted<MyClass>
247 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between
248 // different implementations of RefCounted.
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
257 // The following example is illegal, because calling Unref() will not call
258 // the dtor of Child.
260 // class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {}
261 // class Child : public Parent {}
266 template <typename Child, typename Impl = PolymorphicRefCount,
267 bool DeleteUponUnref = true>
268 class RefCounted : public Impl {
270 // Note: Depending on the Impl used, this dtor can be implicitly virtual.
271 ~RefCounted() = default;
273 RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
275 return RefCountedPtr<Child>(static_cast<Child*>(this));
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));
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.
289 if (GPR_UNLIKELY(refs_.Unref())) {
290 internal::Delete<Child, DeleteUponUnref>(static_cast<Child*>(this));
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));
299 RefCountedPtr<Child> RefIfNonZero() GRPC_MUST_USE_RESULT {
300 return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this)
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)
310 // Not copyable nor movable.
311 RefCounted(const RefCounted&) = delete;
312 RefCounted& operator=(const RefCounted&) = delete;
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) {}
321 // Allow RefCountedPtr<> to access IncrementRefCount().
322 template <typename T>
323 friend class RefCountedPtr;
325 void IncrementRefCount() { refs_.Ref(); }
326 void IncrementRefCount(const DebugLocation& location, const char* reason) {
327 refs_.Ref(location, reason);
333 } // namespace grpc_core
335 #endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */