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