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_UTIL_REF_COUNTED_H 20 #define GRPC_SRC_CORE_UTIL_REF_COUNTED_H 21 22 #include <grpc/support/port_platform.h> 23 24 #include <atomic> 25 #include <cassert> 26 #include <cinttypes> 27 28 #include "absl/log/check.h" 29 #include "absl/log/log.h" 30 #include "src/core/util/atomic_utils.h" 31 #include "src/core/util/debug_location.h" 32 #include "src/core/util/down_cast.h" 33 #include "src/core/util/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 LOG(INFO) << trace_ << ":" << this << " ref " << prior << " -> " 76 << 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 LOG(INFO) << trace_ << ":" << this << " " << location.file() << ":" 87 << location.line() << " ref " << 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 LOG(INFO) << trace_ << ":" << this << " ref " << prior << " -> " 104 << 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 LOG(INFO) << trace_ << ":" << this << " " << location.file() << ":" 116 << location.line() << " ref " << 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 LOG(INFO) << trace_ << ":" << this << " ref_if_non_zero " << prior 133 << " -> " << 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 LOG(INFO) << trace_ << ":" << this << " " << location.file() << ":" 143 << location.line() << " ref_if_non_zero " << prior << " -> " 144 << prior + 1 << " " << reason; 145 } 146 #endif 147 // Avoid unused-parameter warnings for debug-only parameters 148 (void)location; 149 (void)reason; 150 return IncrementIfNonzero(&value_); 151 } 152 153 // Decrements the ref-count and returns true if the ref-count reaches 0. Unref()154 bool Unref() { 155 #ifndef NDEBUG 156 // Grab a copy of the trace flag before the atomic change, since we 157 // will no longer be holding a ref afterwards and therefore can't 158 // safely access it, since another thread might free us in the interim. 159 auto* trace = trace_; 160 #endif 161 const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel); 162 #ifndef NDEBUG 163 if (trace != nullptr) { 164 LOG(INFO) << trace << ":" << this << " unref " << prior << " -> " 165 << prior - 1; 166 } 167 DCHECK_GT(prior, 0); 168 #endif 169 return prior == 1; 170 } Unref(const DebugLocation & location,const char * reason)171 bool Unref(const DebugLocation& location, const char* reason) { 172 #ifndef NDEBUG 173 // Grab a copy of the trace flag before the atomic change, since we 174 // will no longer be holding a ref afterwards and therefore can't 175 // safely access it, since another thread might free us in the interim. 176 auto* trace = trace_; 177 #endif 178 const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel); 179 #ifndef NDEBUG 180 if (trace != nullptr) { 181 LOG(INFO) << trace << ":" << this << " " << location.file() << ":" 182 << location.line() << " unref " << prior << " -> " << prior - 1 183 << " " << reason; 184 } 185 DCHECK_GT(prior, 0); 186 #else 187 // Avoid unused-parameter warnings for debug-only parameters 188 (void)location; 189 (void)reason; 190 #endif 191 return prior == 1; 192 } 193 194 private: get()195 Value get() const { return value_.load(std::memory_order_relaxed); } 196 197 #ifndef NDEBUG 198 const char* trace_; 199 #endif 200 std::atomic<Value> value_{0}; 201 }; 202 203 // PolymorphicRefCount enforces polymorphic destruction of RefCounted. 204 class PolymorphicRefCount { 205 public: 206 virtual ~PolymorphicRefCount() = default; 207 }; 208 209 // NonPolymorphicRefCount does not enforce polymorphic destruction of 210 // RefCounted. Please refer to RefCounted for more details, and 211 // when in doubt use PolymorphicRefCount. 212 class NonPolymorphicRefCount { 213 public: 214 ~NonPolymorphicRefCount() = default; 215 }; 216 217 // Behavior of RefCounted<> upon ref count reaching 0. 218 219 // Default behavior: Delete the object. 220 struct UnrefDelete { 221 template <typename T> operatorUnrefDelete222 void operator()(T* p) const { 223 delete p; 224 } 225 }; 226 227 // Do not delete the object upon unref. This is useful in cases where all 228 // existing objects must be tracked in a registry but the object's entry in 229 // the registry cannot be removed from the object's dtor due to 230 // synchronization issues. In this case, the registry can be cleaned up 231 // later by identifying entries for which RefIfNonZero() returns null. 232 struct UnrefNoDelete { 233 template <typename T> operatorUnrefNoDelete234 void operator()(T* /*p*/) const {} 235 }; 236 237 // Call the object's dtor but do not delete it. This is useful for cases 238 // where the object is stored in memory allocated elsewhere (e.g., the call 239 // arena). 240 struct UnrefCallDtor { 241 template <typename T> operatorUnrefCallDtor242 void operator()(T* p) const { 243 p->~T(); 244 } 245 }; 246 247 // Call the Destroy method on the object. This is useful when the object 248 // needs precise control of how it's deallocated. 249 struct UnrefCallDestroy { 250 template <typename T> operatorUnrefCallDestroy251 void operator()(T* p) const { 252 p->Destroy(); 253 } 254 }; 255 256 // A base class for reference-counted objects. 257 // New objects should be created via new and start with a refcount of 1. 258 // When the refcount reaches 0, executes the specified UnrefBehavior. 259 // 260 // This will commonly be used by CRTP (curiously-recurring template pattern) 261 // e.g., class MyClass : public RefCounted<MyClass> 262 // 263 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between 264 // different implementations of RefCounted. 265 // 266 // Note that NonPolymorphicRefCount does not support polymorphic destruction. 267 // So, use NonPolymorphicRefCount only when both of the following conditions 268 // are guaranteed to hold: 269 // (a) Child is a concrete leaf class in RefCounted<Child>, and 270 // (b) you are guaranteed to call Unref only on concrete leaf classes and not 271 // their parents. 272 // 273 // The following example is illegal, because calling Unref() will not call 274 // the dtor of Child. 275 // 276 // class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {} 277 // class Child : public Parent {} 278 // 279 // Child* ch; 280 // ch->Unref(); 281 // 282 template <typename Child, typename Impl = PolymorphicRefCount, 283 typename UnrefBehavior = UnrefDelete> 284 class RefCounted : public Impl { 285 public: 286 using RefCountedChildType = Child; 287 using RefCountedUnrefBehaviorType = UnrefBehavior; 288 289 // Not copyable nor movable. 290 RefCounted(const RefCounted&) = delete; 291 RefCounted& operator=(const RefCounted&) = delete; 292 293 // Note: Depending on the Impl used, this dtor can be implicitly virtual. 294 ~RefCounted() = default; 295 296 // Ref() for mutable types. Ref()297 GRPC_MUST_USE_RESULT RefCountedPtr<Child> Ref() { 298 IncrementRefCount(); 299 return RefCountedPtr<Child>(static_cast<Child*>(this)); 300 } Ref(const DebugLocation & location,const char * reason)301 GRPC_MUST_USE_RESULT RefCountedPtr<Child> Ref(const DebugLocation& location, 302 const char* reason) { 303 IncrementRefCount(location, reason); 304 return RefCountedPtr<Child>(static_cast<Child*>(this)); 305 } 306 307 // Ref() for const types. Ref()308 GRPC_MUST_USE_RESULT RefCountedPtr<const Child> Ref() const { 309 IncrementRefCount(); 310 return RefCountedPtr<const Child>(static_cast<const Child*>(this)); 311 } Ref(const DebugLocation & location,const char * reason)312 GRPC_MUST_USE_RESULT RefCountedPtr<const Child> Ref( 313 const DebugLocation& location, const char* reason) const { 314 IncrementRefCount(location, reason); 315 return RefCountedPtr<const Child>(static_cast<const Child*>(this)); 316 } 317 318 template < 319 typename Subclass, 320 std::enable_if_t<std::is_base_of<Child, Subclass>::value, bool> = true> RefAsSubclass()321 RefCountedPtr<Subclass> RefAsSubclass() { 322 IncrementRefCount(); 323 return RefCountedPtr<Subclass>( 324 DownCast<Subclass*>(static_cast<Child*>(this))); 325 } 326 template < 327 typename Subclass, 328 std::enable_if_t<std::is_base_of<Child, Subclass>::value, bool> = true> RefAsSubclass(const DebugLocation & location,const char * reason)329 RefCountedPtr<Subclass> RefAsSubclass(const DebugLocation& location, 330 const char* reason) { 331 IncrementRefCount(location, reason); 332 return RefCountedPtr<Subclass>( 333 DownCast<Subclass*>(static_cast<Child*>(this))); 334 } 335 336 // RefIfNonZero() for mutable types. RefIfNonZero()337 GRPC_MUST_USE_RESULT RefCountedPtr<Child> RefIfNonZero() { 338 return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this) 339 : nullptr); 340 } RefIfNonZero(const DebugLocation & location,const char * reason)341 GRPC_MUST_USE_RESULT RefCountedPtr<Child> RefIfNonZero( 342 const DebugLocation& location, const char* reason) { 343 return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason) 344 ? static_cast<Child*>(this) 345 : nullptr); 346 } 347 348 // RefIfNonZero() for const types. RefIfNonZero()349 GRPC_MUST_USE_RESULT RefCountedPtr<const Child> RefIfNonZero() const { 350 return RefCountedPtr<const Child>( 351 refs_.RefIfNonZero() ? static_cast<const Child*>(this) : nullptr); 352 } RefIfNonZero(const DebugLocation & location,const char * reason)353 GRPC_MUST_USE_RESULT RefCountedPtr<const Child> RefIfNonZero( 354 const DebugLocation& location, const char* reason) const { 355 return RefCountedPtr<const Child>(refs_.RefIfNonZero(location, reason) 356 ? static_cast<const Child*>(this) 357 : nullptr); 358 } 359 360 // TODO(roth): Once all of our code is converted to C++ and can use 361 // RefCountedPtr<> instead of manual ref-counting, make this method 362 // private, since it will only be used by RefCountedPtr<>, which is a 363 // friend of this class. Unref()364 void Unref() const { 365 if (GPR_UNLIKELY(refs_.Unref())) { 366 unref_behavior_(static_cast<const Child*>(this)); 367 } 368 } Unref(const DebugLocation & location,const char * reason)369 void Unref(const DebugLocation& location, const char* reason) const { 370 if (GPR_UNLIKELY(refs_.Unref(location, reason))) { 371 unref_behavior_(static_cast<const Child*>(this)); 372 } 373 } 374 375 protected: 376 // Note: Tracing is a no-op on non-debug builds. 377 explicit RefCounted(const char* trace = nullptr, 378 intptr_t initial_refcount = 1) refs_(initial_refcount,trace)379 : refs_(initial_refcount, trace) {} 380 381 // Note: Tracing is a no-op on non-debug builds. 382 explicit RefCounted(UnrefBehavior b, const char* trace = nullptr, 383 intptr_t initial_refcount = 1) refs_(initial_refcount,trace)384 : refs_(initial_refcount, trace), unref_behavior_(b) {} 385 386 private: 387 // Allow RefCountedPtr<> to access IncrementRefCount(). 388 template <typename T> 389 friend class RefCountedPtr; 390 IncrementRefCount()391 void IncrementRefCount() const { refs_.Ref(); } IncrementRefCount(const DebugLocation & location,const char * reason)392 void IncrementRefCount(const DebugLocation& location, 393 const char* reason) const { 394 refs_.Ref(location, reason); 395 } 396 397 mutable RefCount refs_; 398 GPR_NO_UNIQUE_ADDRESS UnrefBehavior unref_behavior_; 399 }; 400 401 } // namespace grpc_core 402 403 #endif // GRPC_SRC_CORE_UTIL_REF_COUNTED_H 404