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/log.h> 25 #include <grpc/support/sync.h> 26 27 #include <cinttypes> 28 29 #include "src/core/lib/debug/trace.h" 30 #include "src/core/lib/gprpp/abstract.h" 31 #include "src/core/lib/gprpp/debug_location.h" 32 #include "src/core/lib/gprpp/memory.h" 33 #include "src/core/lib/gprpp/ref_counted_ptr.h" 34 35 namespace grpc_core { 36 37 // A base class for reference-counted objects. 38 // New objects should be created via New() and start with a refcount of 1. 39 // When the refcount reaches 0, the object will be deleted via Delete(). 40 // 41 // This will commonly be used by CRTP (curiously-recurring template pattern) 42 // e.g., class MyClass : public RefCounted<MyClass> 43 template <typename Child> 44 class RefCounted { 45 public: Ref()46 RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT { 47 IncrementRefCount(); 48 return RefCountedPtr<Child>(static_cast<Child*>(this)); 49 } 50 51 // TODO(roth): Once all of our code is converted to C++ and can use 52 // RefCountedPtr<> instead of manual ref-counting, make this method 53 // private, since it will only be used by RefCountedPtr<>, which is a 54 // friend of this class. Unref()55 void Unref() { 56 if (gpr_unref(&refs_)) { 57 Delete(static_cast<Child*>(this)); 58 } 59 } 60 61 // Not copyable nor movable. 62 RefCounted(const RefCounted&) = delete; 63 RefCounted& operator=(const RefCounted&) = delete; 64 65 GRPC_ABSTRACT_BASE_CLASS 66 67 protected: 68 GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE 69 RefCounted()70 RefCounted() { gpr_ref_init(&refs_, 1); } 71 ~RefCounted()72 virtual ~RefCounted() {} 73 74 private: 75 // Allow RefCountedPtr<> to access IncrementRefCount(). 76 template <typename T> 77 friend class RefCountedPtr; 78 IncrementRefCount()79 void IncrementRefCount() { gpr_ref(&refs_); } 80 81 gpr_refcount refs_; 82 }; 83 84 // An alternative version of the RefCounted base class that 85 // supports tracing. This is intended to be used in cases where the 86 // object will be handled both by idiomatic C++ code using smart 87 // pointers and legacy code that is manually calling Ref() and Unref(). 88 // Once all of our code is converted to idiomatic C++, we may be able to 89 // eliminate this class. 90 template <typename Child> 91 class RefCountedWithTracing { 92 public: Ref()93 RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT { 94 IncrementRefCount(); 95 return RefCountedPtr<Child>(static_cast<Child*>(this)); 96 } 97 Ref(const DebugLocation & location,const char * reason)98 RefCountedPtr<Child> Ref(const DebugLocation& location, 99 const char* reason) GRPC_MUST_USE_RESULT { 100 if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) { 101 gpr_atm old_refs = gpr_atm_no_barrier_load(&refs_.count); 102 gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s", 103 trace_flag_->name(), this, location.file(), location.line(), 104 old_refs, old_refs + 1, reason); 105 } 106 return Ref(); 107 } 108 109 // TODO(roth): Once all of our code is converted to C++ and can use 110 // RefCountedPtr<> instead of manual ref-counting, make the Unref() methods 111 // private, since they will only be used by RefCountedPtr<>, which is a 112 // friend of this class. 113 Unref()114 void Unref() { 115 if (gpr_unref(&refs_)) { 116 Delete(static_cast<Child*>(this)); 117 } 118 } 119 Unref(const DebugLocation & location,const char * reason)120 void Unref(const DebugLocation& location, const char* reason) { 121 if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) { 122 gpr_atm old_refs = gpr_atm_no_barrier_load(&refs_.count); 123 gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s", 124 trace_flag_->name(), this, location.file(), location.line(), 125 old_refs, old_refs - 1, reason); 126 } 127 Unref(); 128 } 129 130 // Not copyable nor movable. 131 RefCountedWithTracing(const RefCountedWithTracing&) = delete; 132 RefCountedWithTracing& operator=(const RefCountedWithTracing&) = delete; 133 134 GRPC_ABSTRACT_BASE_CLASS 135 136 protected: 137 GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE 138 RefCountedWithTracing()139 RefCountedWithTracing() 140 : RefCountedWithTracing(static_cast<TraceFlag*>(nullptr)) {} 141 RefCountedWithTracing(TraceFlag * trace_flag)142 explicit RefCountedWithTracing(TraceFlag* trace_flag) 143 : trace_flag_(trace_flag) { 144 gpr_ref_init(&refs_, 1); 145 } 146 147 #ifdef NDEBUG RefCountedWithTracing(DebugOnlyTraceFlag * trace_flag)148 explicit RefCountedWithTracing(DebugOnlyTraceFlag* trace_flag) 149 : RefCountedWithTracing() {} 150 #endif 151 ~RefCountedWithTracing()152 virtual ~RefCountedWithTracing() {} 153 154 private: 155 // Allow RefCountedPtr<> to access IncrementRefCount(). 156 template <typename T> 157 friend class RefCountedPtr; 158 IncrementRefCount()159 void IncrementRefCount() { gpr_ref(&refs_); } 160 161 TraceFlag* trace_flag_ = nullptr; 162 gpr_refcount refs_; 163 }; 164 165 } // namespace grpc_core 166 167 #endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */ 168