• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_PTR_H
20 #define GRPC_SRC_CORE_UTIL_REF_COUNTED_PTR_H
21 
22 #include <grpc/support/port_platform.h>
23 
24 #include <cstddef>
25 #include <iosfwd>
26 #include <type_traits>
27 #include <utility>
28 
29 #include "absl/hash/hash.h"
30 #include "src/core/util/debug_location.h"
31 #include "src/core/util/down_cast.h"
32 
33 namespace grpc_core {
34 
35 // A smart pointer class for objects that provide IncrementRefCount() and
36 // Unref() methods, such as those provided by the RefCounted base class.
37 template <typename T>
38 class RefCountedPtr {
39  public:
RefCountedPtr()40   RefCountedPtr() {}
41   // NOLINTNEXTLINE(google-explicit-constructor)
RefCountedPtr(std::nullptr_t)42   RefCountedPtr(std::nullptr_t) {}
43 
44   // If value is non-null, we take ownership of a ref to it.
45   template <typename Y,
46             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
RefCountedPtr(Y * value)47   explicit RefCountedPtr(Y* value) : value_(value) {}
48 
49   // Move ctors.
RefCountedPtr(RefCountedPtr && other)50   RefCountedPtr(RefCountedPtr&& other) noexcept {
51     value_ = other.value_;
52     other.value_ = nullptr;
53   }
54   template <typename Y,
55             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
56   // NOLINTNEXTLINE(google-explicit-constructor)
RefCountedPtr(RefCountedPtr<Y> && other)57   RefCountedPtr(RefCountedPtr<Y>&& other) noexcept {
58     value_ = static_cast<T*>(other.value_);
59     other.value_ = nullptr;
60   }
61 
62   // Move assignment.
63   RefCountedPtr& operator=(RefCountedPtr&& other) noexcept {
64     reset(std::exchange(other.value_, nullptr));
65     return *this;
66   }
67   template <typename Y,
68             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
69   RefCountedPtr& operator=(RefCountedPtr<Y>&& other) noexcept {
70     reset(std::exchange(other.value_, nullptr));
71     return *this;
72   }
73 
74   // Copy ctors.
RefCountedPtr(const RefCountedPtr & other)75   RefCountedPtr(const RefCountedPtr& other) {
76     if (other.value_ != nullptr) other.value_->IncrementRefCount();
77     value_ = other.value_;
78   }
79   template <typename Y,
80             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
81   // NOLINTNEXTLINE(google-explicit-constructor)
RefCountedPtr(const RefCountedPtr<Y> & other)82   RefCountedPtr(const RefCountedPtr<Y>& other) {
83     static_assert(std::has_virtual_destructor<T>::value,
84                   "T does not have a virtual dtor");
85     if (other.value_ != nullptr) other.value_->IncrementRefCount();
86     value_ = static_cast<T*>(other.value_);
87   }
88 
89   // Copy assignment.
90   // NOLINTNEXTLINE(bugprone-unhandled-self-assignment)
91   RefCountedPtr& operator=(const RefCountedPtr& other) {
92     // Note: Order of reffing and unreffing is important here in case value_
93     // and other.value_ are the same object.
94     if (other.value_ != nullptr) other.value_->IncrementRefCount();
95     reset(other.value_);
96     return *this;
97   }
98   template <typename Y,
99             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
100   RefCountedPtr& operator=(const RefCountedPtr<Y>& other) {
101     static_assert(std::has_virtual_destructor<T>::value,
102                   "T does not have a virtual dtor");
103     // Note: Order of reffing and unreffing is important here in case value_
104     // and other.value_ are the same object.
105     if (other.value_ != nullptr) other.value_->IncrementRefCount();
106     reset(other.value_);
107     return *this;
108   }
109 
~RefCountedPtr()110   ~RefCountedPtr() {
111     if (value_ != nullptr) value_->Unref();
112   }
113 
114   // An explicit copy method that supports ref-count tracing.
Ref(const DebugLocation & location,const char * reason)115   RefCountedPtr<T> Ref(const DebugLocation& location, const char* reason) {
116     if (value_ != nullptr) value_->IncrementRefCount(location, reason);
117     return RefCountedPtr<T>(value_);
118   }
119 
swap(RefCountedPtr & other)120   void swap(RefCountedPtr& other) { std::swap(value_, other.value_); }
121 
122   // If value is non-null, we take ownership of a ref to it.
123   void reset(T* value = nullptr) {
124     T* old_value = std::exchange(value_, value);
125     if (old_value != nullptr) old_value->Unref();
126   }
127   void reset(const DebugLocation& location, const char* reason,
128              T* value = nullptr) {
129     T* old_value = std::exchange(value_, value);
130     if (old_value != nullptr) old_value->Unref(location, reason);
131   }
132   template <typename Y,
133             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
134   void reset(Y* value = nullptr) {
135     static_assert(std::has_virtual_destructor<T>::value,
136                   "T does not have a virtual dtor");
137     reset(static_cast<T*>(value));
138   }
139   template <typename Y,
140             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
141   void reset(const DebugLocation& location, const char* reason,
142              Y* value = nullptr) {
143     static_assert(std::has_virtual_destructor<T>::value,
144                   "T does not have a virtual dtor");
145     reset(location, reason, static_cast<T*>(value));
146   }
147 
148   // This method is mostly useful for interoperating with C code.
149   // Eventually use within core should be banned, except at the surface API
150   // boundaries.
release()151   T* release() { return std::exchange(value_, nullptr); }
152 
get()153   T* get() const { return value_; }
154 
155   T& operator*() const { return *value_; }
156   T* operator->() const { return value_; }
157 
158   template <typename Y,
159             std::enable_if_t<std::is_base_of<T, Y>::value, bool> = true>
TakeAsSubclass()160   RefCountedPtr<Y> TakeAsSubclass() {
161     return RefCountedPtr<Y>(DownCast<Y*>(release()));
162   }
163 
164   template <typename Y,
165             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
166   bool operator==(const RefCountedPtr<Y>& other) const {
167     return value_ == other.value_;
168   }
169 
170   template <typename Y,
171             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
172   bool operator==(const Y* other) const {
173     return value_ == other;
174   }
175 
176   bool operator==(std::nullptr_t) const { return value_ == nullptr; }
177 
178   template <typename Y,
179             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
180   bool operator!=(const RefCountedPtr<Y>& other) const {
181     return value_ != other.value_;
182   }
183 
184   template <typename Y,
185             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
186   bool operator!=(const Y* other) const {
187     return value_ != other;
188   }
189 
190   bool operator!=(std::nullptr_t) const { return value_ != nullptr; }
191 
192  private:
193   template <typename Y>
194   friend class RefCountedPtr;
195 
196   T* value_ = nullptr;
197 };
198 
199 // A smart pointer class for objects that provide IncrementWeakRefCount() and
200 // WeakUnref() methods, such as those provided by the DualRefCounted base class.
201 template <typename T>
202 class WeakRefCountedPtr {
203  public:
WeakRefCountedPtr()204   WeakRefCountedPtr() {}
205   // NOLINTNEXTLINE(google-explicit-constructor)
WeakRefCountedPtr(std::nullptr_t)206   WeakRefCountedPtr(std::nullptr_t) {}
207 
208   // If value is non-null, we take ownership of a ref to it.
209   template <typename Y,
210             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
WeakRefCountedPtr(Y * value)211   explicit WeakRefCountedPtr(Y* value) {
212     value_ = value;
213   }
214 
215   // Move ctors.
WeakRefCountedPtr(WeakRefCountedPtr && other)216   WeakRefCountedPtr(WeakRefCountedPtr&& other) noexcept {
217     value_ = other.value_;
218     other.value_ = nullptr;
219   }
220   template <typename Y,
221             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
222   // NOLINTNEXTLINE(google-explicit-constructor)
WeakRefCountedPtr(WeakRefCountedPtr<Y> && other)223   WeakRefCountedPtr(WeakRefCountedPtr<Y>&& other) noexcept {
224     value_ = static_cast<T*>(other.value_);
225     other.value_ = nullptr;
226   }
227 
228   // Move assignment.
229   WeakRefCountedPtr& operator=(WeakRefCountedPtr&& other) noexcept {
230     reset(std::exchange(other.value_, nullptr));
231     return *this;
232   }
233   template <typename Y,
234             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
235   WeakRefCountedPtr& operator=(WeakRefCountedPtr<Y>&& other) noexcept {
236     reset(std::exchange(other.value_, nullptr));
237     return *this;
238   }
239 
240   // Copy ctors.
WeakRefCountedPtr(const WeakRefCountedPtr & other)241   WeakRefCountedPtr(const WeakRefCountedPtr& other) {
242     if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
243     value_ = other.value_;
244   }
245   template <typename Y,
246             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
247   // NOLINTNEXTLINE(google-explicit-constructor)
WeakRefCountedPtr(const WeakRefCountedPtr<Y> & other)248   WeakRefCountedPtr(const WeakRefCountedPtr<Y>& other) {
249     static_assert(std::has_virtual_destructor<T>::value,
250                   "T does not have a virtual dtor");
251     if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
252     value_ = static_cast<T*>(other.value_);
253   }
254 
255   // Copy assignment.
256   // NOLINTNEXTLINE(bugprone-unhandled-self-assignment)
257   WeakRefCountedPtr& operator=(const WeakRefCountedPtr& other) {
258     // Note: Order of reffing and unreffing is important here in case value_
259     // and other.value_ are the same object.
260     if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
261     reset(other.value_);
262     return *this;
263   }
264   template <typename Y,
265             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
266   WeakRefCountedPtr& operator=(const WeakRefCountedPtr<Y>& other) {
267     static_assert(std::has_virtual_destructor<T>::value,
268                   "T does not have a virtual dtor");
269     // Note: Order of reffing and unreffing is important here in case value_
270     // and other.value_ are the same object.
271     if (other.value_ != nullptr) other.value_->IncrementWeakRefCount();
272     reset(other.value_);
273     return *this;
274   }
275 
~WeakRefCountedPtr()276   ~WeakRefCountedPtr() {
277     if (value_ != nullptr) value_->WeakUnref();
278   }
279 
280   // An explicit copy method that supports ref-count tracing.
WeakRef(const DebugLocation & location,const char * reason)281   WeakRefCountedPtr<T> WeakRef(const DebugLocation& location,
282                                const char* reason) {
283     if (value_ != nullptr) value_->IncrementWeakRefCount(location, reason);
284     return WeakRefCountedPtr<T>(value_);
285   }
286 
swap(WeakRefCountedPtr & other)287   void swap(WeakRefCountedPtr& other) { std::swap(value_, other.value_); }
288 
289   // If value is non-null, we take ownership of a ref to it.
290   void reset(T* value = nullptr) {
291     T* old_value = std::exchange(value_, value);
292     if (old_value != nullptr) old_value->WeakUnref();
293   }
294   void reset(const DebugLocation& location, const char* reason,
295              T* value = nullptr) {
296     T* old_value = std::exchange(value_, value);
297     if (old_value != nullptr) old_value->WeakUnref(location, reason);
298   }
299   template <typename Y,
300             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
301   void reset(Y* value = nullptr) {
302     static_assert(std::has_virtual_destructor<T>::value,
303                   "T does not have a virtual dtor");
304     reset(static_cast<T*>(value));
305   }
306   template <typename Y,
307             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
308   void reset(const DebugLocation& location, const char* reason,
309              Y* value = nullptr) {
310     static_assert(std::has_virtual_destructor<T>::value,
311                   "T does not have a virtual dtor");
312     reset(location, reason, static_cast<T*>(value));
313   }
314 
315   // TODO(roth): This method exists solely as a transition mechanism to allow
316   // us to pass a ref to idiomatic C code that does not use WeakRefCountedPtr<>.
317   // Once all of our code has been converted to idiomatic C++, this
318   // method should go away.
release()319   T* release() { return std::exchange(value_, nullptr); }
320 
get()321   T* get() const { return value_; }
322 
323   T& operator*() const { return *value_; }
324   T* operator->() const { return value_; }
325 
326   template <typename Y,
327             std::enable_if_t<std::is_base_of<T, Y>::value, bool> = true>
TakeAsSubclass()328   WeakRefCountedPtr<Y> TakeAsSubclass() {
329     return WeakRefCountedPtr<Y>(static_cast<Y*>(release()));
330   }
331 
332   template <typename Y,
333             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
334   bool operator==(const WeakRefCountedPtr<Y>& other) const {
335     return value_ == other.value_;
336   }
337 
338   template <typename Y,
339             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
340   bool operator==(const Y* other) const {
341     return value_ == other;
342   }
343 
344   bool operator==(std::nullptr_t) const { return value_ == nullptr; }
345 
346   template <typename Y,
347             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
348   bool operator!=(const WeakRefCountedPtr<Y>& other) const {
349     return value_ != other.value_;
350   }
351 
352   template <typename Y,
353             std::enable_if_t<std::is_convertible<Y*, T*>::value, bool> = true>
354   bool operator!=(const Y* other) const {
355     return value_ != other;
356   }
357 
358   bool operator!=(std::nullptr_t) const { return value_ != nullptr; }
359 
360  private:
361   template <typename Y>
362   friend class WeakRefCountedPtr;
363 
364   T* value_ = nullptr;
365 };
366 
367 template <typename T, typename... Args>
MakeRefCounted(Args &&...args)368 inline RefCountedPtr<T> MakeRefCounted(Args&&... args) {
369   return RefCountedPtr<T>(new T(std::forward<Args>(args)...));
370 }
371 
372 template <typename T>
373 bool operator<(const RefCountedPtr<T>& p1, const RefCountedPtr<T>& p2) {
374   return p1.get() < p2.get();
375 }
376 
377 template <typename T>
378 bool operator<(const WeakRefCountedPtr<T>& p1, const WeakRefCountedPtr<T>& p2) {
379   return p1.get() < p2.get();
380 }
381 
382 //
383 // absl::Hash integration
384 //
385 
386 template <typename H, typename T>
AbslHashValue(H h,const RefCountedPtr<T> & p)387 H AbslHashValue(H h, const RefCountedPtr<T>& p) {
388   return H::combine(std::move(h), p.get());
389 }
390 
391 template <typename H, typename T>
AbslHashValue(H h,const WeakRefCountedPtr<T> & p)392 H AbslHashValue(H h, const WeakRefCountedPtr<T>& p) {
393   return H::combine(std::move(h), p.get());
394 }
395 
396 // Heterogenous lookup support.
397 template <typename T>
398 struct RefCountedPtrHash {
399   using is_transparent = void;
operatorRefCountedPtrHash400   size_t operator()(const RefCountedPtr<T>& p) const {
401     return absl::Hash<RefCountedPtr<T>>{}(p);
402   }
operatorRefCountedPtrHash403   size_t operator()(const WeakRefCountedPtr<T>& p) const {
404     return absl::Hash<WeakRefCountedPtr<T>>{}(p);
405   }
operatorRefCountedPtrHash406   size_t operator()(T* p) const { return absl::Hash<T*>{}(p); }
407 };
408 template <typename T>
409 struct RefCountedPtrEq {
410   using is_transparent = void;
operatorRefCountedPtrEq411   bool operator()(const RefCountedPtr<T>& p1,
412                   const RefCountedPtr<T>& p2) const {
413     return p1 == p2;
414   }
operatorRefCountedPtrEq415   bool operator()(const WeakRefCountedPtr<T>& p1,
416                   const WeakRefCountedPtr<T>& p2) const {
417     return p1 == p2;
418   }
operatorRefCountedPtrEq419   bool operator()(const RefCountedPtr<T>& p1,
420                   const WeakRefCountedPtr<T>& p2) const {
421     return p1 == p2.get();
422   }
operatorRefCountedPtrEq423   bool operator()(const WeakRefCountedPtr<T>& p1,
424                   const RefCountedPtr<T>& p2) const {
425     return p1 == p2.get();
426   }
operatorRefCountedPtrEq427   bool operator()(const RefCountedPtr<T>& p1, const T* p2) const {
428     return p1 == p2;
429   }
operatorRefCountedPtrEq430   bool operator()(const WeakRefCountedPtr<T>& p1, const T* p2) const {
431     return p1 == p2;
432   }
operatorRefCountedPtrEq433   bool operator()(const T* p1, const RefCountedPtr<T>& p2) const {
434     return p2 == p1;
435   }
operatorRefCountedPtrEq436   bool operator()(const T* p1, const WeakRefCountedPtr<T>& p2) const {
437     return p2 == p1;
438   }
439 };
440 
441 }  // namespace grpc_core
442 
443 #endif  // GRPC_SRC_CORE_UTIL_REF_COUNTED_PTR_H
444