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/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" 36 37 namespace grpc_core { 38 39 // RefCount is a simple atomic ref-count. 40 // 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. 44 // 45 // TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph 46 // above. 47 class RefCount { 48 public: 49 using Value = intptr_t; 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 = 1, 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_.FetchAdd(n, MemoryOrder::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_.FetchAdd(n, MemoryOrder::RELAXED); 80 #endif 81 } 82 void Ref(const DebugLocation& location, const char* reason, Value n = 1) { 83 #ifndef NDEBUG 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, 88 reason); 89 } 90 #else 91 // Use conditionally-important parameters 92 (void)location; 93 (void)reason; 94 value_.FetchAdd(n, MemoryOrder::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_.FetchAdd(1, MemoryOrder::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_.FetchAdd(1, MemoryOrder::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_.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, 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 value_.IncrementIfNonzero(); 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 value_.IncrementIfNonzero(); 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_.FetchSub(1, MemoryOrder::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_.FetchSub(1, MemoryOrder::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(MemoryOrder::RELAXED); } 197 198 #ifndef NDEBUG 199 const char* trace_; 200 #endif 201 Atomic<Value> value_; 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 grpc_core::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 enum UnrefBehavior { 220 // Default behavior: Delete the object. 221 kUnrefDelete, 222 // Do not delete the object upon unref. This is useful in cases where all 223 // existing objects must be tracked in a registry but the object's entry in 224 // the registry cannot be removed from the object's dtor due to 225 // synchronization issues. In this case, the registry can be cleaned up 226 // later by identifying entries for which RefIfNonZero() returns null. 227 kUnrefNoDelete, 228 // Call the object's dtor but do not delete it. This is useful for cases 229 // where the object is stored in memory allocated elsewhere (e.g., the call 230 // arena). 231 kUnrefCallDtor, 232 }; 233 234 namespace internal { 235 template <typename T, UnrefBehavior UnrefBehaviorArg> 236 class Delete; 237 template <typename T> 238 class Delete<T, kUnrefDelete> { 239 public: Delete(T * t)240 explicit Delete(T* t) { delete t; } 241 }; 242 template <typename T> 243 class Delete<T, kUnrefNoDelete> { 244 public: Delete(T *)245 explicit Delete(T* /*t*/) {} 246 }; 247 template <typename T> 248 class Delete<T, kUnrefCallDtor> { 249 public: Delete(T * t)250 explicit Delete(T* t) { t->~T(); } 251 }; 252 } // namespace internal 253 254 // A base class for reference-counted objects. 255 // New objects should be created via new and start with a refcount of 1. 256 // When the refcount reaches 0, executes the specified UnrefBehavior. 257 // 258 // This will commonly be used by CRTP (curiously-recurring template pattern) 259 // e.g., class MyClass : public RefCounted<MyClass> 260 // 261 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between 262 // different implementations of RefCounted. 263 // 264 // Note that NonPolymorphicRefCount does not support polymorphic destruction. 265 // So, use NonPolymorphicRefCount only when both of the following conditions 266 // are guaranteed to hold: 267 // (a) Child is a concrete leaf class in RefCounted<Child>, and 268 // (b) you are guaranteed to call Unref only on concrete leaf classes and not 269 // their parents. 270 // 271 // The following example is illegal, because calling Unref() will not call 272 // the dtor of Child. 273 // 274 // class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {} 275 // class Child : public Parent {} 276 // 277 // Child* ch; 278 // ch->Unref(); 279 // 280 template <typename Child, typename Impl = PolymorphicRefCount, 281 UnrefBehavior UnrefBehaviorArg = kUnrefDelete> 282 class RefCounted : public Impl { 283 public: 284 // Note: Depending on the Impl used, this dtor can be implicitly virtual. 285 ~RefCounted() = default; 286 Ref()287 RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT { 288 IncrementRefCount(); 289 return RefCountedPtr<Child>(static_cast<Child*>(this)); 290 } 291 Ref(const DebugLocation & location,const char * reason)292 RefCountedPtr<Child> Ref(const DebugLocation& location, 293 const char* reason) GRPC_MUST_USE_RESULT { 294 IncrementRefCount(location, reason); 295 return RefCountedPtr<Child>(static_cast<Child*>(this)); 296 } 297 298 // TODO(roth): Once all of our code is converted to C++ and can use 299 // RefCountedPtr<> instead of manual ref-counting, make this method 300 // private, since it will only be used by RefCountedPtr<>, which is a 301 // friend of this class. Unref()302 void Unref() { 303 if (GPR_UNLIKELY(refs_.Unref())) { 304 internal::Delete<Child, UnrefBehaviorArg>(static_cast<Child*>(this)); 305 } 306 } Unref(const DebugLocation & location,const char * reason)307 void Unref(const DebugLocation& location, const char* reason) { 308 if (GPR_UNLIKELY(refs_.Unref(location, reason))) { 309 internal::Delete<Child, UnrefBehaviorArg>(static_cast<Child*>(this)); 310 } 311 } 312 RefIfNonZero()313 RefCountedPtr<Child> RefIfNonZero() GRPC_MUST_USE_RESULT { 314 return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this) 315 : nullptr); 316 } RefIfNonZero(const DebugLocation & location,const char * reason)317 RefCountedPtr<Child> RefIfNonZero(const DebugLocation& location, 318 const char* reason) GRPC_MUST_USE_RESULT { 319 return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason) 320 ? static_cast<Child*>(this) 321 : nullptr); 322 } 323 324 // Not copyable nor movable. 325 RefCounted(const RefCounted&) = delete; 326 RefCounted& operator=(const RefCounted&) = delete; 327 328 protected: 329 // Note: Tracing is a no-op on non-debug builds. 330 explicit RefCounted(const char* trace = nullptr, 331 intptr_t initial_refcount = 1) refs_(initial_refcount,trace)332 : refs_(initial_refcount, trace) {} 333 334 private: 335 // Allow RefCountedPtr<> to access IncrementRefCount(). 336 template <typename T> 337 friend class RefCountedPtr; 338 IncrementRefCount()339 void IncrementRefCount() { refs_.Ref(); } IncrementRefCount(const DebugLocation & location,const char * reason)340 void IncrementRefCount(const DebugLocation& location, const char* reason) { 341 refs_.Ref(location, reason); 342 } 343 344 RefCount refs_; 345 }; 346 347 } // namespace grpc_core 348 349 #endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */ 350