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/ref_counted_ptr.h" 33 34 namespace grpc_core { 35 36 // RefCount is a simple atomic ref-count. 37 // 38 // This is a C++ implementation of gpr_refcount, with inline functions. Due to 39 // inline functions, this class is significantly more efficient than 40 // gpr_refcount and should be preferred over gpr_refcount whenever possible. 41 // 42 // TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph 43 // above. 44 class RefCount { 45 public: 46 using Value = intptr_t; 47 48 // `init` is the initial refcount stored in this object. 49 // 50 // `trace` is a string to be logged with trace events; if null, no 51 // trace logging will be done. Tracing is a no-op in non-debug builds. 52 explicit RefCount( 53 Value init = 1, 54 const char* 55 #ifndef NDEBUG 56 // Leave unnamed if NDEBUG to avoid unused parameter warning 57 trace 58 #endif 59 = nullptr) 60 : 61 #ifndef NDEBUG trace_(trace)62 trace_(trace), 63 #endif 64 value_(init) { 65 } 66 67 // Increases the ref-count by `n`. 68 void Ref(Value n = 1) { 69 #ifndef NDEBUG 70 const Value prior = value_.fetch_add(n, std::memory_order_relaxed); 71 if (trace_ != nullptr) { 72 gpr_log(GPR_INFO, "%s:%p ref %" PRIdPTR " -> %" PRIdPTR, trace_, this, 73 prior, prior + n); 74 } 75 #else 76 value_.fetch_add(n, std::memory_order_relaxed); 77 #endif 78 } 79 void Ref(const DebugLocation& location, const char* reason, Value n = 1) { 80 #ifndef NDEBUG 81 const Value prior = value_.fetch_add(n, std::memory_order_relaxed); 82 if (trace_ != nullptr) { 83 gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s", 84 trace_, this, location.file(), location.line(), prior, prior + n, 85 reason); 86 } 87 #else 88 // Use conditionally-important parameters 89 (void)location; 90 (void)reason; 91 value_.fetch_add(n, std::memory_order_relaxed); 92 #endif 93 } 94 95 // Similar to Ref() with an assert on the ref-count being non-zero. RefNonZero()96 void RefNonZero() { 97 #ifndef NDEBUG 98 const Value prior = value_.fetch_add(1, std::memory_order_relaxed); 99 if (trace_ != nullptr) { 100 gpr_log(GPR_INFO, "%s:%p ref %" PRIdPTR " -> %" PRIdPTR, trace_, this, 101 prior, prior + 1); 102 } 103 assert(prior > 0); 104 #else 105 value_.fetch_add(1, std::memory_order_relaxed); 106 #endif 107 } RefNonZero(const DebugLocation & location,const char * reason)108 void RefNonZero(const DebugLocation& location, const char* reason) { 109 #ifndef NDEBUG 110 const Value prior = value_.fetch_add(1, std::memory_order_relaxed); 111 if (trace_ != nullptr) { 112 gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s", 113 trace_, this, location.file(), location.line(), prior, prior + 1, 114 reason); 115 } 116 assert(prior > 0); 117 #else 118 // Avoid unused-parameter warnings for debug-only parameters 119 (void)location; 120 (void)reason; 121 RefNonZero(); 122 #endif 123 } 124 RefIfNonZero()125 bool RefIfNonZero() { 126 #ifndef NDEBUG 127 if (trace_ != nullptr) { 128 const Value prior = get(); 129 gpr_log(GPR_INFO, "%s:%p ref_if_non_zero %" PRIdPTR " -> %" PRIdPTR, 130 trace_, this, prior, prior + 1); 131 } 132 #endif 133 return IncrementIfNonzero(&value_); 134 } RefIfNonZero(const DebugLocation & location,const char * reason)135 bool RefIfNonZero(const DebugLocation& location, const char* reason) { 136 #ifndef NDEBUG 137 if (trace_ != nullptr) { 138 const Value prior = get(); 139 gpr_log(GPR_INFO, 140 "%s:%p %s:%d ref_if_non_zero %" PRIdPTR " -> %" PRIdPTR " %s", 141 trace_, this, location.file(), location.line(), prior, prior + 1, 142 reason); 143 } 144 #endif 145 // Avoid unused-parameter warnings for debug-only parameters 146 (void)location; 147 (void)reason; 148 return IncrementIfNonzero(&value_); 149 } 150 151 // Decrements the ref-count and returns true if the ref-count reaches 0. Unref()152 bool Unref() { 153 #ifndef NDEBUG 154 // Grab a copy of the trace flag before the atomic change, since we 155 // will no longer be holding a ref afterwards and therefore can't 156 // safely access it, since another thread might free us in the interim. 157 auto* trace = trace_; 158 #endif 159 const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel); 160 #ifndef NDEBUG 161 if (trace != nullptr) { 162 gpr_log(GPR_INFO, "%s:%p unref %" PRIdPTR " -> %" PRIdPTR, trace, this, 163 prior, prior - 1); 164 } 165 GPR_DEBUG_ASSERT(prior > 0); 166 #endif 167 return prior == 1; 168 } Unref(const DebugLocation & location,const char * reason)169 bool Unref(const DebugLocation& location, const char* reason) { 170 #ifndef NDEBUG 171 // Grab a copy of the trace flag before the atomic change, since we 172 // will no longer be holding a ref afterwards and therefore can't 173 // safely access it, since another thread might free us in the interim. 174 auto* trace = trace_; 175 #endif 176 const Value prior = value_.fetch_sub(1, std::memory_order_acq_rel); 177 #ifndef NDEBUG 178 if (trace != nullptr) { 179 gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s", 180 trace, this, location.file(), location.line(), prior, prior - 1, 181 reason); 182 } 183 GPR_DEBUG_ASSERT(prior > 0); 184 #else 185 // Avoid unused-parameter warnings for debug-only parameters 186 (void)location; 187 (void)reason; 188 #endif 189 return prior == 1; 190 } 191 192 private: get()193 Value get() const { return value_.load(std::memory_order_relaxed); } 194 195 #ifndef NDEBUG 196 const char* trace_; 197 #endif 198 std::atomic<Value> value_{0}; 199 }; 200 201 // PolymorphicRefCount enforces polymorphic destruction of RefCounted. 202 class PolymorphicRefCount { 203 public: 204 virtual ~PolymorphicRefCount() = default; 205 }; 206 207 // NonPolymorphicRefCount does not enforce polymorphic destruction of 208 // RefCounted. Please refer to RefCounted for more details, and 209 // when in doubt use PolymorphicRefCount. 210 class NonPolymorphicRefCount { 211 public: 212 ~NonPolymorphicRefCount() = default; 213 }; 214 215 // Behavior of RefCounted<> upon ref count reaching 0. 216 217 // Default behavior: Delete the object. 218 struct UnrefDelete { 219 template <typename T> operatorUnrefDelete220 void operator()(T* p) { 221 delete p; 222 } 223 }; 224 225 // Do not delete the object upon unref. This is useful in cases where all 226 // existing objects must be tracked in a registry but the object's entry in 227 // the registry cannot be removed from the object's dtor due to 228 // synchronization issues. In this case, the registry can be cleaned up 229 // later by identifying entries for which RefIfNonZero() returns null. 230 struct UnrefNoDelete { 231 template <typename T> operatorUnrefNoDelete232 void operator()(T* /*p*/) {} 233 }; 234 235 // Call the object's dtor but do not delete it. This is useful for cases 236 // where the object is stored in memory allocated elsewhere (e.g., the call 237 // arena). 238 struct UnrefCallDtor { 239 template <typename T> operatorUnrefCallDtor240 void operator()(T* p) { 241 p->~T(); 242 } 243 }; 244 245 // A base class for reference-counted objects. 246 // New objects should be created via new and start with a refcount of 1. 247 // When the refcount reaches 0, executes the specified UnrefBehavior. 248 // 249 // This will commonly be used by CRTP (curiously-recurring template pattern) 250 // e.g., class MyClass : public RefCounted<MyClass> 251 // 252 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between 253 // different implementations of RefCounted. 254 // 255 // Note that NonPolymorphicRefCount does not support polymorphic destruction. 256 // So, use NonPolymorphicRefCount only when both of the following conditions 257 // are guaranteed to hold: 258 // (a) Child is a concrete leaf class in RefCounted<Child>, and 259 // (b) you are guaranteed to call Unref only on concrete leaf classes and not 260 // their parents. 261 // 262 // The following example is illegal, because calling Unref() will not call 263 // the dtor of Child. 264 // 265 // class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {} 266 // class Child : public Parent {} 267 // 268 // Child* ch; 269 // ch->Unref(); 270 // 271 template <typename Child, typename Impl = PolymorphicRefCount, 272 typename UnrefBehavior = UnrefDelete> 273 class RefCounted : public Impl { 274 public: 275 using RefCountedChildType = Child; 276 277 // Note: Depending on the Impl used, this dtor can be implicitly virtual. 278 ~RefCounted() = default; 279 Ref()280 RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT { 281 IncrementRefCount(); 282 return RefCountedPtr<Child>(static_cast<Child*>(this)); 283 } 284 Ref(const DebugLocation & location,const char * reason)285 RefCountedPtr<Child> Ref(const DebugLocation& location, 286 const char* reason) GRPC_MUST_USE_RESULT { 287 IncrementRefCount(location, reason); 288 return RefCountedPtr<Child>(static_cast<Child*>(this)); 289 } 290 291 // TODO(roth): Once all of our code is converted to C++ and can use 292 // RefCountedPtr<> instead of manual ref-counting, make this method 293 // private, since it will only be used by RefCountedPtr<>, which is a 294 // friend of this class. Unref()295 void Unref() { 296 if (GPR_UNLIKELY(refs_.Unref())) { 297 unref_behavior_(static_cast<Child*>(this)); 298 } 299 } Unref(const DebugLocation & location,const char * reason)300 void Unref(const DebugLocation& location, const char* reason) { 301 if (GPR_UNLIKELY(refs_.Unref(location, reason))) { 302 unref_behavior_(static_cast<Child*>(this)); 303 } 304 } 305 RefIfNonZero()306 RefCountedPtr<Child> RefIfNonZero() GRPC_MUST_USE_RESULT { 307 return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this) 308 : nullptr); 309 } RefIfNonZero(const DebugLocation & location,const char * reason)310 RefCountedPtr<Child> RefIfNonZero(const DebugLocation& location, 311 const char* reason) GRPC_MUST_USE_RESULT { 312 return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason) 313 ? static_cast<Child*>(this) 314 : nullptr); 315 } 316 317 // Not copyable nor movable. 318 RefCounted(const RefCounted&) = delete; 319 RefCounted& operator=(const RefCounted&) = delete; 320 321 protected: 322 // Note: Tracing is a no-op on non-debug builds. 323 explicit RefCounted(const char* trace = nullptr, 324 intptr_t initial_refcount = 1) refs_(initial_refcount,trace)325 : refs_(initial_refcount, trace) {} 326 327 // Note: Tracing is a no-op on non-debug builds. 328 explicit RefCounted(UnrefBehavior b, const char* trace = nullptr, 329 intptr_t initial_refcount = 1) refs_(initial_refcount,trace)330 : refs_(initial_refcount, trace), unref_behavior_(b) {} 331 332 private: 333 // Allow RefCountedPtr<> to access IncrementRefCount(). 334 template <typename T> 335 friend class RefCountedPtr; 336 IncrementRefCount()337 void IncrementRefCount() { refs_.Ref(); } IncrementRefCount(const DebugLocation & location,const char * reason)338 void IncrementRefCount(const DebugLocation& location, const char* reason) { 339 refs_.Ref(location, reason); 340 } 341 342 RefCount refs_; 343 GPR_NO_UNIQUE_ADDRESS UnrefBehavior unref_behavior_; 344 }; 345 346 } // namespace grpc_core 347 348 #endif // GRPC_SRC_CORE_LIB_GPRPP_REF_COUNTED_H 349