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_SRC_CORE_LIB_GPRPP_REF_COUNTED_H 20 #define GRPC_SRC_CORE_LIB_GPRPP_REF_COUNTED_H 21 22 #include <grpc/support/port_platform.h> 23 24 #include <atomic> 25 #include <cassert> 26 #include <cinttypes> 27 28 #include <grpc/support/log.h> 29 30 #include "src/core/lib/gprpp/atomic_utils.h" 31 #include "src/core/lib/gprpp/debug_location.h" 32 #include "src/core/lib/gprpp/down_cast.h" 33 #include "src/core/lib/gprpp/ref_counted_ptr.h" 34 35 namespace grpc_core { 36 37 // RefCount is a simple atomic ref-count. 38 // 39 // This is a C++ implementation of gpr_refcount, with inline functions. Due to 40 // inline functions, this class is significantly more efficient than 41 // gpr_refcount and should be preferred over gpr_refcount whenever possible. 42 // 43 // TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph 44 // above. 45 class RefCount { 46 public: 47 using Value = intptr_t; 48 RefCount()49 RefCount() : RefCount(1) {} 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, 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 trace_(trace)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_.fetch_add(n, std::memory_order_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_.fetch_add(n, std::memory_order_relaxed); 80 #endif 81 } 82 void Ref(const DebugLocation& location, const char* reason, Value n = 1) { 83 #ifndef NDEBUG 84 const Value prior = value_.fetch_add(n, std::memory_order_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_.fetch_add(n, std::memory_order_relaxed); 95 #endif 96 } 97 98 // Similar to Ref() with an assert on the ref-count being non-zero. RefNonZero()99 void RefNonZero() { 100 #ifndef NDEBUG 101 const Value prior = value_.fetch_add(1, std::memory_order_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_.fetch_add(1, std::memory_order_relaxed); 109 #endif 110 } RefNonZero(const DebugLocation & location,const char * reason)111 void RefNonZero(const DebugLocation& location, const char* reason) { 112 #ifndef NDEBUG 113 const Value prior = value_.fetch_add(1, std::memory_order_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 RefIfNonZero()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 IncrementIfNonzero(&value_); 137 } RefIfNonZero(const DebugLocation & location,const char * reason)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 IncrementIfNonzero(&value_); 152 } 153 154 // Decrements the ref-count and returns true if the ref-count reaches 0. Unref()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_.fetch_sub(1, std::memory_order_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 } Unref(const DebugLocation & location,const char * reason)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_.fetch_sub(1, std::memory_order_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: get()196 Value get() const { return value_.load(std::memory_order_relaxed); } 197 198 #ifndef NDEBUG 199 const char* trace_; 200 #endif 201 std::atomic<Value> value_{0}; 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 RefCounted for more details, and 212 // when in doubt use PolymorphicRefCount. 213 class NonPolymorphicRefCount { 214 public: 215 ~NonPolymorphicRefCount() = default; 216 }; 217 218 // Behavior of RefCounted<> upon ref count reaching 0. 219 220 // Default behavior: Delete the object. 221 struct UnrefDelete { 222 template <typename T> operatorUnrefDelete223 void operator()(T* p) const { 224 delete p; 225 } 226 }; 227 228 // Do not delete the object upon unref. This is useful in cases where all 229 // existing objects must be tracked in a registry but the object's entry in 230 // the registry cannot be removed from the object's dtor due to 231 // synchronization issues. In this case, the registry can be cleaned up 232 // later by identifying entries for which RefIfNonZero() returns null. 233 struct UnrefNoDelete { 234 template <typename T> operatorUnrefNoDelete235 void operator()(T* /*p*/) const {} 236 }; 237 238 // Call the object's dtor but do not delete it. This is useful for cases 239 // where the object is stored in memory allocated elsewhere (e.g., the call 240 // arena). 241 struct UnrefCallDtor { 242 template <typename T> operatorUnrefCallDtor243 void operator()(T* p) const { 244 p->~T(); 245 } 246 }; 247 248 // A base class for reference-counted objects. 249 // New objects should be created via new and start with a refcount of 1. 250 // When the refcount reaches 0, executes the specified UnrefBehavior. 251 // 252 // This will commonly be used by CRTP (curiously-recurring template pattern) 253 // e.g., class MyClass : public RefCounted<MyClass> 254 // 255 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between 256 // different implementations of RefCounted. 257 // 258 // Note that NonPolymorphicRefCount does not support polymorphic destruction. 259 // So, use NonPolymorphicRefCount only when both of the following conditions 260 // are guaranteed to hold: 261 // (a) Child is a concrete leaf class in RefCounted<Child>, and 262 // (b) you are guaranteed to call Unref only on concrete leaf classes and not 263 // their parents. 264 // 265 // The following example is illegal, because calling Unref() will not call 266 // the dtor of Child. 267 // 268 // class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {} 269 // class Child : public Parent {} 270 // 271 // Child* ch; 272 // ch->Unref(); 273 // 274 template <typename Child, typename Impl = PolymorphicRefCount, 275 typename UnrefBehavior = UnrefDelete> 276 class RefCounted : public Impl { 277 public: 278 using RefCountedChildType = Child; 279 280 // Not copyable nor movable. 281 RefCounted(const RefCounted&) = delete; 282 RefCounted& operator=(const RefCounted&) = delete; 283 284 // Note: Depending on the Impl used, this dtor can be implicitly virtual. 285 ~RefCounted() = default; 286 287 // Ref() for mutable types. Ref()288 GRPC_MUST_USE_RESULT RefCountedPtr<Child> Ref() { 289 IncrementRefCount(); 290 return RefCountedPtr<Child>(static_cast<Child*>(this)); 291 } Ref(const DebugLocation & location,const char * reason)292 GRPC_MUST_USE_RESULT RefCountedPtr<Child> Ref(const DebugLocation& location, 293 const char* reason) { 294 IncrementRefCount(location, reason); 295 return RefCountedPtr<Child>(static_cast<Child*>(this)); 296 } 297 298 // Ref() for const types. Ref()299 GRPC_MUST_USE_RESULT RefCountedPtr<const Child> Ref() const { 300 IncrementRefCount(); 301 return RefCountedPtr<const Child>(static_cast<const Child*>(this)); 302 } Ref(const DebugLocation & location,const char * reason)303 GRPC_MUST_USE_RESULT RefCountedPtr<const Child> Ref( 304 const DebugLocation& location, const char* reason) const { 305 IncrementRefCount(location, reason); 306 return RefCountedPtr<const Child>(static_cast<const Child*>(this)); 307 } 308 309 template < 310 typename Subclass, 311 std::enable_if_t<std::is_base_of<Child, Subclass>::value, bool> = true> RefAsSubclass()312 RefCountedPtr<Subclass> RefAsSubclass() { 313 IncrementRefCount(); 314 return RefCountedPtr<Subclass>( 315 DownCast<Subclass*>(static_cast<Child*>(this))); 316 } 317 template < 318 typename Subclass, 319 std::enable_if_t<std::is_base_of<Child, Subclass>::value, bool> = true> RefAsSubclass(const DebugLocation & location,const char * reason)320 RefCountedPtr<Subclass> RefAsSubclass(const DebugLocation& location, 321 const char* reason) { 322 IncrementRefCount(location, reason); 323 return RefCountedPtr<Subclass>( 324 DownCast<Subclass*>(static_cast<Child*>(this))); 325 } 326 327 // RefIfNonZero() for mutable types. RefIfNonZero()328 GRPC_MUST_USE_RESULT RefCountedPtr<Child> RefIfNonZero() { 329 return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this) 330 : nullptr); 331 } RefIfNonZero(const DebugLocation & location,const char * reason)332 GRPC_MUST_USE_RESULT RefCountedPtr<Child> RefIfNonZero( 333 const DebugLocation& location, const char* reason) { 334 return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason) 335 ? static_cast<Child*>(this) 336 : nullptr); 337 } 338 339 // RefIfNonZero() for const types. RefIfNonZero()340 GRPC_MUST_USE_RESULT RefCountedPtr<const Child> RefIfNonZero() const { 341 return RefCountedPtr<const Child>( 342 refs_.RefIfNonZero() ? static_cast<const Child*>(this) : nullptr); 343 } RefIfNonZero(const DebugLocation & location,const char * reason)344 GRPC_MUST_USE_RESULT RefCountedPtr<const Child> RefIfNonZero( 345 const DebugLocation& location, const char* reason) const { 346 return RefCountedPtr<const Child>(refs_.RefIfNonZero(location, reason) 347 ? static_cast<const Child*>(this) 348 : nullptr); 349 } 350 351 // TODO(roth): Once all of our code is converted to C++ and can use 352 // RefCountedPtr<> instead of manual ref-counting, make this method 353 // private, since it will only be used by RefCountedPtr<>, which is a 354 // friend of this class. Unref()355 void Unref() const { 356 if (GPR_UNLIKELY(refs_.Unref())) { 357 unref_behavior_(static_cast<const Child*>(this)); 358 } 359 } Unref(const DebugLocation & location,const char * reason)360 void Unref(const DebugLocation& location, const char* reason) const { 361 if (GPR_UNLIKELY(refs_.Unref(location, reason))) { 362 unref_behavior_(static_cast<const Child*>(this)); 363 } 364 } 365 366 protected: 367 // Note: Tracing is a no-op on non-debug builds. 368 explicit RefCounted(const char* trace = nullptr, 369 intptr_t initial_refcount = 1) refs_(initial_refcount,trace)370 : refs_(initial_refcount, trace) {} 371 372 // Note: Tracing is a no-op on non-debug builds. 373 explicit RefCounted(UnrefBehavior b, const char* trace = nullptr, 374 intptr_t initial_refcount = 1) refs_(initial_refcount,trace)375 : refs_(initial_refcount, trace), unref_behavior_(b) {} 376 377 private: 378 // Allow RefCountedPtr<> to access IncrementRefCount(). 379 template <typename T> 380 friend class RefCountedPtr; 381 IncrementRefCount()382 void IncrementRefCount() const { refs_.Ref(); } IncrementRefCount(const DebugLocation & location,const char * reason)383 void IncrementRefCount(const DebugLocation& location, 384 const char* reason) const { 385 refs_.Ref(location, reason); 386 } 387 388 mutable RefCount refs_; 389 GPR_NO_UNIQUE_ADDRESS UnrefBehavior unref_behavior_; 390 }; 391 392 } // namespace grpc_core 393 394 #endif // GRPC_SRC_CORE_LIB_GPRPP_REF_COUNTED_H 395