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 namespace internal { 219 template <typename T, bool DoDelete> 220 class Delete; 221 template <typename T> 222 class Delete<T, true> { 223 public: Delete(T * t)224 explicit Delete(T* t) { delete t; } 225 }; 226 template <typename T> 227 class Delete<T, false> { 228 public: Delete(T *)229 explicit Delete(T* /*t*/) {} 230 }; 231 } // namespace internal 232 233 // A base class for reference-counted objects. 234 // New objects should be created via new and start with a refcount of 1. 235 // When the refcount reaches 0, the object will be deleted via delete. 236 // 237 // If DeleteUponUnref is false, deletion will not occur when the ref 238 // count reaches 0. This is useful in cases where all existing objects 239 // must be tracked in a registry but the object's entry in the registry 240 // cannot be removed from the object's dtor due to synchronization issues. 241 // In this case, the registry can be cleaned up later by identifying 242 // entries for which RefIfNonZero() returns null. 243 // 244 // This will commonly be used by CRTP (curiously-recurring template pattern) 245 // e.g., class MyClass : public RefCounted<MyClass> 246 // 247 // Use PolymorphicRefCount and NonPolymorphicRefCount to select between 248 // different implementations of RefCounted. 249 // 250 // Note that NonPolymorphicRefCount does not support polymorphic destruction. 251 // So, use NonPolymorphicRefCount only when both of the following conditions 252 // are guaranteed to hold: 253 // (a) Child is a concrete leaf class in RefCounted<Child>, and 254 // (b) you are guaranteed to call Unref only on concrete leaf classes and not 255 // their parents. 256 // 257 // The following example is illegal, because calling Unref() will not call 258 // the dtor of Child. 259 // 260 // class Parent : public RefCounted<Parent, NonPolymorphicRefCount> {} 261 // class Child : public Parent {} 262 // 263 // Child* ch; 264 // ch->Unref(); 265 // 266 template <typename Child, typename Impl = PolymorphicRefCount, 267 bool DeleteUponUnref = true> 268 class RefCounted : public Impl { 269 public: 270 // Note: Depending on the Impl used, this dtor can be implicitly virtual. 271 ~RefCounted() = default; 272 Ref()273 RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT { 274 IncrementRefCount(); 275 return RefCountedPtr<Child>(static_cast<Child*>(this)); 276 } 277 Ref(const DebugLocation & location,const char * reason)278 RefCountedPtr<Child> Ref(const DebugLocation& location, 279 const char* reason) GRPC_MUST_USE_RESULT { 280 IncrementRefCount(location, reason); 281 return RefCountedPtr<Child>(static_cast<Child*>(this)); 282 } 283 284 // TODO(roth): Once all of our code is converted to C++ and can use 285 // RefCountedPtr<> instead of manual ref-counting, make this method 286 // private, since it will only be used by RefCountedPtr<>, which is a 287 // friend of this class. Unref()288 void Unref() { 289 if (GPR_UNLIKELY(refs_.Unref())) { 290 internal::Delete<Child, DeleteUponUnref>(static_cast<Child*>(this)); 291 } 292 } Unref(const DebugLocation & location,const char * reason)293 void Unref(const DebugLocation& location, const char* reason) { 294 if (GPR_UNLIKELY(refs_.Unref(location, reason))) { 295 internal::Delete<Child, DeleteUponUnref>(static_cast<Child*>(this)); 296 } 297 } 298 RefIfNonZero()299 RefCountedPtr<Child> RefIfNonZero() GRPC_MUST_USE_RESULT { 300 return RefCountedPtr<Child>(refs_.RefIfNonZero() ? static_cast<Child*>(this) 301 : nullptr); 302 } RefIfNonZero(const DebugLocation & location,const char * reason)303 RefCountedPtr<Child> RefIfNonZero(const DebugLocation& location, 304 const char* reason) GRPC_MUST_USE_RESULT { 305 return RefCountedPtr<Child>(refs_.RefIfNonZero(location, reason) 306 ? static_cast<Child*>(this) 307 : nullptr); 308 } 309 310 // Not copyable nor movable. 311 RefCounted(const RefCounted&) = delete; 312 RefCounted& operator=(const RefCounted&) = delete; 313 314 protected: 315 // Note: Tracing is a no-op on non-debug builds. 316 explicit RefCounted(const char* trace = nullptr, 317 intptr_t initial_refcount = 1) refs_(initial_refcount,trace)318 : refs_(initial_refcount, trace) {} 319 320 private: 321 // Allow RefCountedPtr<> to access IncrementRefCount(). 322 template <typename T> 323 friend class RefCountedPtr; 324 IncrementRefCount()325 void IncrementRefCount() { refs_.Ref(); } IncrementRefCount(const DebugLocation & location,const char * reason)326 void IncrementRefCount(const DebugLocation& location, const char* reason) { 327 refs_.Ref(location, reason); 328 } 329 330 RefCount refs_; 331 }; 332 333 } // namespace grpc_core 334 335 #endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */ 336